いっしょにtalk C栗(第百三十回:C言語例--Cプログラムメモリレイアウト二)


皆さん、こんにちは、前回はCプログラムのメモリレイアウトの例を話しましたが、今回はこの例を続けてみましょう.余談はさておき,話は本筋に戻る.一緒にtalk C栗を食べましょう!
前回では、メモリ内のCプログラムのレイアウトを紹介し、簡単なプレゼンテーションを行いました.前回の例は比較的簡単で、プログラム内のメモリレイアウトの概略的な輪郭しか説明できません.今日は具体的なメモリアドレスを通じて、メモリ内のCプログラムのレイアウトを明確に紹介します.次に例のプログラムソースコードを示します.参考にしてください.
int ga1;
int ga2 = 1;

int func()
{
    int i = 0;
    static static_la1;
    static static_la2 = 3;

    printf("func is running 
"
); printf("Address of i: %p
"
,&i); printf("Address of static_la1 : %p
"
,&static_la1); printf("Address of static_la2 : %p
"
,&static_la2); while(i++<8) sleep(1); return 0; } int main() { int la1; int la2 = 2; int *p; p = (int *) malloc(3*sizeof(int)); if(NULL == p) printf("malloc failed
"
); printf("Address of ga1: %p
"
,&ga1); printf("Address of ga2: %p
"
,&ga2); printf("Address of la1: %p
"
,&la1); printf("Address of la2: %p
"
,&la2); printf("Address of p: %p
"
,p); func(); free(p); p = NULL; return 0; }

このコードでは,各変数のアドレスを手動で出力し,変数のアドレスによって変数のメモリにおける位置を判断し,変数がメモリレイアウトにおけるどのパーティションに属するかを確認することを目的とする.では、これらの変数はいったいどのパーティションにあるのでしょうか.dataパーティションですか、bssパーティションですか.以下はプログラムの実行結果です.参考にしてください.
Address of ga1: 0x804a040 
Address of ga2: 0x804a030 
Address of la1: 0xbfc7e7b4 
Address of la2: 0xbfc7e7b8 
Address of p: 0x8ffc008 
func is running 
Address of i: 0xbfc7e78c 
Address of static_la1 : 0x804a03c 
Address of static_la2 : 0x804a034 

プログラムの実行結果から、各変数のメモリ内のアドレスがわかりますが、これらの変数がどのメモリパーティションに属しているかは分かりません.急いではいけません.readelfツールを使ってプログラムのメモリレイアウトを見て、メモリレイアウトを通じて各メモリパーティションのアドレス範囲を見ることができます.これらは変数のアドレスに基づいて変数が存在するメモリパーティションを推定することができます.以下に詳細な結果を示します.
readelf -S s            //  readelf            
There are 30 section headers, starting at offset 0x1190:

Section Headers:
  [Nr] Name              Type            Addr     Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            00000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        08048154 000154 000013 00   A  0   0  1
  [ 2] .note.ABI-tag     NOTE            08048168 000168 000020 00   A  0   0  4
  [ 3] .note.gnu.build-i NOTE            08048188 000188 000024 00   A  0   0  4
  [ 4] .gnu.hash         GNU_HASH        080481ac 0001ac 000020 04   A  5   0  4
  [ 5] .dynsym           DYNSYM          080481cc 0001cc 000090 10   A  6   1  4
  [ 6] .dynstr           STRTAB          0804825c 00025c 000063 00   A  0   0  1
  [ 7] .gnu.version      VERSYM          080482c0 0002c0 000012 02   A  5   0  2
  [ 8] .gnu.version_r    VERNEED         080482d4 0002d4 000020 00   A  6   1  4
  [ 9] .rel.dyn          REL             080482f4 0002f4 000008 08   A  5   0  4
  [10] .rel.plt          REL             080482fc 0002fc 000038 08   A  5  12  4
  [11] .init             PROGBITS        08048334 000334 000023 00  AX  0   0  4
  [12] .plt              PROGBITS        08048360 000360 000080 04  AX  0   0 16
  [13] .text             PROGBITS        080483e0 0003e0 0002a2 00  AX  0   0 16
  [14] .fini             PROGBITS        08048684 000684 000014 00  AX  0   0  4
  [15] .rodata           PROGBITS        08048698 000698 0000dc 00   A  0   0  4
  [16] .eh_frame_hdr     PROGBITS        08048774 000774 000034 00   A  0   0  4
  [17] .eh_frame         PROGBITS        080487a8 0007a8 0000d0 00   A  0   0  4
  [18] .init_array       INIT_ARRAY      08049f08 000f08 000004 00  WA  0   0  4
  [19] .fini_array       FINI_ARRAY      08049f0c 000f0c 000004 00  WA  0   0  4
  [20] .jcr              PROGBITS        08049f10 000f10 000004 00  WA  0   0  4
  [21] .dynamic          DYNAMIC         08049f14 000f14 0000e8 08  WA  6   0  4
  [22] .got              PROGBITS        08049ffc 000ffc 000004 04  WA  0   0  4
  [23] .got.plt          PROGBITS        0804a000 001000 000028 04  WA  0   0  4
  [24] .data             PROGBITS        0804a028 001028 000010 00  WA  0   0  4
  [25] .bss              NOBITS          0804a038 001038 00000c 00  WA  0   0  4
  [26] .comment          PROGBITS        00000000 001038 00004f 01  MS  0   0  1
  [27] .shstrtab         STRTAB          00000000 001087 000106 00      0   0  1
  [28] .symtab           SYMTAB          00000000 001640 0004c0 10     29  47  4
  [29] .strtab           STRTAB          00000000 001b00 0002be 00      0   0  1
