在Linux裡面有一個虛擬的檔案系統,由kernel提供的, 位置在/proc 

裡面有關於整個系統的資訊,像是
/proc/uptime 存放開機時間,你下指令uptime就是從這邊讀的
不然就是htop, glances, ps, free 等proc工具也會來讀這個目錄
另外就是修改kernel的參數也是來這邊改的(也能用sysctl指令來改)
像是最常見的就是,讓系統可以當router轉發封包的指令
echo 1 > /proc/sys/net/ipv4/ip_forward

 

這個資料夾裡面最多的就是目前系統上面跑的行程PID
裡面有status,maps等虛擬檔案
其中maps是紀錄著這個process在memory中的Layout配置
一般來說,一個執行檔在磁碟中,只有code(也稱作text)段和 data段
載入到記憶體中執行時,才會額外多出bss , heap , stack 段
code段,就是存放程式碼的區段,這一區是唯讀的,而且可執行
data段,就是存放初始化的全域變數,static區域變數等的地方
bss段,就是存放 未初始變數的地方,像是全域變數等等(注意,全域變數你assign一個0值仍然算是未初始化)
heap段,就是給malloc動態配置記憶體用的區段,這一區是往高位址的方向成長
stack段,就是給function裡面的auto區域變數、return address , parameter放的地方

這一段是往低位址的方向成長.
另外,如果你的C code有用VLA(Variable Length Array)讓陣列大小可以在runtime時
再來決定的話,那麼你用GCC編譯,GCC會將VLA放在stack這一段空間,
而不像malloc放在heap段.
 

 

搞懂這些區域對於組合語言的學習有很大的幫助,另外就是可以了解C語言的設計
像是
char *s1="string1";
char s2[]="string2";
為什麼前者s1不能更改字串內容(因為位於.rodata段),而後者s2卻可以(因為位於.data段)
或者是說auto的區域變數為什麼離開function後生命周期會消失
還有像是安全方面的 buffer-overflow、heap-overflow, heap-overread 等知識

 

Linux裡面有個size指令可以看一個執行檔的這些區段內容

 

譬如以我目前本機的 bash 為例

 

mint@mint ~ $ size `which bash`
   text         data       bss     dec           hex  filename
 977292   36536   23448 1037276   fd3dc /bin/bash

 

它這裡的bss段會顯示這麼大,但是並不會佔執行檔本身的大小

用兩隻程式來實驗一下

abc.c內容

#include<stdio.h>
int main()

{return 0;}

abc2.c內容

#include<stdio.h>
long double a[1024000];
int main()
{return 0;}

分別去編譯它們,並用ls -l 看他們的大小

-rwxr-xr-x 1 mint mint 8462 Dec 29 01:04 abc
-rwxr-xr-x 1 mint mint 8489 Dec 29 01:03 abc2

兩個執行檔只差了27byte, 很明顯地全域變數的大小並沒有佔ELF執行檔的空間.
雖然用size觀察的結果,前者的bss是 8 byte , 後者是 16384032 byte

bss段的大小,因為節約空間的考量,執行檔裡面是不會有這一段的
是載入到記憶體後,OS才會配一塊空間給它

 

再來看 bash的maps檔案內容,目前的bash pid是 7335
/proc/7335/maps檔案內容

以空白來分隔欄位

第一欄是記憶體位址範圍,也就是代表那一段的大小(這些位址都是虛擬的)
第二欄是 rwx還有private的權限
其它欄位可以參考系統上的man proc

或是網路上的manpage

http://man7.org/linux/man-pages/man5/proc.5.html

stackoverflow也有解說

 

