内核开发中使用的堆栈大小

时间:2008-10-13 04:41:28

标签: operating-system stack size kernel osdev

我正在开发一个操作系统,而不是编写内核,我正在设计内核。该操作系统面向x86架构,我的目标是现代计算机。估计所需的RAM数量为256Mb或更多。

使每个线程的堆栈在系统上运行的好大小是多少?我是否应该尝试设计系统,以便在达到最大长度时自动扩展堆栈?

我想如果我没记错,RAM中的页面是4k或4096字节,这对我来说似乎不是很多。我绝对可以看到时间,特别是在使用大量递归时,我希望一次在RAM中有超过1000个积分。现在,真正的解决方案是让程序通过使用malloc来执行此操作并管理自己的内存资源,但我真的想知道用户对此的意见。

对于具有现代计算机程序的堆栈,4k是否足够大?堆栈应该大于那个吗?堆栈是否应自动扩展以适应任何类型的大小?从实际开发人员的角度和安全角度来看,我对此感兴趣。

4k对于堆栈来说太大了吗?考虑到正常的程序执行,特别是从C ++中的类的角度来看,我注意到良好的源代码倾向于malloc/new创建类时所需的数据,以最小化函数调用中抛出的数据。

我还没有得到的是处理器缓存的大小。理想情况下,我认为堆栈将驻留在缓存中以加快速度,我不确定是否需要实现此目的,或者处理器是否可以为我处理它。我只是计划使用常规无聊的旧RAM进行测试。我无法决定。有什么选择?

5 个答案:

答案 0 :(得分:10)

堆栈大小取决于您的线程正在做什么。我的建议:

  • 在创建线程时使堆栈大小成为参数(不同的线程将执行不同的操作,因此需要不同的堆栈大小)
  • 为那些不想被指定堆栈大小的人提供合理的默认值(4K对我的控制狂有吸引力,因为它会导致堆栈挥手,呃,很快得到信号)
  • 考虑如何检测和处理堆栈溢出。检测可能很棘手。您可以将防护页面 - 空 - 放在堆栈的末尾,这通常会起作用。但是你依靠坏线程的行为不要跳过那条护城河并开始污染什么。一般来说,这不会发生......但是,这就是让真正艰难的虫子变得艰难的原因。密封机制涉及黑客攻击编译器以生成堆栈检查代码。至于处理堆栈溢出,你需要一个专门的堆栈,其他地方的违规线程(或其守护天使,无论你决定谁 - 毕竟你是操作系统设计师)将运行。
  • 我强烈建议用一个独特的模式标记堆栈的末尾,这样当你的线程越过末端(并且它们总是这样做)时,你至少可以进行验尸并看到事实确实跑掉它的堆栈。一页0xDEADBEEF或类似的东西很方便。

顺便说一句,x86页面大小通常是4k,但它们不一定是。你可以使用64k或更大的尺寸。较大页面的通常原因是避免TLB未命中。我再次将其作为内核配置或运行时参数。

答案 1 :(得分:2)

在linux内核源代码中搜索KERNEL_STACK_SIZE,您会发现它非常依赖于体系结构 - 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

答案 2 :(得分:1)

我会把我的两分钱扔进去让球滚动:

  • 我不确定“典型”的堆栈大小是多少。我猜每个线程可能有8 KB,如果一个线程超过这个数量,就抛出异常。但是,根据this,Windows每个线程的默认保留堆栈大小为1MB,但不会一次全部提交(页面在需要时提交)。此外,您可以在编译时使用编译器指令为给定的EXE请求不同的堆栈大小。不知道Linux做了什么,但我看到了对4 KB堆栈的引用(虽然我认为在编译内核时可以改变它,但我不确定默认的堆栈大小是什么......)

    < / LI>
  • 这与第一点有关。您可能希望每个线程可以获得多少堆栈的固定限制。因此,您可能不希望每次线程超过其当前堆栈空间时自动分配更多堆栈空间,因为陷入无限递归的错误程序会占用所有可用内存。

答案 3 :(得分:1)

如果您使用的是虚拟内存,则需要使堆栈增长。强制堆栈大小的静态分配,就像在Qthreads和Windows Fibers等用户级线程中常见的一样。难以使用,容易崩溃。所有现代操作系统都会动态地增加堆栈,我认为通常通过在当前堆栈指针下面设置一个写保护页或两个保护页。然后写入操作系统告诉操作系统堆栈已经低于其分配的空间,并在下面分配一个新的防护页面,并使得命中的页面可写。只要没有单个函数分配超过一页数据,这就可以正常工作。或者您可以使用两个或四个保护页面来允许更大的堆栈帧。

如果你想要一种控制堆栈大小的方法,你的目标是一个真正受控且高效的环境,但不关心与Linux等相同风格的编程,那么选择一个单一的执行模型,其中任务是每次检测到相关事件时,都会启动,运行完成,然后将任何持久数据存储在其任务数据结构中。这样,所有线程都可以共享一个堆栈。用于汽车控制等类似的许多超薄实时操作系统。

答案 4 :(得分:0)

为什么不将堆栈大小设置为可配置项,可以与程序一起存储,也可以在进程创建另一个进程时指定?

您可以通过多种方式对此进行配置。

有一个指示“0,1或n”的指南,意味着您应该允许对象的零,一个或任何数字(受到内存等其他约束的限制) - 这也适用于对象的大小。