Размер стека, используемый при разработке ядра - PullRequest
9 голосов
/ 13 октября 2008

Я занимаюсь разработкой операционной системы и вместо того, чтобы программировать ядро, я занимаюсь разработкой ядра. Эта операционная система ориентирована на архитектуру x86, а моя цель - на современные компьютеры. Предполагаемое количество требуемой оперативной памяти составляет 256 МБ или более.

Каков хороший размер, чтобы стек работал для каждого потока в системе? Должен ли я попытаться спроектировать систему таким образом, чтобы стек мог автоматически расширяться при достижении максимальной длины?

Я думаю, что, если я правильно помню, страница в ОЗУ имеет размер 4 КБ или 4096 байт, и это мне не кажется большим. Я определенно могу видеть времена, особенно при использовании большого количества рекурсий, когда я хотел бы иметь более 1000 интегралов в оперативной памяти одновременно. Теперь реальным решением было бы заставить программу делать это с помощью malloc и управлять собственными ресурсами памяти, но на самом деле я хотел бы узнать мнение пользователя по этому поводу.

Достаточно ли 4К для стека с современными компьютерными программами? Должен ли стек быть больше этого? Должен ли стек автоматически расширяться для размещения любых типов размеров? Мне это интересно как с точки зрения практического разработчика, так и с точки зрения безопасности.

4k слишком велико для стека? Рассматривая нормальное выполнение программы, особенно с точки зрения классов в C ++, я замечаю, что хороший исходный код стремится malloc/new к данным, которые ему нужны при создании классов, чтобы минимизировать выброс данных при вызове функции.

Что я даже не понял, это размер кэш-памяти процессора. В идеале, я думаю, что стек должен находиться в кеше, чтобы ускорить процесс, и я не уверен, что мне нужно этого достичь или процессор сможет справиться с этим для меня. Я просто планировал использовать обычную скучную оперативную память для тестирования. Я не могу решить. Какие есть варианты?

Ответы [ 5 ]

10 голосов
/ 13 октября 2008

Размер стека зависит от того, что делают ваши темы. Мой совет:

  • сделать размер стека параметром во время создания потока (разные потоки будут делать разные вещи, и, следовательно, потребуются разные размеры стека)
  • обеспечивает разумное значение по умолчанию для тех, кто не хочет беспокоиться об указании размера стека (4K обращается ко мне с уродом управления, поскольку это приведет к расточительству стека, чтобы получить сигнал довольно быстро)
  • подумайте, как вы будете обнаруживать и справляться с переполнением стека Обнаружение может быть сложным. Вы можете поместить защитные страницы - пустые - в конце стека, и это, как правило, будет работать. Но вы полагаетесь на поведение Плохой Нити, чтобы не перепрыгнуть через этот ров и начать загрязнять то, что лежит за его пределами. Как правило, этого не произойдет ... но это то, что делает действительно жесткие ошибки жесткими. Герметичный механизм предполагает взлом вашего компилятора для генерации кода проверки стека. Что касается работы с переполнением стека, вам понадобится выделенный стек где-то еще, в котором будет выполняться запущенный поток (или его ангел-хранитель, кто бы вы ни решили, что вы - в конце концов, разработчик ОС).
  • Я бы настоятельно рекомендовал пометить концы вашего стека отличительным паттерном, чтобы, когда ваши нити пересекали концы (и они всегда так делали), вы могли, по крайней мере, пойти посмертно и увидеть, что что-то действительно произошло убежать из своего стека. Страница 0xDEADBEEF или что-то в этом роде очень удобно.

Кстати, размеры страниц x86, как правило, 4 КБ, но это не обязательно. Вы можете пойти с размером 64 КБ или даже больше. Обычная причина для больших страниц - избегать промахов TLB. Опять же, я бы сделал это конфигурацией ядра или параметром времени выполнения.

2 голосов
/ 14 мая 2011

Найдите KERNEL_STACK_SIZE в исходном коде ядра Linux, и вы обнаружите, что он сильно зависит от архитектуры - PAGE_SIZE или 2 * PAGE_SIZE и т. Д. (Ниже приведены только некоторые результаты - многие промежуточные выходные данные удалены).

./arch/cris/include/asm/processor.h:
#define KERNEL_STACK_SIZE PAGE_SIZE