00400000-004ef000 r-xp 00000000 07:00 6  <-- code段               /bin/bash
006ee000-006ef000 r--p 000ee000 07:00 6   <-- code段               /bin/bash
006ef000-006f8000 rw-p 000ef000 07:00 6   <--  data段               /bin/bash
006f8000-006fe000 rw-p 00000000 00:00 0  <--  bss段
0076c000-00966000 rw-p 00000000 00:00 0                                  [heap]
7fd297c5a000-7fd297c65000 r-xp 00000000 07:00 9664                       /lib/x86_64-linux-gnu/libnss_files-2.19.so
7fd297c65000-7fd297e64000 ---p 0000b000 07:00 9664                       /lib/x86_64-linux-gnu/libnss_files-2.19.so
7fd297e64000-7fd297e65000 r--p 0000a000 07:00 9664                       /lib/x86_64-linux-gnu/libnss_files-2.19.so
7fd297e65000-7fd297e66000 rw-p 0000b000 07:00 9664                       /lib/x86_64-linux-gnu/libnss_files-2.19.so
7fd297e66000-7fd297e71000 r-xp 00000000 07:00 9674                       /lib/x86_64-linux-gnu/libnss_nis-2.19.so
7fd297e71000-7fd298070000 ---p 0000b000 07:00 9674                       /lib/x86_64-linux-gnu/libnss_nis-2.19.so
7fd298070000-7fd298071000 r--p 0000a000 07:00 9674                       /lib/x86_64-linux-gnu/libnss_nis-2.19.so
7fd298071000-7fd298072000 rw-p 0000b000 07:00 9674                       /lib/x86_64-linux-gnu/libnss_nis-2.19.so
7fd298072000-7fd298089000 r-xp 00000000 07:00 9658                       /lib/x86_64-linux-gnu/libnsl-2.19.so
7fd298089000-7fd298288000 ---p 00017000 07:00 9658                       /lib/x86_64-linux-gnu/libnsl-2.19.so
7fd298288000-7fd298289000 r--p 00016000 07:00 9658                       /lib/x86_64-linux-gnu/libnsl-2.19.so
7fd298289000-7fd29828a000 rw-p 00017000 07:00 9658                       /lib/x86_64-linux-gnu/libnsl-2.19.so
7fd29828a000-7fd29828c000 rw-p 00000000 00:00 0 
7fd29828c000-7fd298295000 r-xp 00000000 07:00 9660                       /lib/x86_64-linux-gnu/libnss_compat-2.19.so
7fd298295000-7fd298494000 ---p 00009000 07:00 9660                       /lib/x86_64-linux-gnu/libnss_compat-2.19.so
7fd298494000-7fd298495000 r--p 00008000 07:00 9660                       /lib/x86_64-linux-gnu/libnss_compat-2.19.so
7fd298495000-7fd298496000 rw-p 00009000 07:00 9660                       /lib/x86_64-linux-gnu/libnss_compat-2.19.so
7fd298496000-7fd29875f000 r--p 00000000 07:00 23126                      /usr/lib/locale/locale-archive
7fd29875f000-7fd29891b000 r-xp 00000000 07:00 9588                       /lib/x86_64-linux-gnu/libc-2.19.so
7fd29891b000-7fd298b1a000 ---p 001bc000 07:00 9588                       /lib/x86_64-linux-gnu/libc-2.19.so
7fd298b1a000-7fd298b1e000 r--p 001bb000 07:00 9588                       /lib/x86_64-linux-gnu/libc-2.19.so
7fd298b1e000-7fd298b20000 rw-p 001bf000 07:00 9588                       /lib/x86_64-linux-gnu/libc-2.19.so
7fd298b20000-7fd298b25000 rw-p 00000000 00:00 0 
7fd298b25000-7fd298b28000 r-xp 00000000 07:00 9605                       /lib/x86_64-linux-gnu/libdl-2.19.so
7fd298b28000-7fd298d27000 ---p 00003000 07:00 9605                       /lib/x86_64-linux-gnu/libdl-2.19.so
7fd298d27000-7fd298d28000 r--p 00002000 07:00 9605                       /lib/x86_64-linux-gnu/libdl-2.19.so
7fd298d28000-7fd298d29000 rw-p 00003000 07:00 9605                       /lib/x86_64-linux-gnu/libdl-2.19.so
7fd298d29000-7fd298d4e000 r-xp 00000000 07:00 9733                       /lib/x86_64-linux-gnu/libtinfo.so.5.9
7fd298d4e000-7fd298f4d000 ---p 00025000 07:00 9733                       /lib/x86_64-linux-gnu/libtinfo.so.5.9
7fd298f4d000-7fd298f51000 r--p 00024000 07:00 9733                       /lib/x86_64-linux-gnu/libtinfo.so.5.9
7fd298f51000-7fd298f52000 rw-p 00028000 07:00 9733                       /lib/x86_64-linux-gnu/libtinfo.so.5.9
7fd298f52000-7fd298f75000 r-xp 00000000 07:00 9564                       /lib/x86_64-linux-gnu/ld-2.19.so
7fd299150000-7fd299153000 rw-p 00000000 00:00 0 
7fd29916b000-7fd299172000 r--s 00000000 07:00 35281                      /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
7fd299172000-7fd299174000 rw-p 00000000 00:00 0 
7fd299174000-7fd299175000 r--p 00022000 07:00 9564                       /lib/x86_64-linux-gnu/ld-2.19.so
7fd299175000-7fd299176000 rw-p 00023000 07:00 9564                       /lib/x86_64-linux-gnu/ld-2.19.so
7fd299176000-7fd299177000 rw-p 00000000 00:00 0 
7fffd5e12000-7fffd5e33000 rw-p 00000000 00:00 0                          [stack]
7fffd5fcf000-7fffd5fd1000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]
 
