C Linux内核中的堆栈内存

C Linux内核中的堆栈内存,c,linux,memory-management,linux-kernel,C,Linux,Memory Management,Linux Kernel,我最近试着想象一下Linux内核中可能是如何处理堆栈内存的,但却无法得到任何可靠的结果。我知道内核使用自己的函数进行动态内存管理,但我不知道它如何管理普通的C堆栈内存,因为毕竟,这可以用普通的C实现,并用普通的GCC编译。据我所知,在处理操作系统时,堆栈内存分配通常由libc的一种形式来处理,甚至在类似AVR的东西上。就我所知,Linux内核并不依赖于libc 我不完全确定堆栈内存管理首先是如何委托给libc的,因为它似乎是一个内置的语言特性。我所能想象的是,它以某种方式进行了一般性编译(或实现

我最近试着想象一下Linux内核中可能是如何处理堆栈内存的,但却无法得到任何可靠的结果。我知道内核使用自己的函数进行动态内存管理,但我不知道它如何管理普通的C堆栈内存,因为毕竟,这可以用普通的C实现,并用普通的GCC编译。据我所知,在处理操作系统时,堆栈内存分配通常由libc的一种形式来处理,甚至在类似AVR的东西上。就我所知,Linux内核并不依赖于libc

我不完全确定堆栈内存管理首先是如何委托给libc的,因为它似乎是一个内置的语言特性。我所能想象的是,它以某种方式进行了一般性编译(或实现),这样就可以在编译之后或可能作为编译过程的一部分分配提供程序。有人能帮我解释一下吗?

在链接步骤中,堆栈内存被分配(整个堆栈)

堆栈内存(通常)放在.stack段中(它只不过是一个偏移量和长度,没有内容)

.startup代码(通常是某些库中的汇编函数,我不确定是哪个库)在加载的代码中找到.stack部分的名称,添加长度值以获得堆栈的顶部地址(堆栈向下增长),并且(在其他几个操作中)设置
sp
bp
寄存器

当操作系统运行时(即不在“用户”模式下),它(通常)有自己的堆栈空间,并使用自己的值作为
sp
bp
寄存器

“用户”模式和“特权”模式之间的切换以及寄存器值的交换在您编写的内容和内核中的代码之间的转换中处理

大多数此类切换是在
libc
库中的包装函数中处理的,结果是通过跳转到内核例程的表进行控制转换


注意:其他CPU和OS在如何完成任务方面有不同的细节,但结果非常相似。

堆栈可以是任何读/写内存块。堆栈没有什么特别之处。操作系统创建堆栈—即,它分配内存并将其位置分配给堆栈指针寄存器,因为启动进程需要内存,但它不管理堆栈

在unix平台上,运行程序需要两个步骤。首先,您要克隆父进程。因此,新进程与父进程具有相同的堆栈设置

最后,执行程序。程序加载从可执行文件获取所需的堆栈大小,而可执行文件又从链接器获取信息(例如,请参阅ld命令的“stack”选项)

启动后,应用程序可以调用系统服务来分配内存页,并使堆栈指针寄存器引用该内存页。然后这就成为程序堆栈

请记住,这都是指用户堆栈。操作系统为每个进程维护一个内核堆栈,该进程用于自己的目的

据我所知,堆栈内存分配通常由 一种形式的libc

此程序说明,libc中的任何内容都不用于堆栈内存分配:

// compile with: gcc -nostdlib nolibc.c -o nolibc
_start()
{
    int a[9999];
    *a = 0;
    asm("   mov %0,%%ebx\n\
            mov $1,%%eax\n\
            int $128" : : "r" (*a));    // _exit(*a);
}

如果超出分配的堆栈空间,则会发生故障,内核的故障处理程序可以分配更多空间,除非达到限制或使用固定大小。

在现代linux中,我找不到.stack段<代码>%objdump-h/bin/cat也许您可以阅读这个stackoverflow问题来找到您想要的答案:保存/恢复寄存器以及交换到内核堆栈不由libc处理。内核不能信任libc,因为libc只是由非特权进程运行的用户空间代码!此外,当您“直接”进行系统调用时(例如,使用64位模式下的
syscall
指令),即使对于不链接任何库的玩具可执行文件,也必须发生同样的情况,并且工作相同。堆栈内存在链接步骤中没有“分配”,它只是存储一个标记为默认堆栈大小的数字。堆栈内存是在操作系统加载程序时使用“stack size”参数分配的。@ii,用于分配堆栈的参数是在链接时设置的。实际内存分配在加载时处理。很抱歉,如果我说错了,当一个新进程启动时,它有一个映射到进程地址空间的用户空间堆栈,以及一个内核堆栈,供它进行系统调用时使用。堆栈指针从指向堆栈开始。libc根本不参与其中。发布的答案有点令人困惑,但ELF二进制文件能够控制它们得到的堆栈设置似乎是正确的:。默认情况下没有这样一个节,然后在内核默认选择的virt地址处获得一个默认堆栈。