./arch/ia64/include/asm/ptrace.h:
# define KERNEL_STACK_SIZE_ORDER        3
# define KERNEL_STACK_SIZE_ORDER        2
# define KERNEL_STACK_SIZE_ORDER        1
# define KERNEL_STACK_SIZE_ORDER        0
#define IA64_STK_OFFSET         ((1 << KERNEL_STACK_SIZE_ORDER)*PAGE_SIZE)
#define KERNEL_STACK_SIZE       IA64_STK_OFFSET

./arch/ia64/include/asm/mca.h:
    u64 mca_stack[KERNEL_STACK_SIZE/8];
    u64 init_stack[KERNEL_STACK_SIZE/8];

./arch/ia64/include/asm/thread_info.h:
#define THREAD_SIZE         KERNEL_STACK_SIZE

./arch/ia64/include/asm/mca_asm.h:
#define MCA_PT_REGS_OFFSET      ALIGN16(KERNEL_STACK_SIZE-IA64_PT_REGS_SIZE)

./arch/parisc/include/asm/processor.h:
#define KERNEL_STACK_SIZE   (4*PAGE_SIZE)

./arch/xtensa/include/asm/ptrace.h:
#define KERNEL_STACK_SIZE (2 * PAGE_SIZE)

./arch/microblaze/include/asm/processor.h:
# define KERNEL_STACK_SIZE  0x2000
1 голос
/ 13 октября 2008

Если вы используете виртуальную память, вы хотите, чтобы стек увеличивался. Принудительное статическое выделение размера стека, как это обычно бывает при многопоточности на уровне пользователя, например, Qthreads и Windows Fibers - беспорядок. Трудно использовать, легко разбить. Все современные операционные системы динамически наращивают стек, я думаю, обычно имея защищенную от записи защитную страницу или две под текущим указателем стека. Записывает туда, а затем сообщает ОС, что стек вышел за пределы выделенного пространства, и вы выделяете новую защитную страницу под ним и делаете страницу, получившую удар, доступной для записи. Пока ни одна функция не выделяет больше страницы данных, это работает нормально. Или вы можете использовать две или четыре защитных страницы, чтобы увеличить размер стека.

Если вам нужен способ управления размером стека, и ваша цель - это действительно контролируемая и эффективная среда, но вы не заботитесь о программировании в том же стиле, что и Linux и т. Д., Выберите модель выполнения единовременного выполнения, где задача запускается каждый раз, когда обнаруживается соответствующее событие, выполняется до завершения, а затем сохраняет все постоянные данные в своей структуре данных задачи. Таким образом, все потоки могут совместно использовать один стек. Используется во многих тонких операционных системах реального времени для управления автомобилем и тому подобное.

1 голос
/ 13 октября 2008

Я брошу свои два цента, чтобы мяч заработал:

  • Я не уверен, какой будет «типичный» размер стека. Я бы предположил, может быть, 8 КБ на поток, и если поток превышает эту сумму, просто выбросить исключение. Однако, согласно this , Windows имеет зарезервированный размер стека по умолчанию 1 МБ на поток, но он не фиксируется сразу (страницы фиксируются так, как они нужны). Кроме того, вы можете запросить другой размер стека для данного EXE во время компиляции с помощью директивы компилятора. Не уверен, что Linux делает, но я видел ссылки на стеки 4 КБ (хотя я думаю, что это можно изменить, когда вы компилируете ядро, и я не уверен, какой размер стека по умолчанию ...)

  • Это связано с первым пунктом. Вы, вероятно, хотите установить фиксированный предел того, сколько стека может получить каждый поток. Таким образом, вы, вероятно, не хотите автоматически выделять больше стекового пространства каждый раз, когда поток превышает его текущее стековое пространство, потому что глючная программа, которая застревает в бесконечной рекурсии, собирается съесть всю доступную память.

0 голосов
/ 13 октября 2008

Почему бы не сделать размер стека настраиваемым элементом, сохраненным вместе с программой или указанным, когда процесс создает другой процесс?

Существует множество способов сделать это настраиваемым.

Существует руководство, в котором указано «0, 1 или n», что означает, что вы должны разрешить ноль, одно или любое число (ограниченное другими ограничениями, такими как память) объекта - это также относится и к размерам объектов.

...