#operating-system #stack #size #kernel #osdev
Вопрос:
Я разрабатываю операционную систему, и вместо того, чтобы программировать ядро, я разрабатываю ядро. Эта операционная система ориентирована на архитектуру x86, и моя цель-современные компьютеры. Предполагаемое количество необходимой оперативной памяти составляет 256 МБ или более.
Каков хороший размер стека для каждого потока, запущенного в системе? Должен ли я попытаться спроектировать систему таким образом, чтобы стек мог автоматически расширяться при достижении максимальной длины?
Я думаю, если я правильно помню, что страница в оперативной памяти составляет 4k или 4096 байт, и это просто не кажется мне большим. Я определенно вижу времена, особенно при использовании большого количества рекурсии, когда я хотел бы иметь в оперативной памяти более 1000 целых чисел одновременно. Теперь реальным решением было бы заставить программу делать это с помощью malloc
собственных ресурсов памяти и управлять ими, но на самом деле я хотел бы знать мнение пользователей по этому поводу.
Достаточно ли 4k для стека с современными компьютерными программами? Должен ли стек быть больше этого? Должен ли стек автоматически расширяться для размещения любых типов размеров? Меня это интересует как с практической точки зрения разработчика, так и с точки зрения безопасности.
4k слишком большой для стека? Рассматривая нормальное выполнение программы, особенно с точки зрения классов в C , я замечаю, что хороший исходный код стремится к malloc/new
данным, которые ему нужны при создании классов, чтобы свести к минимуму количество данных, передаваемых при вызове функции.
То, в чем я даже не вдавался, — это размер кэш-памяти процессора. В идеале, я думаю, что стек должен находиться в кэше, чтобы ускорить процесс, и я не уверен, нужно ли мне этого добиваться или процессор может справиться с этим за меня. Я просто планировал использовать обычную скучную старую оперативную память для целей тестирования. Я не могу решить. Какие есть варианты?
Ответ №1:
Размер стека зависит от того, что делают ваши потоки. Мой совет:
- сделайте размер стека параметром во время создания потока (разные потоки будут выполнять разные действия и, следовательно, будут нуждаться в разных размерах стека).
- обеспечьте разумное значение по умолчанию для тех, кто не хочет беспокоиться об указании размера стека (4K привлекает во мне помешанного на контроле, так как это приведет к тому, что расточитель стека, э-э, получит сигнал довольно быстро)
- подумайте, как вы будете обнаруживать переполнение стека и справляться с ним. Обнаружение может быть непростым делом. Вы можете поместить защитные страницы-пустые-в концы вашего стека, и это, как правило, сработает. Но вы полагаетесь на поведение Плохой нити, чтобы не перепрыгнуть через этот ров и не начать загрязнять то, что лежит за его пределами. Как правило, этого не произойдет…но с другой стороны, это то, что делает действительно трудных жуков трудными. Герметичный механизм включает в себя взлом вашего компилятора для генерации кода проверки стека. Что касается решения проблемы переполнения стека, вам понадобится выделенный стек где-то еще, на котором будет работать поток-нарушитель (или его ангел-хранитель, кем бы вы его ни выбрали-в конце концов, вы разработчик ОС).
- Я бы настоятельно рекомендовал пометить концы вашего стека характерным рисунком, чтобы, когда ваши потоки пересекут концы (а они всегда пересекаются), вы могли, по крайней мере, провести вскрытие и увидеть, что что-то действительно вышло из своего стека. Страница с 0xDEADBEEF или что-то в этом роде удобна.
Кстати, размеры страниц x86, как правило, 4k, но они не обязательно должны быть такими. Вы можете выбрать размер 64 кб или даже больше. Обычная причина для больших страниц заключается в том, чтобы избежать пропусков TLB. Опять же, я бы сделал это конфигурацией ядра или параметром времени выполнения.
Комментарии:
1. Я никогда не думал о том, чтобы сделать его чем-то, что можно настроить во время компиляции. Мне также, кажется, нравится 4k, помешанный на контроле говорит мне, что если вам действительно нужно использовать больше, чем столько памяти, вам следует делать это с помощью malloc. ^_^ Тем не менее, есть сумасшедшие программисты ИИ, которые любят свою рекурсию.
2. если вы снова и снова выделяете много объектов одного и того же размера, вам следует рассмотреть возможность распределения с фиксированным блоком. Это может быть намного эффективнее, чем универсальный malloc.
3. размеры страниц x86 ограничены и зависят от множества факторов (включая режимы, в которых находится процессор и т. Д.). Но размеры страниц, которые могут существовать, составляют 4 КБ, 2 МБ, 4 МБ и 1 Гб. В любой существующей конфигурации x86/x86-64 нет размера страницы 64 КБ.
Ответ №2:
Найдите KERNEL_STACK_SIZE в исходном коде ядра Linux, и вы обнаружите, что он очень сильно зависит от архитектуры — РАЗМЕР страницы или 2*РАЗМЕР СТРАНИЦЫ и т. Д. (Ниже приведены лишь некоторые результаты — многие промежуточные выходные данные удалены).
./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
Ответ №3:
Я брошу свои два цента, чтобы запустить мяч:
- Я не уверен, каким будет «типичный» размер стека. Я бы предположил, что, возможно, 8 КБ на поток, и если поток превышает эту сумму, просто создайте исключение. Однако, согласно этому, Windows имеет зарезервированный размер стека по умолчанию 1 МБ на поток, но он не фиксируется сразу (страницы фиксируются по мере необходимости). Кроме того, вы можете запросить другой размер стека для данного EXE-файла во время компиляции с помощью директивы компилятора. Не уверен, что делает Linux, но я видел ссылки на стеки размером 4 КБ (хотя я думаю, что это можно изменить при компиляции ядра, и я не уверен, каков размер стека по умолчанию…)
- Это связано с первым пунктом. Вероятно, вам нужен фиксированный предел того, сколько стека может получить каждый поток. Таким образом, вы, вероятно, не захотите автоматически выделять больше места в стеке каждый раз, когда поток превышает свое текущее место в стеке, потому что программа с ошибками, застрявшая в бесконечной рекурсии, съест всю доступную память.
Ответ №4:
Если вы используете виртуальную память, вы действительно хотите сделать стек увеличиваемым. Принудительное статическое распределение размера стека, как это часто бывает в потоках пользовательского уровня, таких как Qthreads и Windows, — это беспорядок. Трудно использовать, легко разбиться. Все современные операционные системы динамически увеличивают стек, я думаю, что обычно у них есть защищенная от записи защитная страница или две ниже текущего указателя стека. Записывает там, затем сообщает ОС, что стек опустился ниже выделенного места, и вы выделяете новую защитную страницу ниже этого и делаете страницу, на которую попали, доступной для записи. До тех пор, пока ни одна функция не выделяет более одной страницы данных, это работает нормально. Или вы можете использовать две или четыре защитные страницы, чтобы разрешить большие рамки стека.
Если вам нужен способ контролировать размер стека, и ваша цель-действительно управляемая и эффективная среда, но вас не волнует программирование в том же стиле, что и Linux и т. Д., Перейдите к модели однократного выполнения, в которой задача запускается каждый раз при обнаружении соответствующего события, выполняется до завершения, а затем сохраняет любые постоянные данные в структуре данных задачи. Таким образом, все потоки могут совместно использовать один стек. Используется во многих тонких операционных системах реального времени для управления автомобилем и тому подобного.
Комментарии:
1. Я вижу здесь два недостатка. Основной из них заключается в том, чтобы позволить системе самостоятельно переполнять стек. Если я сделаю это, все, что потребуется, — это один рекурсивный бесконечный цикл и blamo, стек для 1 процесса внезапно поглотит весь ПК… Что касается сделки с одним выстрелом, то это многозадачная ОС, так что никаких бобов там нет.
Ответ №5:
Почему бы не сделать размер стека настраиваемым элементом, либо сохраняемым в программе, либо указываемым, когда процесс создает другой процесс?
Существует множество способов сделать это настраиваемым.
Существует руководство, в котором указано «0, 1 или n», что означает, что вы должны разрешить ноль, одно или любое число (ограниченное другими ограничениями, такими как память) объекта — это также относится к размерам объектов.