code段(唯讀,可執行)的大小,減一減後,是983040 byte , 大約就是size指令觀查的977292
data段(可讀可寫)的大小,減一減後,是 36864 byte , 大約就是size指令觀查的 36536
bss段(可讀可寫)的大小,減一減後,是24576 byte , 大約就是size指令觀查的 23448

(另外你也可以參考 /proc/7335/status的檔案內容
裡面的VmExe、VmData、VmStk分別就是Code, Data, Stack段的大小)

heap段的大小是OS配給它的

stack段的大小會依照你的區域變數、環境變數、函式參數而有所不同

假設我的程式myfile只在main函式裡宣告一個long double陣列

long double ld[500000];

在我的機器上,long double的sizeof是16

因此,這個陣列就佔了 16*500000=8000000 bytes

那這時你再去看 它的stack 大小

mint@mint $ less /proc/`pidof myfile`/maps

裡面的 stack區段,相減之後就會大約是 8000000 bytes

因為stack裡還要放 return address , environment variable , function parameter等等

所以實際上不會剛剛好是 8000000 bytes.

但是這個stack區段並不會讓你開到無窮大,系統還是會給予限制 hard limit

一般來說stack段的hard limit在Linux下是剛好8MB,
你可以用 ulimit  指令來得知或修改
mint@mint $ ulimit -a
core file size          (blocks, -c) 0
data seg size           (kbytes, -d) unlimited
scheduling priority             (-e) 0
file size               (blocks, -f) unlimited
pending signals                 (-i) 125748
max locked memory       (kbytes, -l) 64
max memory size         (kbytes, -m) unlimited
open files                      (-n) 1024
pipe size            (512 bytes, -p) 8
POSIX message queues     (bytes, -q) 819200
real-time priority              (-r) 0
stack size              (kbytes, -s) 8192
cpu time               (seconds, -t) unlimited
max user processes              (-u) 125748
virtual memory          (kbytes, -v) unlimited
file locks                      (-x) unlimited

 

所以當你的程式一直在call function(譬如使用recursive),stack段裡面的
stack frame會一直增加下去,超過 stack 段的大小後(預設值8MB)就會爆了
跟你說記憶體區段錯誤Segmentation Fault.
不過有一種遞迴例外,就是尾遞迴tail-recursion,因為尾遞迴它本質上就是
迴圈,所以如果compiler設計良好,做了最佳化,你寫的尾遞迴不會stack
一直call下去.
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 fvalinux 的頭像
    fvalinux

    Elegance

    fvalinux 發表在 痞客邦 留言(0) 人氣()