main函數(shù)執(zhí)行前后 《程序員的自我修養(yǎng)》·筆記

【前言】main函數(shù)執(zhí)行前后的宏觀過(guò)程(C++)

  • linux系統(tǒng)下壓板程序的入口是"_start"浙巫,這個(gè)函數(shù)是linux系統(tǒng)庫(kù)(Glibc)的一部分,當(dāng)我們的程序和Glibc鏈接在一起形成最終的可執(zhí)行文件的之后荤懂,這個(gè)函數(shù)就是程序執(zhí)行初始化的入口函數(shù)。
  • 程序初始化部分完成一系列初始化過(guò)程之后喝峦,會(huì)調(diào)用main函數(shù)來(lái)執(zhí)行程序的主體势誊。在main函數(shù)執(zhí)行完成以后,再返回到初始化部分谣蠢,進(jìn)行一些清理工作粟耻,然后結(jié)束進(jìn)程查近。
  • 對(duì)C++而言:(ELF文件為其定義了兩個(gè)特殊的段)
    • .init 該段保存的是可執(zhí)行的命令,它構(gòu)成了進(jìn)程的初始化代碼挤忙。因此霜威,當(dāng)一個(gè)程序開始運(yùn)行的時(shí)候,在main函數(shù)被調(diào)用之前册烈,Glibc的初始化部分安排執(zhí)行這個(gè)段中的代碼
    • .fini 該段保存著進(jìn)程終止命令代碼戈泼。因此,當(dāng)一個(gè)程序的main函數(shù)正常退出的時(shí)候赏僧,Glibc會(huì)安排執(zhí)行這個(gè)段中的代碼大猛。
  • 這兩個(gè)段的存在有特別的目的,如果一個(gè)函數(shù)放到.init段淀零,在mai函數(shù)執(zhí)行前系統(tǒng)就會(huì)執(zhí)行它(就是因?yàn)樗谶@個(gè)段)挽绩。同理,如果一個(gè)函數(shù)放到.fini段驾中,在main函數(shù)返回后該函數(shù)就會(huì)被執(zhí)行唉堪。利用這兩個(gè)特性,C++實(shí)現(xiàn)了全局構(gòu)造和析構(gòu)函數(shù)肩民。

一個(gè)典型程序的大致運(yùn)行步驟

  • 操作系統(tǒng)創(chuàng)建進(jìn)程后唠亚,把控制權(quán)交到了程序入口,這個(gè)入口往往是程序運(yùn)行庫(kù)中的某個(gè)入口函數(shù)持痰。
  • 入口函數(shù)對(duì)運(yùn)行庫(kù)和程序運(yùn)行環(huán)境進(jìn)行初始化灶搜,包括堆、I/O共啃、線程占调、全局變量的構(gòu)造等等。
  • 入口函數(shù)在完成初始化之后移剪,調(diào)用main函數(shù),正式開始執(zhí)行函數(shù)主體部分薪者。
  • main函數(shù)執(zhí)行完畢之后纵苛,返回到入口函數(shù),入口函數(shù)進(jìn)行清理工作言津,包括全局變量析構(gòu)攻人、堆銷毀、關(guān)閉I/O等悬槽,然后進(jìn)行系統(tǒng)調(diào)用結(jié)束進(jìn)程怀吻。

入口函數(shù)的實(shí)現(xiàn)

  • Glibc的入口函數(shù)
    • _start函數(shù)
      ??該入口是由ld鏈接器默認(rèn)的鏈接腳本指定的,當(dāng)然用戶也可以通過(guò)參數(shù)進(jìn)行設(shè)定初婆。_start由匯編代碼實(shí)現(xiàn)蓬坡。大致用如下偽代碼表示:
void _start()
{
  %ebp = 0;
  int argc = pop from stack
  char ** argv = top of stack;
  __libc_start_main(main, argc, argv, __libc_csu_init, __linc_csu_fini,
  edx, top of stack);
}
具體過(guò)程可以參見下圖:


??在調(diào)用_start之前猿棉,裝載器就會(huì)將用戶的參數(shù)和環(huán)境變量壓入棧中,如圖所示屑咳,棧頂元素是argc萨赁,接著就是argv和環(huán)境變量的數(shù)組。
??其中argv除了指向參數(shù)表外兆龙,還隱含緊接著環(huán)境變量表杖爽。這個(gè)環(huán)境變量表要在__libc_start_main里從argv內(nèi)提取出來(lái)
??實(shí)際執(zhí)行代碼的是__libc_start_main紫皇。

- __libc_start_main函數(shù)
    - 函數(shù)頭
