
代碼裝在內存里,卻沒東談主真見過它長啥樣,體式跑著跑著就崩了,到底哪塊兒出了問題?我今天我方泉源扒了一遍。
我過去以為內存分區便是講義上畫的那幾條橫線,棧在上面堆不才頭,什么常量區數據區的,背得挺熟。后果寫了個小體式,拿`/proc/[pid]/maps`一瞅,全亂了——`r-xp`段好幾個,`rw-p`連著三塊,還有塊`r--p`標著`[anon]`,根底沒聽過。本來講義說的“五區”,僅僅老誠為了好講,硬湊出來的說法。

C話語圭臬里根本沒提“棧”“堆”這兩個字。翻了翻PDF版C11草案,在6.2.4節只寫了“自動存儲期”和“靜態存儲期”,至于存在哪兒,圭臬說:不歸我管。這話聽著有點放膽不論的真諦,但其實很果真——編譯器和操作系統才決定東西放哪兒。GCC把`int x = 5;`扔`.data`,把`char s[] = "hi";`塞`.rodata`,而`const char *p = "bye";`呢?字符串也曾在`.rodata`,指針`p`卻在`.data`里,值是指向阿誰只讀地址。這下顯豁為啥改字符串字面量會段誕妄了——不是體式錯了,是硬件平直攔住不讓你寫。
我試了`size a.out`,發現`.bss`大小果然是0,天然代碼里寫了`int a, b, c;`三個沒開動化的全局變量。再查`readelf -S`,果然`.bss`標著`ALLOC`但沒標`LOAD`,真諦便是:磁盤上不存,加載時系統順遂清零就行。省空間,也快。`.rodata`卻老渾樸實占著硬盤,因為“hello”這種字符串得從文獻讀進去。是以`const int x = 123;`進`.rodata`,`const int y = z + 1;`(z是變量)就不成——編譯時算不出來,只可放`.data`。
棧也沒思象中那么“肥碩上”。我寫了個函數,里頭開個`int arr[100000];`,一跑就段誕妄。`ulimit -s`一看,8192KB。不是編譯器攔你,是Linux內核在頁內外設的紅線,越界就發`SIGSEGV`。奇怪的是,用`alloca(100000)`也崩,但崩得更“脆”,連調試信息齊少半截。棧確乎是CPU和編譯器聯手搭的架子,`call`推幀,`ret`彈幀,干凈利落,但沒緩沖區。
堆更實踐。`malloc(100)`看起來通俗,背后是glibc的ptmalloc在艱苦:查tcache有莫得現成塊,莫得就找fastbin,再不成就碰`brk`或`mmap`。我用`malloc_stats`打出來,看到“fastbins”“unsorted bin”一堆名詞,才懂為啥小內存分撥其實挺快——很厚情況根本沒動系統調用。但`malloc(200*1024)`(200KB)就平直`mmap`了,2026美加墨世界杯中國認證平臺`/proc/[pid]/maps`里多了個獨處的`rw-p`段,跟堆干線斷開了。不是`malloc`偷懶,是OS以為這樣大一塊,單獨管更省事。
我還試了`thread_local int t = 99;`,`pstack`看不出啥,但`/proc/[pid]/maps`里多了一小塊帶`[stack:xxx]`標記的內存,每個線程一份。TLS不是存在某個“零散區”,便是給每個線程暗暗劃了一小塊地,名字叫`dtv`,glibc我方記住。還有`mmap`映文獻,一個`open+read`變`open+mmap`,讀大文獻時少一次拷貝,但內存用量平直變大——這些齊不是C話語教的,是Linux給你開的后門。
開云app中國2026世界杯官方下載終末我把悉數變量地址全打出來:全局`g`在`0x404024`(`readelf`闡明是`.data`),字符串`"abc"`在`0x402004`(`.rodata`),局部`int x`在`0x7fff...`(棧),`malloc`轉頭的在`0x7f...`(堆)。地址數字自己沒真諦,但看權限就懂:棧地址`rw-p`,代碼地址`r-xp`,`.rodata`地址`r--p`。W^X安全模子就靠這個撐著——寫代碼段?不成。奉行數據段?也不成。當代系統不是靠體式員自愿,是靠硬件頁表死卡著。
考證這事真不難。寫個十幾行的C文獻,`gcc -g -O0`編譯,后臺跑起來,查`pid`,再貓進`/proc/[pid]/maps`,左邊地址右邊權限,中間旅途,一目了然。`pstack`看棧幀,`cat /proc/[pid]/status`看總內存。不需要懂匯編,也無須背術語,眼睛盯著`r-xp`和`rw-p`,比啥齊準。
許多東談主說堆比棧慢,我測了下100次`malloc(8)`和`int x[8]`,技藝差不到0.01毫秒。真慢的是`free`之后又`malloc`,碎屑多了就得并吞。或者你`malloc`一兆再`free`,后果別東談主`malloc`兩百字節卡半天——不是堆慢,是懲辦政策在衡量。選棧也曾堆,看生命周期就行:函數里用,走棧;要傳出去、要活久點,走堆。別的齊是障眼法。
我刪掉了悉數“常量區”“靜態區”的條記。`.rodata`便是`.rodata`,它跟`.data`同屬數據段,但權限不同。`static`變量放在`.data`或`.bss`,跟“靜態”倆字不蹙迫,只跟有無開動值相干。術語越疲塌,debug越持瞎。
當今我看體式,不先思語法,先思內存。`printf("%s", p);`崩了?先查`p`指向哪——棧上局部數組已開釋?堆上`free`過甚?也曾`.rodata`里改了字符串?地址一打2026美加墨世界杯(中國),權限一看,約略心里就稀有了。