Key to Flags:
  W (write), A (alloc), X (execute), M (merge), S (strings)
  I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown)
  O (extra OS processing required) o (OS specific), p (processor specific)

実行結果からプログラムの
dataパーティションは、アドレス0 x 0804 a 028から始まり、サイズは0 x 000010(16 byte)である.bssパーティションは0 x 0804 a 038から始まり、サイズは0 x 00000 c(12 byte)である.
では、プログラム内の変数のアドレスと組み合わせて、変数が存在するメモリパーティションを推定します.
Address of ga1: 0x804a040    //       bss(0x0804a038),    bss  
Address of ga2: 0x804a030    //       data(0x0804a028),    bss,    data  
Address of static_la1 : 0x804a03c    //       bss(0x0804a038),    bss  
Address of static_la2 : 0x804a034    //       data(0x0804a028),    bss,    data  

また、4つの変数のアドレスはbssとdataパーティションの範囲内ではないため、これらの変数が存在するパーティションを特定することはできません.
Address of la1: 0xbfc7e7b4   //       bss(0x0804a038),     bss     
Address of la2: 0xbfc7e7b8   //       bss(0x0804a038),     bss     
Address of p: 0x8ffc008      //       bss(0x0804a038),     bss     
Address of i: 0xbfc7e78c     //       bss(0x0804a038),     bss     

前の章で紹介したように、重要なパーティションは4種類で、dataとbssではなく、スタックまたはスタックにしかありません.今は変数のアドレスしかありませんが、スタック領域やスタック領域のアドレス範囲があればいいです.これにより、bssとdataパーティションの変数を推定するのと同じように、メモリ内のこれらの変数のパーティションを推定することができます.
次に、プログラムのスタックとスタックパーティションを探してみましょう.前の章で紹介したproc仮想ファイルシステムを覚えていますか?スタック領域とスタック領域のメモリ領域情報を取得するために使用できます.
 ./s &                               //           
[1] 2736                                                                                    //         ,       PID
Address of ga1: 0x804a040            //    ,        
Address of ga2: 0x804a030 
Address of la1: 0xbfe01cf4 
Address of la2: 0xbfe01cf8 
Address of p: 0x964d008 
func is running 
Address of i: 0xbfe01ccc 
Address of static_la1 : 0x804a03c 
Address of static_la2 : 0x804a034 
cat /proc/2736/maps                   //  proc        
08048000-08049000 r-xp 00000000 08:13 22415696   /home/talk8/s
08049000-0804a000 r--p 00000000 08:13 22415696 /home/talk8/s
0804a000-0804b000 rw-p 00001000 08:13 22415696   /home/talk8/s
0964d000-0966e000 rw-p 00000000 00:00 0          [heap]  
b75b7000-b75b8000 rw-p 00000000 00:00 0 
b75b8000-b7761000 r-xp 00000000 08:16 6444       /lib/i386-linux-gnu/libc-2.19.so
b7761000-b7763000 r--p 001a9000 08:16 6444 /lib/i386-linux-gnu/libc-2.19.so
b7763000-b7764000 rw-p 001ab000 08:16 6444       /lib/i386-linux-gnu/libc-2.19.so
b7764000-b7767000 rw-p 00000000 00:00 0 
b777e000-b7781000 rw-p 00000000 00:00 0 
b7781000-b7782000 r-xp 00000000 00:00 0          [vdso]
b7782000-b77a2000 r-xp 00000000 08:16 6459       /lib/i386-linux-gnu/ld-2.19.so
b77a2000-b77a3000 r--p 0001f000 08:16 6459 /lib/i386-linux-gnu/ld-2.19.so
b77a3000-b77a4000 rw-p 00020000 08:16 6459       /lib/i386-linux-gnu/ld-2.19.so
bfde3000-bfe04000 rw-p 00000000 00:00 0          [stack]

上記の情報から、[heap]と[stack]とマークされたパーティションは、同じ行の内容が2つのパーティションのメモリアドレス空間を示すスタック領域とスタック領域であることがわかります.
ヒープ領域:0 x 094 d 000-0 x 0966 e 000スタック領域:0 xbfde 3000-0 xbfe 04000
今、私たちはさっきどの4つの自分のパーティションを知らない変数を推定することができます.次は私たちの推定結果です.
Address of la1: 0xbfe01cf4   //              ,       
Address of la2: 0xbfe01cf8   //              ,       
Address of p: 0x964d008      //              ,       
Address of i: 0xbfe01ccc     //              ,       

これまで、Cプログラムのメモリ内のレイアウトを具体的なメモリアドレスで明確に示してきました.最後に直感的な図形を描いて見せて、Cプログラムのメモリモデルを直感的に見ることができます.このグラフィックは比較的簡単で、グラフィックの中で上から下を見ると、メモリアドレスが高アドレスから低アドレスに延び、各アドレスの後ろにはそのアドレスに対応するパーティション名が表示されます.
官达を见て、完全なコードは私の资源の中に置いて、みんなはここをクリックしてダウンロードして使うことができます(CSDNはまた问题が発生して、だからしばらくプログラムをアップロードすることができなくて、CSDNが问题を修复した后に、私は直ちにプログラムをアップロードして、その时みんなは私达の资源の中でプログラムをダウンロードすることができます).パソコンは人によって異なり、使用するプログラミング環境も異なる場合がありますので、視聴者の皆さんが自分のパソコンにプログラムをダウンロードして実行し、私たちが紹介した方法でメモリ内のモデルを観察することをお勧めします.
皆さん、Cプログラムのメモリレイアウトの例についてお話しします.後に何か例があるか知りたいので、次の分解を聞いてください.