```
int __libc_start_main(
        int (*main)(int, char **, char *),
        char * __unbounded *__unbounded ubp_av,
        __typeof(main) init,
        void (*fini)(void),
        void (*rtld_fini)(void),
        viud *__unbounded stack_end)
    ??可以啊看出慰安,一共有7個(gè)參數(shù),其中main由第一個(gè)參數(shù)傳入聪铺,緊接著就是argc和argv(這里叫做ubp_av泻帮,應(yīng)為其中還包括了環(huán)境變量表)。此外的3個(gè)函數(shù)指針:
    (1)init:main調(diào)用之前的初始化工作计寇;
    (2)fini:main結(jié)束之后的收尾工作锣杂;
    (3)rtld_fini:和動(dòng)態(tài)加載有關(guān)的收尾工作。
    最后的stack_end標(biāo)明了棧底的位置番宁,即最高的棧地址元莫。
        - \__libc_start_main代碼中的一個(gè)特殊的宏(宏INIT_ARGV_and_ENVIRON)
        宏展開之后如下:
        `char **ubp_rv = &ubp_av[argc+1];`
        `__environ = ubo_ev;`
        `__libc_stack_end = stack_end;`
        ??上述代碼實(shí)際上就是從_start源代碼分析得到的棧布局,重點(diǎn)是讓_environ指針指向緊跟子啊argv數(shù)組后面的環(huán)境變量數(shù)組蝶押。如下圖:
        ![](http://7xl3j2.com1.z0.glb.clouddn.com/cxy-21.png)
        - __libc_start_main代碼中的一系列重要的函數(shù)
        ```
          __pthread_initialize_minimal();
        __cxa_atexit(rtld_fini, NULL, NULL);
        __libc_init_first(argc, argv, __environ);
        __cxa_atexit(fini, NULL, NULL);
        (*init)(argc, argv, __environ);
        - __cxa_atexit函數(shù)是glibc的內(nèi)部函數(shù)踱蠢,等同于atexit,在main之后調(diào)用棋电。
        - 所以可以看出茎截,參數(shù)傳入的fini和rtld_fini均是用于main結(jié)束之后調(diào)用的。在\__libc_start_main末尾赶盔,關(guān)鍵是如下兩行的代碼:
        `result = main(argc, argv, _environ);`
        `exit(result);`
        main函數(shù)最終被調(diào)用企锌,并退出。
        【補(bǔ)充】程序正常結(jié)束有兩種情況:main函數(shù)正常返回于未;程序中exit()退出撕攒。但是在\__libc_start_main中可以看出,即使main正常返回了烘浦,exit還是會(huì)被調(diào)用抖坪。所以說(shuō)exit()是程序退出的必經(jīng)之路。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末闷叉,一起剝皮案震驚了整個(gè)濱河市擦俐,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌握侧,老刑警劉巖蚯瞧,帶你破解...
    沈念sama閱讀 222,000評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘿期,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡状知,警方通過(guò)查閱死者的電腦和手機(jī)秽五,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,745評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)饥悴,“玉大人坦喘,你說(shuō)我怎么就攤上這事∥魃瑁” “怎么了瓣铣?”我有些...
    開封第一講書人閱讀 168,561評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)贷揽。 經(jīng)常有香客問(wèn)我棠笑,道長(zhǎng),這世上最難降的妖魔是什么禽绪? 我笑而不...
    開封第一講書人閱讀 59,782評(píng)論 1 298
  • 正文 為了忘掉前任蓖救,我火速辦了婚禮,結(jié)果婚禮上印屁,老公的妹妹穿的比我還像新娘循捺。我一直安慰自己,他們只是感情好雄人,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,798評(píng)論 6 397
  • 文/花漫 我一把揭開白布从橘。 她就那樣靜靜地躺著,像睡著了一般础钠。 火紅的嫁衣襯著肌膚如雪恰力。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,394評(píng)論 1 310
  • 那天旗吁,我揣著相機(jī)與錄音踩萎,去河邊找鬼。 笑死阵漏,一個(gè)胖子當(dāng)著我的面吹牛驻民,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播履怯,決...
    沈念sama閱讀 40,952評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼裆泳!你這毒婦竟也來(lái)了叹洲?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,852評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤工禾,失蹤者是張志新(化名)和其女友劉穎运提,沒想到半個(gè)月后蝗柔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,409評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡民泵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,483評(píng)論 3 341
  • 正文 我和宋清朗相戀三年癣丧,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栈妆。...
    茶點(diǎn)故事閱讀 40,615評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡胁编,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出鳞尔,到底是詐尸還是另有隱情嬉橙,我是刑警寧澤,帶...
    沈念sama閱讀 36,303評(píng)論 5 350
  • 正文 年R本政府宣布寥假,位于F島的核電站市框,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏糕韧。R本人自食惡果不足惜枫振,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,979評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望萤彩。 院中可真熱鬧粪滤,春花似錦、人聲如沸乒疏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,470評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)怕吴。三九已至窍侧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間转绷,已是汗流浹背伟件。 一陣腳步聲響...
    開封第一講書人閱讀 33,571評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留议经,地道東北人斧账。 一個(gè)月前我還...
    沈念sama閱讀 49,041評(píng)論 3 377
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像煞肾,于是被迫代替她去往敵國(guó)和親咧织。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,630評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容