9

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

使每个线程的堆栈在系统上运行的合适大小是多少?我是否应该尝试以这样一种方式设计系统,即如果达到最大长度,堆栈可以自动扩展?

我想如果我没记错的话,RAM 中的一个页面是 4k 或 4096 字节,这对我来说似乎并不多。我绝对可以看到时间,尤其是在使用大量递归时,我希望一次在 RAM 中拥有超过 1000 个整数。现在,真正的解决方案是让程序通过使用malloc和管理自己的内存资源来做到这一点,但我真的很想知道用户对此的看法。

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

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

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

4

5 回答 5

11

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

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

顺便说一句,x86 页面大小通常为 4k,但并非必须如此。您可以选择 64k 甚至更大的尺寸。较大页面的通常原因是避免 TLB 未命中。同样,我会将其设为内核配置或运行时参数。

于 2008-10-13T05:10:57.840 回答
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
于 2011-05-14T13:03:22.200 回答
1

我会投入两分钱让球滚动:

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

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

于 2008-10-13T05:12:58.147 回答
1

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

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

于 2008-10-13T06:18:23.880 回答
0

为什么不让堆栈大小成为一个可配置的项目,或者与程序一起存储,或者在一个进程创建另一个进程时指定?

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

有一条准则指出“0、1 或 n”,这意味着您应该允许对象的零、一个或任意数量(受其他约束,例如内存限制) - 这也适用于对象的大小。

于 2008-10-13T05:10:09.823 回答