午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

深入解析神奇的vfork 

 心不留意外塵 2016-07-07
http://blog./uid-27164517-id-3308726.html
2012
一段神奇的代碼

在論壇里看到下面一段代碼:
int createproc();
int main()
{
pid_t pid=createproc();
printf("%d\n", pid);
exit(0);
}
int createproc()
{
pid_t pid;
if(!(pid=vfork())) {
printf("child proc:%d\n", pid);
return pid;
}
else return -1;
}
輸出結(jié)果:
child proc:0
0
child proc:0
Killed

感覺非常奇怪,為什么vfork以后,父子進(jìn)程都走了“子進(jìn)程”的分支呢?

什么是vfork?

“寫時(shí)復(fù)制”其實(shí)還是有復(fù)制,進(jìn)程的mm結(jié)構(gòu)、頁表都還是被復(fù)制了(“寫時(shí)復(fù)制”也必須由這些信息來支撐。否則內(nèi)核捕捉到CPU訪存異常,怎么區(qū)分這是“寫時(shí)復(fù)制”引起的,還是真正的越權(quán)訪問呢?)。
而 vfork就把事情做絕了,所有有關(guān)于內(nèi)存的東西都不復(fù)制了,父子進(jìn)程的內(nèi)存是完全共享的。但是這樣一來又有問題了,雖然用戶程序可以設(shè)計(jì)很多方法來避免 父子進(jìn)程間的訪存沖突。但是關(guān)鍵的一點(diǎn),父子進(jìn)程共用著棧,這可不由用戶程序控制的。一個(gè)進(jìn)程進(jìn)行了關(guān)于函數(shù)調(diào)用或返回的操作,則另一個(gè)進(jìn)程的調(diào)用棧(實(shí) 際上就是同一個(gè)棧)也被影響了。這樣的程序沒法運(yùn)行下去。

所以,vfork有個(gè)限制,子進(jìn)程生成后,父進(jìn)程在vfork中被內(nèi)核掛起,直 到子進(jìn)程有了自己的內(nèi)存空間(exec**)或退出(_exit)。并且,在此之前,子進(jìn)程不能從調(diào)用vfork的函數(shù)中返回(同時(shí),不能修改棧上變量、 不能繼續(xù)調(diào)用除_exit或exec系列之外的函數(shù),否則父進(jìn)程的數(shù)據(jù)可能被改寫)。

問題的思考

說到這里,可以看出文章開頭的那段代碼是存在問題的了。子進(jìn)程不但調(diào)用了printf,還從createproc函數(shù)中返回了。
但是,子進(jìn)程的違規(guī)為什么會(huì)使父進(jìn)程走上“child proc”這條路呢?父進(jìn)程在子進(jìn)程退出前被阻塞在vfork里面,vfork的返回值是如何變成0的呢?

前面一直在說vfork,其實(shí)它是兩個(gè)東西,庫(libc)函數(shù)vfork和系統(tǒng)調(diào)用vfork。用戶程序調(diào)用的是庫函數(shù),而庫函數(shù)再去調(diào)用系統(tǒng)調(diào)用。用戶 程序中幾乎所有的系統(tǒng)調(diào)用都是通過庫函數(shù)去調(diào)用的。因?yàn)椴煌w系結(jié)構(gòu)下(甚至相同體系結(jié)構(gòu)),系統(tǒng)調(diào)用的指令和參數(shù)傳遞規(guī)則都可能不同,這些細(xì)節(jié)被庫函數(shù) 隱藏了。

前面提到,父進(jìn)程被掛起在vfork中,這是指的系統(tǒng)調(diào)用vfork。在系統(tǒng)調(diào)用中,進(jìn)程使用的是內(nèi)核棧(每個(gè)進(jìn)程有著自己獨(dú)有 的內(nèi)核棧)。此時(shí),父進(jìn)程在內(nèi)核里面是安全的,隨便子進(jìn)程怎么違規(guī)。內(nèi)核會(huì)保證系統(tǒng)調(diào)用vfork的完整性,系統(tǒng)調(diào)用的返回值也不會(huì)有問題(它是通過寄存 器傳回用戶空間的,跟棧無關(guān))。
而vfork的返回值變成0的問題,則是在庫函數(shù)vfork中產(chǎn)生的。既然子進(jìn)程已經(jīng)違規(guī)了,庫函數(shù)沒辦法保證程 序的正確性。而庫函數(shù)vfork是否返回0也是不確定的,可能不同版本的libc、不同的程序上下文、不同的系統(tǒng)、等等、都會(huì)有不同的返回值(或者就直接 “段錯(cuò)誤”了)。還有可能是,父進(jìn)程中庫函數(shù)vfork并沒有返回0,但是棧上的返回地址被改寫了,從函數(shù)createproc返回,返回到 printf("child proc")這句話去了。

