将内存分配给递归函数

时间:2013-10-28 16:34:43

标签: linux memory c function

我写了一个简单的程序,如下所示。

#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输出结束时,您可以看到在打开的系统调用之间没有调用系统调用。那么,如果没有系统调用,如何调用被调用函数的堆栈内存?

5 个答案:

答案 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上)以这种方式工作:

  • 分配了一点堆栈。
  • 在程序的“其他”部分之后设置保护范围,大约是地址空间的1/4。
  • 如果堆栈在其顶部及以上使用,则堆栈会自动增加。
  • 如果达到ulimit限制(和SIGSEGV s),或者如果不存在,则会发生这种情况,直到达到保护范围(然后获得SIGBUS)。