- 論壇徽章:
- 0
|
前幾天問(wèn)了關(guān)于shell里"."和":"的問(wèn)題(http://72891.cn/viewthr ... =bzw2000&page=1), 在版主的推薦下, 看了網(wǎng)中人前輩的http://72891.cn/viewthr ... ;page=6#pid1583329. 感覺(jué)對(duì)進(jìn)程的理解不夠, 這兩天在網(wǎng)上查了點(diǎn)資料, 看了點(diǎn)書(shū), 現(xiàn)在總結(jié)一下(當(dāng)然也有新的問(wèn)題):
因?yàn)閟hell本身是一個(gè)進(jìn)程, 所以我們這里把shell進(jìn)程抽象為進(jìn)程1(P1).
先來(lái)說(shuō)一下一個(gè)進(jìn)程本身在系統(tǒng)中的上下文環(huán)境. 一個(gè)進(jìn)程通常包括:
* CPU寄存器組
* GDT表中的四項(xiàng): OS代碼段描述符, OS數(shù)據(jù)段描述符(指向OS代碼段和數(shù)據(jù)段, 所有進(jìn)程共享), 本進(jìn)程的ldt描述符, 本進(jìn)程的tss描述符(指向后面提到的PCB中的ldt段和tss段)
* OS代碼段, OS數(shù)據(jù)段: 用戶進(jìn)程可通過(guò)系統(tǒng)調(diào)用int 0x80訪問(wèn)這些代碼和數(shù)據(jù)
* 進(jìn)程控制塊(即PCB): 大小通常是一頁(yè)內(nèi)存, 在內(nèi)核模塊的kernel代碼塊中, 包含了所有本進(jìn)程需要的信息. 其中包括進(jìn)程號(hào), 各進(jìn)程時(shí)間, ldt段(內(nèi)容有指向本進(jìn)程私有代碼段和數(shù)據(jù)段的描述符), tss段(本進(jìn)程的上下文, 進(jìn)程切換時(shí)有用), 打開(kāi)文件信息等.
* 內(nèi)核棧: 與PCB在同一頁(yè)內(nèi)存中, 故大小為(一頁(yè)內(nèi)存-PCB的大小).
* 私有代碼段, 數(shù)據(jù)段: 進(jìn)程本身的代碼和數(shù)據(jù)
* 頁(yè)目錄(由CR3指定, 所有進(jìn)程共享)和頁(yè)表
再說(shuō)fork/exec的區(qū)別:
在shell里執(zhí)行"1.sh fork", 相當(dāng)于在P1里fork一個(gè)新進(jìn)程P2, 然后在P2里exec 2.sh. 具體為:
* P1通過(guò)系統(tǒng)調(diào)用int 0x80創(chuàng)建P2(功能號(hào)為_(kāi)_NR_fork, 即fork). fork的過(guò)程從本質(zhì)上說(shuō)就是構(gòu)建新進(jìn)程本身的GDT項(xiàng)(2項(xiàng)), PCB和頁(yè)表三項(xiàng). 基本過(guò)程如下:
{
struct task_struct *p; //p即為新進(jìn)程P2的PCB
/* 1. 先復(fù)制P1的PCB, 然后進(jìn)行修改*/
p <= get_free_page(); //在主內(nèi)存申請(qǐng)一頁(yè)空閑內(nèi)存, 用來(lái)存放PCB
*p = *current; //current指向P1的PCB
p->pid <= P2進(jìn)程的進(jìn)程號(hào);
設(shè)置 p->tss; //不知道除了進(jìn)程切換會(huì)用到tss, 還有什么情況會(huì)用到??
設(shè)置 p其他一些成員(比如進(jìn)程時(shí)間, 優(yōu)先級(jí), 時(shí)間滴答數(shù)等), 具體可以參考fork.c里的copy_process;
設(shè)置 p->ldt, 使之指向P2的私有代碼段, 數(shù)據(jù)段;
/* 2. 復(fù)制頁(yè)表 (參考memory.c中相關(guān)程序)*/
copy_page_tables(); // 此函數(shù)的主要作用就是為進(jìn)程2復(fù)制進(jìn)程1的頁(yè)目錄項(xiàng), 申請(qǐng)新頁(yè)表,
//使新頁(yè)表項(xiàng)指向父進(jìn)程的空間(因?yàn)閘inux采用了寫(xiě)時(shí)復(fù)制機(jī)制).
// 此和前面說(shuō)的p->ldt一起使用, 就實(shí)現(xiàn)了新進(jìn)程2的地址映射:
// p->ldt指明了P2的線性地址空間范圍, 而頁(yè)表又把此線性地址范圍
// 映射到P1的物理地址范圍內(nèi), 直到P1或P2對(duì)此物理地址范圍有寫(xiě)入
//操作, 才會(huì)使P2映射到新的物理地址, 否則P1和P2就共享同一塊物理地址
//空間.
/* 3. 設(shè)置GDT表中新進(jìn)程P2的ldt,tss描述符: 使它們指向PCB中l(wèi)dt和tss*/
set_tss_desc();
set_ldt_desc();
}
* 然后P2也通過(guò)系統(tǒng)調(diào)用int 0x80加載P2的代碼(功能號(hào)為_(kāi)_NR_execve, 即exec). 主要作用是加載新可執(zhí)行程序, 覆蓋原來(lái)的代碼和數(shù)據(jù). 具體為:
/* 以下具體代碼可參考exec.c中do_execve()函數(shù) */
{
讀取新進(jìn)程執(zhí)行文件第一塊數(shù)據(jù)(即頭結(jié)構(gòu). 不過(guò)怎么讀取的我也沒(méi)看懂 ), 根據(jù)頭結(jié)構(gòu)信息判斷是shell腳本文件還是二進(jìn)制可執(zhí)行文件;
如果是可執(zhí)行文件(ELF), 則:
1. 把新進(jìn)程的環(huán)境變量(envp)和參數(shù)(argv)放入一組臨時(shí)物理頁(yè)面中(環(huán)境參數(shù)空間), 并把這些頁(yè)面的物理地址對(duì)應(yīng)地填入一個(gè)數(shù)組page[]中.
這就相當(dāng)于頁(yè)表與線性地址的關(guān)系. 每個(gè)page[]項(xiàng)包含物理頁(yè)面的物理地址, 對(duì)應(yīng)關(guān)系即為頁(yè)面的線性地址對(duì)page的索引.
2. 接下來(lái)是把和原來(lái)用戶代碼段和數(shù)據(jù)段相關(guān)的頁(yè)表、頁(yè)目錄項(xiàng)清0, 即釋放原來(lái)用戶的代碼和數(shù)據(jù)段. 這樣, 當(dāng)前進(jìn)程P2就只有PCB所占的那頁(yè)物理內(nèi)存了.
這里再?gòu)?qiáng)調(diào)一下是釋放原來(lái)"用戶"的私有代碼和私有數(shù)據(jù), 并沒(méi)有釋放OS代碼和數(shù)據(jù), 否則程序就執(zhí)行不下去拉.
3. 重新設(shè)置新用戶程序代碼段(ldt[1]),數(shù)據(jù)段(ldt[2])的基址和限長(zhǎng). 還記得嗎, ldt是在PCB中的. 所以這步是修改PCB.
再把page[]中對(duì)應(yīng)的頁(yè)面放到代碼段的最后(大家可以參考ELF格式). 然后修改頁(yè)目錄和頁(yè)表, 使其頁(yè)表項(xiàng)指向這些物理頁(yè)面. 也就是說(shuō),
現(xiàn)在新進(jìn)程用戶空間只有參數(shù)環(huán)境空間這些物理頁(yè)面.
4. 通過(guò)分配新的物理頁(yè)面創(chuàng)建用戶堆棧, 把以上這些環(huán)境參數(shù)項(xiàng)壓入堆棧. 現(xiàn)在進(jìn)程用戶空間有參數(shù)環(huán)境空間這些物理頁(yè)面, 和堆棧的物理頁(yè)面.
其他部分(如代碼,數(shù)據(jù)等)都沒(méi)有物理頁(yè)面。
5. 修改PCB中current的一些字段, 并修改內(nèi)核棧原來(lái)的eip和esp, 使得eip指向可執(zhí)行文件的入口,esp指向新堆棧頂端. 注意到這里為進(jìn)程還是沒(méi)為新程序的
代碼和數(shù)據(jù)分配物理頁(yè)面. 這只有當(dāng)執(zhí)行到新代碼時(shí), 發(fā)現(xiàn)缺頁(yè)異常, 才會(huì)分配.
如果是shell腳本, 則:
過(guò)程和以上差不多, 就是要逐條解析腳本里程序名.
}
在bash(進(jìn)程)里執(zhí)行"1.sh exec", 相當(dāng)于在P1里exec 2.sh. 即上面所說(shuō)的第二步.
問(wèn)題來(lái)了, bash執(zhí)行完1.sh exec, bash的代碼應(yīng)該被替代為1.sh的代碼, 即應(yīng)該已經(jīng)沒(méi)有bash進(jìn)程了. 但為什么bash進(jìn)程執(zhí)行完1.sh后, 還能繼續(xù)作為bash shell運(yùn)行? 它是怎么從1.sh恢復(fù)為bash的? 是不是我前面理解錯(cuò)了啊?
還有就是":"也告訴我一下是什么命令吧, 查不到
[ 本帖最后由 bzw2000 于 2007-9-4 17:50 編輯 ] |
|