我写了一个简单的程序,如下所示。
#include<stdio.h>
int foo(int i)
{
int k=9;
if(i==10)
return 1;
else
foo(++i);
open("1",1);
}
int main()
{
foo(1);
}
我这样做的目的是检查如何在堆栈中的函数中为变量(在本例中为int k)分配内存。我使用开放系统调用作为标记。 strace的输出如下:
execve("./a.out", ["./a.out"], [/* 25 vars */]) = 0
brk(0) = 0x8653000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap2(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb777e000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat64(3, {st_mode=S_IFREG|0644, st_size=95172, ...}) = 0
mmap2(NULL, 95172, PROT_READ, MAP_PRIVATE, 3, 0) = 0xb7766000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., 512) = 512
fstat64(3, {st_mode=S_IFREG|0755, st_size=1734120, ...}) = 0
mmap2(NULL, 1743580, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0xb75bc000
mmap2(0xb7760000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1a4) = 0xb7760000
mmap2(0xb7763000, 10972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xb7763000
close(3) = 0
mmap2(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb75bb000
set_thread_area({entry_number:-1 -> 6, base_addr:0xb75bb900, limit:1048575, seg_32bit:1, contents:0, read_exec_only:0, limit_in_pages:1, seg_not_present:0, useable:1}) = 0
mprotect(0xb7760000, 8192, PROT_READ) = 0
mprotect(0x8049000, 4096, PROT_READ) = 0
mprotect(0xb77a1000, 4096, PROT_READ) = 0
munmap(0xb7766000, 95172) = 0
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
open("1", O_WRONLY) = -1 ENOENT (No such file or directory)
exit_group(-1) = ?
在strace输出结束时,您可以看到在打开的系统调用之间没有调用系统调用。那么,如果没有系统调用,如何调用被调用函数的堆栈内存?
答案 0 :(得分:4)
主线程的堆栈内存由内核在execve()
系统调用期间分配。在此调用期间,还会设置可执行文件中定义的其他映射(也可能还包括可执行文件中指定的动态链接器)。对于ELF文件,这可以在fs/binfmt_elf.c
。
其他线程的堆栈内存由线程支持库mmap()
编写,该库通常是C运行时库的一部分。
您还应该注意,在虚拟内存系统上,主线程堆栈由内核增长以响应页面错误,最多可达到可配置的限制(由ulimit -s
显示)。
答案 1 :(得分:2)
您的(单线程)程序堆栈大小是固定的,因此无法进一步分配。
您可以使用ulimit -s
命令查询并增加此大小。
请注意,即使您将此限制设置为“无限制”,也始终存在实际限制:
使用32位进程,除非RAM / swap较低,否则虚拟内存空间限制将导致地址冲突
使用64位进程,内存(RAM +交换)耗尽会破坏您的系统并最终导致程序崩溃。
无论如何,从来没有明确的系统调用期望会增加堆栈大小,只有在程序启动时才会设置。
另请注意,堆栈内存的处理方式与堆内存完全相同,即只有已访问过的内存部分才会映射到实际内存(RAM或交换)。这意味着堆栈类型按需增长,但除了标准虚拟内存管理之外没有其他机制正在处理它。
答案 2 :(得分:1)
在递归“降至最低”之前,您的程序不会开始进行任何open
调用。此时,堆栈被分配,它刚刚从嵌套中弹出。
为什么不用调试器逐步完成它。
答案 3 :(得分:1)
您是否想知道为函数创建的“堆栈帧”的变量分配位置? 我修改了你的程序,以显示堆栈变量k的内存地址和参数变量kk,
//Show stack location for a variable, k
#include <stdio.h>
int foo(int i)
{
int k=9;
if(i>=10) //relax the condition, safer
return 1;
else
foo(++i);
open("1",1);
//return i;
}
int bar(int kk, int i)
{
int k=9;
printf("&k: %x, &kk: %x\n",&k,&kk); //address variable on stack, parameter
if(i<10) //relax the condition, safer
bar(k,++i);
else
return 1;
return k;
}
int main()
{
//foo(1);
bar(0,1);
}
输出,在我的系统上,
$ ./foo
&k: bfa8064c, &kk: bfa80660
&k: bfa8061c, &kk: bfa80630
&k: bfa805ec, &kk: bfa80600
&k: bfa805bc, &kk: bfa805d0
&k: bfa8058c, &kk: bfa805a0
&k: bfa8055c, &kk: bfa80570
&k: bfa8052c, &kk: bfa80540
&k: bfa804fc, &kk: bfa80510
&k: bfa804cc, &kk: bfa804e0
&k: bfa8049c, &kk: bfa804b0
答案 4 :(得分:1)
堆栈使用和分配(至少在Linux上)以这种方式工作:
ulimit
限制(和SIGSEGV
s),或者如果不存在,则会发生这种情况,直到达到保护范围(然后获得SIGBUS
)。