3

以下のような簡単なプログラムを書いてトレースしました。

#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) にメモリがどのように割り当てられているかを確認することでした。open システム コールをマーカーとして使用しました。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 出力の終わりに向かって、open システム コールの間にシステム コールが呼び出されていないことがわかります。では、システムコールなしで、関数が呼び出されるために、スタックにどのようにメモリが割り当てられるのでしょうか?

4

5 に答える 5

4

メイン スレッドのスタック メモリは、execve()システム コール中にカーネルによって割り当てられます。この呼び出し中に、実行可能ファイルで定義された他のマッピング (および実行可能ファイルで指定された動的リンカーに対しても) もセットアップされます。ELF ファイルの場合、これは で行われfs/binfmt_elf.cます。

他のスレッドのスタック メモリmmap()は、通常 C ランタイム ライブラリの一部であるスレッド サポート ライブラリによって使用されます。

また、仮想メモリ システムでは、ページ フォールトに応答してカーネルによってメイン スレッド スタックが構成可能な制限 ( で表示) まで拡大されることにも注意してくださいulimit -s

于 2013-10-28T16:48:53.827 に答える
2

(シングル スレッド) プログラムのスタック サイズは固定されているため、それ以上の割り当てはありません。

コマンドを使用して、このサイズを照会して増やすことができますulimit -s

この制限を「無制限」に設定しても、実際には常に制限があることに注意してください。

  • 32 ビット プロセスでは、RAM/スワップが不足していない限り、仮想メモリ スペースの制限によりアドレス衝突が発生します。

  • 64 ビット プロセスでは、メモリ (RAM + スワップ) の枯渇によりシステムがスラッシングし、最終的にプログラムがクラッシュします。

いずれにせよ、スタック サイズが増加することを期待する明示的なシステム コールは決してなく、プログラムの開始時にのみ設定されます。

また、スタック メモリはヒープ メモリとまったく同じように処理されることに注意してください。つまり、アクセスされた部分のみが実際のメモリ (RAM またはスワップ) にマップされます。これは、スタックの種類がオンデマンドで成長することを意味しますが、標準の仮想メモリ管理以外のメカニズムはそれを処理していません。

于 2013-10-28T16:51:01.133 に答える
1

スタックの使用と割り当て (少なくとも Linux では) は次のように機能します。

  • 少しのスタックが割り当てられます。
  • ガード範囲は、プログラムの「その他」の部分の後に、アドレス空間の約 1/4 に設定されます。
  • スタックがその一番上まで使用された場合、スタックは自動的に増加します。
  • これは、ulimit制限に達した場合 (およびSIGSEGVs)、またはそのようなものが存在しない場合は、ガード範囲に到達するまで (および を取得するまでSIGBUS) 発生します。
于 2013-10-29T05:38:08.803 に答える
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
于 2013-10-28T22:30:54.400 に答える
1

open再帰が「底をつく」まで、プログラムは呼び出しを開始しません。その時点で、スタックが割り当てられ、ネストから飛び出します。

デバッガーでステップスルーしてみませんか。

于 2013-10-28T18:13:12.810 に答える