再深入一點(diǎn)

vfork后,庫函數(shù)沒法保證子進(jìn)程在進(jìn)行函數(shù)調(diào)用或返回的操作后程序還正常,但是庫函數(shù)vfork本身就是一個(gè)函數(shù)呀,從系統(tǒng)調(diào)用vfork返回后,庫函數(shù)vfork接著又返回了。這時(shí),程序的正確性又是如何保證的呢?

關(guān)于函數(shù)調(diào)用,一般而言:調(diào)用前-調(diào)用者將需要傳遞的參數(shù)放到棧上;調(diào)用時(shí)-調(diào)用者使用call指令,該指令自動(dòng)將返回地址入棧;調(diào)用后,在被調(diào)用的函數(shù)中,第一件事是做調(diào)用棧的調(diào)整,如createproc函數(shù)如是做:
08048487 :
8048487:       55                      push   %ebp
8048488:       89 e5                 mov    %esp,%ebp
804848a:       83 ec 28            sub    $0x28,%esp
......
其中ESP是當(dāng)前棧的指針,而EBP是上一層調(diào)用棧的指針。調(diào)用棧調(diào)整之前,EBP保存著上上一層棧的指針,這個(gè)值不能丟,需要放在棧上,以便函數(shù)返回時(shí)恢復(fù)。

每層調(diào)用都有自己的調(diào)用棧,“深”的調(diào)用不會(huì)影響到之前的調(diào)用棧。所以,vfork后子進(jìn)程調(diào)用其他函數(shù)應(yīng)該是沒有問題的(但是可能會(huì)改寫掉屬于父進(jìn)程的某些數(shù)據(jù),造成邏輯上的錯(cuò)誤),只要它不從調(diào)用vfork的函數(shù)中返回就行了。
但是,庫函數(shù)vfork本身卻不是這樣做的。在這個(gè)函數(shù)中沒有使用棧上的內(nèi)存空間,它沒有去進(jìn)行調(diào)用棧的切換,如:
000983f0 <__vfork>:
983f0:       59                      pop    %ecx
983f1:       65 8b 15 6c 00 00 00    mov    %gs:0x6c,%edx
983f8:       89 d0                   mov    %edx,%eax
983fa:       f7 d8                   neg    %eax
......
9840e:       cd 80                   int    $0x80
98410:       51                      push   %ecx
......

所以父進(jìn)程在庫函數(shù)中運(yùn)行時(shí),不用擔(dān)心棧上的數(shù)據(jù)已經(jīng)被子進(jìn)程修改(它根本不去使用棧上的數(shù)據(jù))。
然而call/ret指令卻不得不使用棧(因?yàn)榉祷氐刂纷詣?dòng)會(huì)被CPU放在棧上),如果子進(jìn)程在vfork后調(diào)用其他函數(shù),會(huì)使得父進(jìn)程在進(jìn)入庫函數(shù)vfork時(shí)通過call指令在棧上留下的“返回地址”被擦掉。
事 情的確是這樣。于是庫函數(shù)vfork為了解決這個(gè)問題,做了一些手腳,它并沒有讓棧上的“返回地址”一直留在棧上。注意上面的匯編代碼,進(jìn)入庫函數(shù) vfork的第一條指令就是“pop %ecx”,把放在棧上的“返回地址”彈到了ECX中去,保存起來。然后在系統(tǒng)調(diào)用vfork返回后(int 0x80是用于系統(tǒng)調(diào)用的指令),再“push %ecx”,把“返回地址”放回去。                                   

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多