• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            brk(), sbrk() 用法詳解 (轉(zhuǎn))

            Posted on 2012-12-27 12:15 鑫龍 閱讀(815) 評論(0)  編輯 收藏 引用 所屬分類: linux編程
            brk() , sbrk() 的聲明如下:
            #include <unistd.h>
            int brk(void *addr);
            void *sbrk(intptr_t increment);
            這兩個(gè)函數(shù)都用來改變 "program break" (程序間斷點(diǎn))的位置,這個(gè)位置可參考下圖:

            如 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" 的位置,這個(gè)位置定義了進(jìn)程數(shù)據(jù)段的終止處(也就是說,program break 是在未初始化數(shù)據(jù)段終止處后的第一個(gè)位置)。
            如此翻譯過來,似乎會(huì)讓人認(rèn)為這個(gè) program break 是和上圖中矛盾的,上圖中的 program break 是在堆的增長方向的第一個(gè)位置處(堆和棧的增長方向是相對的),而按照說明手冊來理解,似乎是在 bss segment 結(jié)束那里(因?yàn)槲闯跏蓟瘮?shù)據(jù)段一般認(rèn)為是 bss segment)。


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

            #include <stdio.h>
            #include <unistd.h>
            #include <stdlib.h>
            #include <sys/time.h>
            #include <sys/resource.h>
             
             
            int bssvar;    //聲明一個(gè)味定義的變量,它會(huì)放在 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);          //從堆中分配一塊內(nèi)存區(qū),一般從堆的開始處獲取
                if (pmem == NULL) {
                    perror("malloc");
                    exit (EXIT_FAILURE);
                }
             
             
                printf ("pmem:%p\n", pmem);
             
             
            //計(jì)算堆的開始地址和 bss segment 結(jié)束處得空隙大小,注意每次加載程序時(shí)這個(gè)空隙都是變化的,但是在同一次加載中它不會(huì)改變
                heap_gap_bss = (long)pmem - (long)&bssvar - 4;          
                printf ("1-gap between heap and bss:%lu\n", heap_gap_bss);
             
             
                free (pmem);   //釋放內(nèi)存,歸還給堆
                 
                sbrk(32);        //調(diào)整 program break 位置(假設(shè)現(xiàn)在不知道這個(gè)位置在堆頭還是堆尾)
                 pmem = (char *)malloc(32);   //再一次獲取內(nèi)存區(qū)
                    if (pmem == NULL) {
                            perror("malloc");
                            exit (EXIT_FAILURE);
                    }
             
             
                    printf ("pmem:%p\n", pmem);   //檢查和第一次獲取的內(nèi)存區(qū)的起始地址是否一樣
                heap_gap_bss = (long)pmem - (long)&bssvar - 4;  //計(jì)算調(diào)整 program break 后的空隙
                printf ("2-gap between heap and bss:%lu\n", heap_gap_bss);
             
             
                free(pmem);   //釋放
                return 0;
            }
            下面,我們分別運(yùn)行兩次程序,并查看其輸出:


            引用
            [beyes@localhost C]$ ./sbrk 
            end of bss section:0x8049938
            pmem:0x82ec008
            1-gap between heap and bss:2762448
            pmem:0x82ec008
            2-gap between heap and bss:2762448
            [beyes@localhost C]$ ./sbrk 
            end of bss section:0x8049938
            pmem:0x8dbc008
            1-gap between heap and bss:14100176
            pmem:0x8dbc008
            2-gap between heap and bss:14100176
            從上面的輸出中,可以發(fā)現(xiàn)幾點(diǎn):
            1. bss 段一旦在在程序編譯好后,它的地址就已經(jīng)規(guī)定下來。
            2. 一般及簡單的情況下,使用 malloc() 申請的內(nèi)存,釋放后,仍然歸還回原處,再次申請同樣大小的內(nèi)存區(qū)時(shí),還是從第 1 次那里獲得。
            3. bss segment 結(jié)束處和堆的開始處的空隙大小,并不因?yàn)?sbrk() 的調(diào)整而改變,也就是說明了 program break 不是調(diào)整堆頭部。

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

            當(dāng) sbrk() 中的參數(shù)為 0 時(shí),我們可以找到 program break 的位置。那么根據(jù)這一點(diǎn),檢查一下每次在程序加載時(shí),系統(tǒng)給堆的分配是不是等同大小的:
            #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;
            }
            運(yùn)行上面的程序 3 次:

            引用
            [beyes@localhost C]$ ./sbrk 
            pmem:0x80c9008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x9682008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x9a7d008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x8d92008
            heap size on each load: 135160
            [beyes@localhost C]$ vi sbrk.c
            從輸出可以看到,雖然堆的頭部地址在每次程序加載后都不一樣,但是每次加載后,堆的大小默認(rèn)分配是一致的。但是這不是不能改的,可以使用 sysctl 命令修改一下內(nèi)核參數(shù):
            引用
            #sysctl -w kernel/randomize_va_space=0
            這么做之后,再運(yùn)行 3 次這個(gè)程序看看:
            引用
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            [beyes@localhost C]$ ./sbrk 
            pmem:0x804a008
            heap size on each load: 135160
            從輸出看到,每次加載后,堆頭部的其實(shí)地址都一樣了。但我們不需要這么做,每次堆都一樣,容易帶來緩沖區(qū)溢出攻擊(以前老的 linux 內(nèi)核就是特定地址加載的),所以還是需要保持 randomize_va_space 這個(gè)內(nèi)核變量值為 1 。

            下面就來驗(yàn)證 sbrk() 改變的 program break 位置在堆的增長方向處:
            #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 就是堆預(yù)分配的固定大小;改變后要用 sbrk(0) 再次獲取更新后的program break位置
                    }
                   free(pmem);
             
                    
                   return 0;
            }
            運(yùn)行輸出:

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

            ... ...
            61
            62
            63
            64

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

            而 brk() 這個(gè)函數(shù)的參數(shù)是一個(gè)地址,假如你已經(jīng)知道了堆的起始地址,還有堆的大小,那么你就可以據(jù)此修改 brk() 中的地址參數(shù)已達(dá)到調(diào)整堆的目的。

            實(shí)際上,在應(yīng)用程序中,基本不直接使用這兩個(gè)函數(shù),取而代之的是 malloc() 一類函數(shù),這一類庫函數(shù)的執(zhí)行效率會(huì)更高。 還需要注意一點(diǎn),當(dāng)使用 malloc() 分配過大的空間,比如超出 0x20ff8 這個(gè)常數(shù)(在我的系統(tǒng)(Fedora15)上是這樣,別的系統(tǒng)可能會(huì)有變)時(shí),malloc 不再從堆中分配空間,而是使用 mmap() 這個(gè)系統(tǒng)調(diào)用從映射區(qū)尋找可用的內(nèi)存空間。


            brk/sbrk我覺得:一個(gè)程序可尋址的邏輯地址范圍是3G(本來是4G,有1G是內(nèi)核空間不能用),但是這只是邏輯地址,并不可能全部給你用,所以就把預(yù)定約定可以給你用的那部分空間(code\data\bss\heap\stack)映射成了物理地址。但是預(yù)先約定的總有不夠用的時(shí)候,此時(shí)可以通過brk/sbrk來增加邏輯地址到物理地址映射的范圍,即擴(kuò)大該程序堆空間的大小。

            久久无码国产专区精品| 久久精品国产秦先生| 日本久久中文字幕| 亚洲国产成人久久精品99| 亚洲人成网亚洲欧洲无码久久 | 色欲久久久天天天综合网精品| 日韩乱码人妻无码中文字幕久久| 成人久久综合网| 亚洲国产精品成人久久蜜臀 | 蜜桃麻豆WWW久久囤产精品| 精品乱码久久久久久久| 久久www免费人成看国产片 | 99久久精品国产免看国产一区| 青草影院天堂男人久久| 国产69精品久久久久久人妻精品| 青青国产成人久久91网| 午夜精品久久久久久99热| 久久久WWW成人免费精品| 精品熟女少妇av免费久久| 国产色综合久久无码有码| 久久久久婷婷| 国产香蕉97碰碰久久人人| 久久无码人妻一区二区三区午夜| 色99久久久久高潮综合影院| 青青草原综合久久| 国产精品久久波多野结衣| 亚洲精品国产字幕久久不卡| 久久久久国产一级毛片高清板| 国产精品久久久久久福利69堂| 国产偷久久久精品专区| 久久精品国产99久久久古代| 欧美精品福利视频一区二区三区久久久精品 | 人妻无码αv中文字幕久久琪琪布| 亚洲精品视频久久久| 欧美大战日韩91综合一区婷婷久久青草 | 久久99精品久久久久久hb无码| 无码人妻少妇久久中文字幕 | 国产精品9999久久久久| 久久午夜伦鲁片免费无码| 国内精品久久久久久99蜜桃| 久久久久久亚洲AV无码专区|