日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

brk(), sbrk() 用法详解【转】

發布時間:2024/4/13 编程问答 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 brk(), sbrk() 用法详解【转】 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉自:http://blog.csdn.net/sgbfblog/article/details/7772153

貼上原文地址,好不容易找到了:brk(), sbrk() -- 改變數據段長度

?

brk() , sbrk() 的聲明如下:

?

[cpp]?view plaincopy
  • #include?<unistd.h>??
  • int?brk(void?*addr);??
  • void?*sbrk(intptr_t?increment);??

  • 這兩個函數都用來改變 "program break" (程序間斷點)的位置,這個位置可參考下圖:

    ?

    如 man 里說的:

    引用 brk()??and??sbrk() change the location of the program break, which defines the end of the process's data segment (i.e., the program break is the first location after the end of the uninitialized data segment).?

    brk() 和 sbrk() 改變 "program brek" 的位置,這個位置定義了進程數據段的終止處(也就是說,program break 是在未初始化數據段終止處后的第一個位置)。
    如此翻譯過來,似乎會讓人認為這個 program break 是和上圖中矛盾的,上圖中的 program break 是在堆的增長方向的第一個位置處(堆和棧的增長方向是相對的),而按照說明手冊來理解,似乎是在 bss segment 結束那里(因為未初始化數據段一般認為是 bss segment)。

    ?


    首先說明一點,一個程序一旦編譯好后,text segment ,data segment 和 bss segment 是確定下來的,這也可以通過 objdump 觀察到。下面通過一個程序來測試這個 program break 是不是在 bss segment 結束那里:

    ?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<unistd.h>??
  • #include?<stdlib.h>??
  • #include?<sys/time.h>??
  • #include?<sys/resource.h>??
  • ???
  • ???
  • int?bssvar;????//聲明一個味定義的變量,它會放在?bss?segment?中??
  • ???
  • ???
  • int?main(void)??
  • {??
  • ????char?*pmem;??
  • ????long?heap_gap_bss;??
  • ???
  • ???
  • ????printf?("end?of?bss?section:%p\n",?(long)&bssvar?+?4);??
  • ???
  • ???
  • ????pmem?=?(char?*)malloc(32);??????????//從堆中分配一塊內存區,一般從堆的開始處獲取??
  • ????if?(pmem?==?NULL)?{??
  • ????????perror("malloc");??
  • ????????exit?(EXIT_FAILURE);??
  • ????}??
  • ???
  • ???
  • ????printf?("pmem:%p\n",?pmem);??
  • ???
  • ???
  • //計算堆的開始地址和?bss?segment?結束處得空隙大小,注意每次加載程序時這個空隙都是變化的,但是在同一次加載中它不會改變??
  • ????heap_gap_bss?=?(long)pmem?-?(long)&bssvar?-?4;????????????
  • ????printf?("1-gap?between?heap?and?bss:%lu\n",?heap_gap_bss);??
  • ???
  • ???
  • ????free?(pmem);???//釋放內存,歸還給堆??
  • ???????
  • ????sbrk(32);????????//調整?program?break?位置(假設現在不知道這個位置在堆頭還是堆尾)??
  • ?????pmem?=?(char?*)malloc(32);???//再一次獲取內存區??
  • ????????if?(pmem?==?NULL)?{??
  • ????????????????perror("malloc");??
  • ????????????????exit?(EXIT_FAILURE);??
  • ????????}??
  • ???
  • ???
  • ????????printf?("pmem:%p\n",?pmem);???//檢查和第一次獲取的內存區的起始地址是否一樣??
  • ????heap_gap_bss?=?(long)pmem?-?(long)&bssvar?-?4;??//計算調整?program?break?后的空隙??
  • ????printf?("2-gap?between?heap?and?bss:%lu\n",?heap_gap_bss);??
  • ???
  • ???
  • ????free(pmem);???//釋放??
  • ????return?0;??
  • }??

  • 下面,我們分別運行兩次程序,并查看其輸出:


    ?

    引用 [beyes@localhost C]./sbrkendofbsssection:0x8049938pmem:0x82ec0081?gapbetweenheapandbss:2762448pmem:0x82ec0082?gapbetweenheapandbss:2762448[beyes@localhostC]./sbrkendofbsssection:0x8049938pmem:0x82ec0081?gapbetweenheapandbss:2762448pmem:0x82ec0082?gapbetweenheapandbss:2762448[beyes@localhostC]?./sbrk?
    end of bss section:0x8049938
    pmem:0x8dbc008
    1-gap between heap and bss:14100176
    pmem:0x8dbc008
    2-gap between heap and bss:14100176


    從上面的輸出中,可以發現幾點:
    1. bss 段一旦在在程序編譯好后,它的地址就已經規定下來。
    2. 一般及簡單的情況下,使用 malloc() 申請的內存,釋放后,仍然歸還回原處,再次申請同樣大小的內存區時,還是從第 1 次那里獲得。
    3. bss segment 結束處和堆的開始處的空隙大小,并不因為 sbrk() 的調整而改變,也就是說明了 program break 不是調整堆頭部。

    所以,man 手冊里所說的??“program break 是在未初始化數據段終止處后的第一個位置” ,不能將這個位置理解為堆頭部。這時,可以猜想應該是在堆尾部,也就是堆增長方向的最前方。下面用程序進行檢驗:

    當 sbrk() 中的參數為 0 時,我們可以找到 program break 的位置。那么根據這一點,檢查一下每次在程序加載時,系統給堆的分配是不是等同大小的:

    ?

    ?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<unistd.h>??
  • #include?<stdlib.h>??
  • #include?<sys/time.h>??
  • #include?<sys/resource.h>??
  • ???
  • ???
  • int?main(void)??
  • {??
  • ????????void?*tret;??
  • ????????char?*pmem;??
  • ???
  • ???
  • ???
  • ????????pmem?=?(char?*)malloc(32);??
  • ????????if?(pmem?==?NULL)?{??
  • ????????????????perror("malloc");??
  • ????????????????exit?(EXIT_FAILURE);??
  • ????????}??
  • ???
  • ???
  • ????????printf?("pmem:%p\n",?pmem);??
  • ???
  • ????????tret?=?sbrk(0);??
  • ????????if?(tret?!=?(void?*)-1)??
  • ????????????????printf?("heap?size?on?each?load:?%lu\n",?(long)tret?-?(long)pmem);??
  • ???
  • ???
  • ????return?0;??
  • }??

  • 運行上面的程序 3 次:

    ?

    引用 [beyes@localhost C]./sbrkpmem:0x80c9008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x80c9008heapsizeoneachload:135160[beyes@localhostC]?./sbrk?
    pmem:0x9682008
    heap size on each load: 135160
    [beyes@localhost C]./sbrkpmem:0x9a7d008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x9a7d008heapsizeoneachload:135160[beyes@localhostC]?./sbrk?
    pmem:0x8d92008
    heap size on each load: 135160
    [beyes@localhost C]$ vi sbrk.c

    從輸出可以看到,雖然堆的頭部地址在每次程序加載后都不一樣,但是每次加載后,堆的大小默認分配是一致的。但是這不是不能改的,可以使用 sysctl 命令修改一下內核參數:

    引用 #sysctl -w kernel/randomize_va_space=0

    這么做之后,再運行 3 次這個程序看看:

    引用 [beyes@localhost C]./sbrkpmem:0x804a008heapsizeoneachload:135160[beyes@localhostC]./sbrkpmem:0x804a008heapsizeoneachload:135160[beyes@localhostC]?./sbrk?
    pmem:0x804a008
    heap size on each load: 135160
    [beyes@localhost C]$ ./sbrk?
    pmem:0x804a008
    heap size on each load: 135160

    從輸出看到,每次加載后,堆頭部的其實地址都一樣了。但我們不需要這么做,每次堆都一樣,容易帶來緩沖區溢出攻擊(以前老的 linux 內核就是特定地址加載的),所以還是需要保持 randomize_va_space 這個內核變量值為 1 。

    下面就來驗證 sbrk() 改變的 program break 位置在堆的增長方向處:

    ?

    ?

    [cpp]?view plaincopy
  • #include?<stdio.h>??
  • #include?<unistd.h>??
  • #include?<stdlib.h>??
  • #include?<sys/time.h>??
  • #include?<sys/resource.h>??
  • ???
  • ???
  • int?main(void)??
  • {??
  • ????????void?*tret;??
  • ????????char?*pmem;??
  • ????????int?i;??
  • ????????long?sbrkret;??
  • ???
  • ???????pmem?=?(char?*)malloc(32);??
  • ????????if?(pmem?==?NULL)?{??
  • ????????????????perror("malloc");??
  • ????????????????exit?(EXIT_FAILURE);??
  • ????????}??
  • ???
  • ???
  • ????????printf?("pmem:%p\n",?pmem);??
  • ???
  • ?????????for?(i?=?0;?i?<?65;?i++)?{??
  • ????????????????sbrk(1);??
  • ????????????????printf?("%d\n",?sbrk(0)?-?(long)pmem?-?0x20ff8);???//0x20ff8?就是堆和?bss段?之間的空隙常數;改變后要用?sbrk(0)?再次獲取更新后的program?break位置??
  • ????????}??
  • ???????free(pmem);??
  • ???
  • ??????????
  • ???????return?0;??
  • }??

  • 運行輸出:

    ?

    引用 [beyes@localhost C]$ ./sbrk?
    pmem:0x804a008
    1
    2
    3
    4
    5

    ... ...
    61
    62
    63
    64


    從輸出看到,sbrk(1) 每次讓堆往棧的方向增加 1 個字節的大小空間。

    ?

    而 brk() 這個函數的參數是一個地址,假如你已經知道了堆的起始地址,還有堆的大小,那么你就可以據此修改 brk() 中的地址參數已達到調整堆的目的。

    實際上,在應用程序中,基本不直接使用這兩個函數,取而代之的是 malloc() 一類函數,這一類庫函數的執行效率會更高。還需要注意一點,當使用 malloc() 分配過大的空間,比如超出 0x20ff8 這個常數(在我的系統(Fedora15)上是這樣,別的系統可能會有變)時,malloc 不再從堆中分配空間,而是使用 mmap() 這個系統調用從映射區尋找可用的內存空間。












    本文轉自張昺華-sky博客園博客,原文鏈接:http://www.cnblogs.com/sky-heaven/p/6283441.html,如需轉載請自行聯系原作者

    總結

    以上是生活随笔為你收集整理的brk(), sbrk() 用法详解【转】的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。