Переполнение стека потоков - PullRequest
2 голосов
/ 20 августа 2009

В RTO, таких как vxworks, всякий раз, когда мы создаем задачу, указывается размер стека. Можем ли мы написать подпрограмму в C, которая проверяет, переполняется ли стек для задачи?

Ответы [ 7 ]

4 голосов
/ 20 августа 2009

Посмотрите на ваш компилятор, они часто позволяют вам добавлять функции прелюдии, чтобы сделать это, или они могут даже сами проверить это, если вы не манипулируете регистром указателя стека.

И проверьте, позволяет ли операционная система устанавливать «сторожевые страницы». Пометьте последнюю страницу в стеке ваших потоков как не для чтения / записи, перехватите сигнал SIGSEGV и используйте специальный способ ОС / ЦП, чтобы выяснить, не произошла ли сбой на странице защиты. Чтобы это работало, вы должны быть уверены, что стековый фрейм функции (переданные в стек параметры, локальные переменные и выделенное пространство) всегда меньше размера страницы, в противном случае вы можете пропустить «guard-страницу» Это лучший способ справиться с этим, так как он не требует дополнительных затрат времени выполнения при обычной обработке.

Вы видите, что это сильно зависит от OS / CPU / Compiler. Но я почти уверен, что Google найдет полезный код и помощники для этого метода для всех систем, так как это довольно распространенный метод для программистов низкого уровня (например, разработчиков среды выполнения или интерпретаторов).

3 голосов
/ 30 октября 2009

Кстати, вы можете сделать что-то подобное из оболочки в VxWorks, используя checkStack ().

3 голосов
/ 20 августа 2009

Если вы знаете, насколько велик стек, и если вы осторожны, тогда да (но не переносимо). Если нет другого способа получить базовый адрес стека, вам нужно записать адрес переменной стека в основной функции потока; это дает вам приближение к вершине стека. Затем в своей функции проверки вы берете адрес локальной переменной; это дает вам дно стека. Если разница между верхом и низом равна размеру вашего стека, пришло время беспокоиться; если разница больше размера стека, уже слишком поздно беспокоиться - ущерб уже нанесен (но теперь вам нужно подумать о том, как очистить).

0 голосов
/ 21 августа 2009

Если ваше конкретное приложение статически распределяет свои потоки, то можно разместить их стеки в статически определенных областях и использовать карту компоновщика для размещения символа в конце этих областей. Затем вам просто нужно получить текущий указатель стека (как описано в других ответах) и сравнить указатель «конец сегмента стека» с этим адресом. Это также может работать для динамического выделения, если у каждого потока есть место для хранения адреса, предоставленного ему как конец его стека.

0 голосов
/ 21 августа 2009

Я не знаю о VxWorks, но я помню, что ядра Green Hill Velosity / uVelosity предоставляют код для этого.Даже если бы они этого не сделали, так как они предоставляют источник, который пользователи могут изменять, и инфраструктура там есть, это было бы действительно легко добавить.их, портируя uVelosity на новую архитектуру.Вот как я стал близок с обработкой стеков потоков.

0 голосов
/ 20 августа 2009

Есть несколько методов, которые вы можете использовать - как правило, у вас есть задача с низким приоритетом, которая отслеживает состояние стека всех других задач каждую секунду или около того.

a: убедитесь, что пространство стека заполнено известным шаблоном перед началом вашей задачи. Затем вы можете узнать, сколько осталось «не поврежденного» стека, проверив шаблон.

  • Преимущество: позволяет проверить «верхний предел» использования стека.
  • Недостаток: если вы выделяете стековую память, но по какой-то причине не записываете в нее, этот метод МОЖЕТ не обнаруживать переполнение.

b: Вы можете просто прослушать указатель стека всех других потоков.

  • Недостаток: это просто "выборка" из указателя стека, поэтому краткий провал в переполнении может не быть замечен
  • Преимущество: быстро и просто.

Я бы порекомендовал комбинацию обоих. Поскольку вы делаете вещи низкого уровня, используя такие вещи, как функции VxWorks TaskInfoGet (), трудно сделать это даже удаленно переносимым.

0 голосов
/ 20 августа 2009

Размер стека по умолчанию равен 1 МБ, в зависимости от компилятора. Основываясь на этой информации, вы можете попытаться поймать оставшийся стек следующим образом:


unsigned long remaining_stack_size() {
    char dummy;
    return 0x000fffff & (unsigned long)&dummy;
    // 0x000fffff is 1MB -1 (1048576 -1)
}

Edit: Обратите внимание, что на самом деле он возвращает текущую позицию стека, что то же самое.

Редактировать (2): Для тех, кто сказал, что я неправ, вот подтверждение концепции:


#include <stdio.h>
#include <windows.h>

unsigned long remaining_stack_size() {
    char dummy;
    return 0x001fffff & (unsigned long)&dummy + 1; // okay, some minor adjusts
}

void recurse_to_death(unsigned long used, char *p) {
    char buf[32*1024];
    used += 32*1024;
    printf("Used: 0x%08x Remaining: 0x%08x\n", used, remaining_stack_size());
    recurse_to_death(used, buf);
}

DWORD WINAPI my_thread(void *p) {
    printf("Total stack size of this Thread: 0x%08x bytes\n", remaining_stack_size() + 72);
    recurse_to_death(0, NULL);
    return 0;
}

int main(int argc, char *argv) {
    DWORD tid;
    // CreateThread's stack size actually defaults to 1MB+64KB and does not honor lower values
    CreateThread(NULL, NULL, my_thread, NULL, NULL, NULL);
    Sleep(30000);
    return 0;
}

remaining_stack_size() предсказывает переполнение стека с совершенством.

...