12.1進(jìn)程切換
實(shí)際上為用戶(hù)提供的系統(tǒng)調(diào)用服務(wù),用戶(hù)在執(zhí)行它的應(yīng)用的過(guò)程當(dāng)中昔脯,有需求要?jiǎng)?chuàng)建一個(gè)新的進(jìn)程啄糙,如何來(lái)創(chuàng)建一個(gè)新的進(jìn)程,在這里頭運(yùn)行一個(gè)新的程序云稚,那這個(gè)是進(jìn)程加載隧饼,父進(jìn)程創(chuàng)建子進(jìn)程之后,它們倆之間需要有一些協(xié)調(diào)的關(guān)系静陈,比如說(shuō)子進(jìn)程結(jié)束之后燕雁,父進(jìn)程負(fù)責(zé)回收它所占用的資源诞丽,那這個(gè)時(shí)候它們之間有一個(gè)通訊關(guān)系這就是進(jìn)程的等待和退出
進(jìn)程切換
■進(jìn)程切換(上下文切換)
暫停當(dāng)前運(yùn)行進(jìn)程,從運(yùn)行狀態(tài)變成其他狀態(tài)
調(diào)度另一個(gè)進(jìn)程從就緒狀態(tài)變成運(yùn)行狀態(tài)
■進(jìn)程切換的要求
切換前拐格,保存進(jìn)程上下文
切換后僧免,恢復(fù)進(jìn)程上下文
快速切換
注:為了保證系統(tǒng)運(yùn)行的效率,這個(gè)切換的速度必須非衬笞牵快所以通常情況下它都是由匯編來(lái)實(shí)現(xiàn)的
■進(jìn)程生命周期的信息
寄存器(PC, SP,…)
CPU狀態(tài)
內(nèi)存地址空間
精髓:
■進(jìn)程切換的原因:中斷懂衩,陷阱(異常)和系統(tǒng)調(diào)用
中斷:
時(shí)鐘中斷:當(dāng)前進(jìn)程時(shí)間片用完了
I/O中斷:操作系統(tǒng)確定是否發(fā)生了I/O活動(dòng)。
內(nèi)存失效:要訪(fǎng)問(wèn)的內(nèi)存不在主存中金踪,要調(diào)入內(nèi)存浊洞。
進(jìn)程控制塊PCB:內(nèi)核的進(jìn)程狀態(tài)記錄
內(nèi)核為每個(gè)進(jìn)程維護(hù)了對(duì)應(yīng)的進(jìn)程控制塊(PCB)
內(nèi)核將相同狀態(tài)的進(jìn)程的PCB放置在同一隊(duì)列
精髓:
進(jìn)程狀態(tài)的變化
進(jìn)程切換步驟:
1)保存處理器上下文環(huán)境,包括程序計(jì)數(shù)器和其他寄存器热康。
2)更新當(dāng)前處于運(yùn)行態(tài)進(jìn)程的進(jìn)程控制塊沛申,包括將進(jìn)程的狀態(tài)改變到另一狀態(tài)(就緒態(tài)、阻塞態(tài)姐军、就緒/掛起態(tài)或退出態(tài))。還必須更新其他相關(guān)域尖淘,包括離開(kāi)運(yùn)行態(tài)的原因和記賬信息奕锌。
3)將進(jìn)程的進(jìn)程控制塊移到相應(yīng)的隊(duì)列(就緒、在事件i處阻塞村生、就緒/掛起)惊暴。
4)選擇另一個(gè)進(jìn)程執(zhí)行,這方面的內(nèi)容將在本書(shū)的第四部分探討趁桃。
5)更新所選擇進(jìn)程的進(jìn)程控制塊辽话,包括將進(jìn)程的狀態(tài)變?yōu)檫\(yùn)行態(tài)。
6)更新內(nèi)存管理的數(shù)據(jù)結(jié)構(gòu)卫病,這取決于如何管理地址轉(zhuǎn)換油啤。
7)恢復(fù)處理器在被選擇的進(jìn)程最近一次切換出運(yùn)行狀態(tài)時(shí)的上下文環(huán)境,這可以通過(guò)載入程序計(jì)數(shù)器和其他寄存器以前的值來(lái)實(shí)現(xiàn)蟀苛。
12.2進(jìn)程創(chuàng)建
創(chuàng)建新進(jìn)程
■Windows進(jìn)程創(chuàng)建API:CreateProcess(filename)
創(chuàng)建時(shí)關(guān)閉所有在子進(jìn)程里的文件描述符CreateProcess(filename, CLOSE_FD)
創(chuàng)建時(shí)改變子進(jìn)程的環(huán)境CreateProcess(filename, CLOSE_FD, new_envp)
■Unix進(jìn)程創(chuàng)建系統(tǒng)調(diào)用:fork/exec
·fork()把一個(gè)進(jìn)程復(fù)制成二個(gè)進(jìn)程
parent (old PID), child (new PID)
·exec()用新程序來(lái)重寫(xiě)當(dāng)前進(jìn)程
PID沒(méi)有改變
■用fork和exec創(chuàng)建進(jìn)程的示例
int pid = fork()益咬;//創(chuàng)建子進(jìn)程
if(pid == 0) {//子進(jìn)程在這里繼續(xù)
// Do anything (unmap memory, close net connections…)
exec(“program”, argc, argv0, argv1,…);
}
■fork()創(chuàng)建一個(gè)繼承的子進(jìn)程
復(fù)制父進(jìn)程的所有變量和內(nèi)存
復(fù)制父進(jìn)程的所有CPU寄存器(有一個(gè)寄存器例外)
■fork()的返回值
子進(jìn)程的fork()返回0
父進(jìn)程的fork()返回子進(jìn)程標(biāo)識(shí)符
fork()返回值可方便后續(xù)使用,子進(jìn)程可使用getpid()獲取PID
fork()的地址空間復(fù)制
■fork()執(zhí)行過(guò)程對(duì)于子進(jìn)程而言帜平,是在調(diào)用時(shí)間對(duì)父進(jìn)程地址空間的一次復(fù)制
對(duì)于父進(jìn)程fork()返回child PID,對(duì)于子進(jìn)程返回值為0
程序加載和執(zhí)行
系統(tǒng)調(diào)用exec( )加載新程序取代當(dāng)前運(yùn)行進(jìn)程
exec()示例代碼
main()
…
int pid = fork();//創(chuàng)建子進(jìn)程
if (pid == 0) {//子進(jìn)程在這里繼續(xù)
exec_status = exec(“calc”, argc, argv0, argv1,…);
printf(“Why would I execute?”);
} ?else {//父進(jìn)程在這里繼續(xù)
printf(“Whose your daddy?”);
…
child_status = wait(pid);
}
fork()使用示例(循環(huán)內(nèi)fork()父子都同時(shí)產(chǎn)生新的子進(jìn)程)
int ?main()
{
pid_t ?pid;
int ?i;
for ?(i=0; ?i
{
/* fork ?another ?process ?*/
pid = fork();
if ?(pid < 0) { /*error ?occurred ?*/
fprintf(stderr,“Fork Failed”);
exit(-1);
}
else if (pid == 0) { /* child process */
fprintf(stdout,“i=%d, ?pid=%d, ?parent ?pid=%d\n”,I,
getpid() ,getppid());
}
}
wait(NULL);
exit(0);
}
Fork()的開(kāi)銷(xiāo)幽告?
■fork()的實(shí)現(xiàn)開(kāi)銷(xiāo)
對(duì)子進(jìn)程分配內(nèi)存
復(fù)制父進(jìn)程的內(nèi)存和CPU寄存器到子進(jìn)程里
開(kāi)銷(xiāo)昂貴!!
■在99%的情況里,我們?cè)谡{(diào)用fork()之后調(diào)用exec()
在fork()操作中內(nèi)存復(fù)制是沒(méi)有作用的
子進(jìn)程將可能關(guān)閉打開(kāi)的文件和連接
為什么不能結(jié)合它們?cè)谝粋€(gè)調(diào)用中?
■vfork()
創(chuàng)建進(jìn)程時(shí)裆甩,不再創(chuàng)建一個(gè)同樣的內(nèi)存映像
一些時(shí)候稱(chēng)為輕量級(jí)fork()
子進(jìn)程應(yīng)該幾乎立即調(diào)用exec()
現(xiàn)在使用Copy on Write ?(COW)技術(shù)
■父進(jìn)程終止其子進(jìn)程的原因有很多
子進(jìn)程使用了超過(guò)它所分配到的一些資源冗锁。(為判定是否發(fā)生這種情況,要求父進(jìn)程有一個(gè)檢查其子進(jìn)程狀態(tài)的機(jī)制嗤栓。)
分配給子進(jìn)程的任務(wù)已不再需要
父進(jìn)程退出冻河,如果父進(jìn)程終止,那么操作系統(tǒng)不允許子進(jìn)程繼續(xù)。
精髓:創(chuàng)建一個(gè)新進(jìn)程芋绸,一般步驟:
1)給新進(jìn)程分配一個(gè)唯一的進(jìn)程標(biāo)識(shí)符媒殉。此時(shí),在主進(jìn)程表中增加一個(gè)新表項(xiàng)摔敛,表中的每個(gè)新表項(xiàng)對(duì)應(yīng)著一個(gè)進(jìn)程廷蓉。
2)給進(jìn)程分配空間。這包括進(jìn)程映像中的所有元素马昙。因此桃犬,操作系統(tǒng)必須知道私有用戶(hù)地址空間(程序和數(shù)據(jù))和用戶(hù)棧需要多少空間⌒欣悖可以根據(jù)進(jìn)程的類(lèi)型使用默認(rèn)值攒暇,也可以在作業(yè)創(chuàng)建時(shí)根據(jù)用戶(hù)請(qǐng)求設(shè)置。如果一個(gè)進(jìn)程是由另一個(gè)進(jìn)程生成的子房,則父進(jìn)程可以把所需的值作為進(jìn)程創(chuàng)建請(qǐng)求的一部分傳遞給操作系統(tǒng)形用。如果任何現(xiàn)有的地址空間被這個(gè)新進(jìn)程共享,則必須建立正確的連接证杭。最后田度,必須給進(jìn)程控制塊分配空間。
3)初始化進(jìn)程控制塊解愤。進(jìn)程標(biāo)識(shí)符部分包括進(jìn)程ID和其他相關(guān)的ID镇饺,如父進(jìn)程的ID等;處理器狀態(tài)信息部分的大多數(shù)項(xiàng)目通常初始化成0,除了程序計(jì)數(shù)器(被置為程序人口點(diǎn))和系統(tǒng)棧指針(用來(lái)定義進(jìn)程棧邊界);進(jìn)程控制信息部分的初始化基于標(biāo)準(zhǔn)默認(rèn)值和為該進(jìn)程所請(qǐng)求的屬性送讲。例如奸笤,進(jìn)程狀態(tài)在典型情況下被初始化成就緒或就緒/掛起;除非顯式地請(qǐng)求更高的優(yōu)先級(jí),否則優(yōu)先級(jí)的默認(rèn)值為最低優(yōu)先級(jí);除非顯式地請(qǐng)求或從父進(jìn)程處繼承哼鬓,否則進(jìn)程最初不擁有任何資源( I/O設(shè)備监右、文件)。
4)設(shè)置正確的連接魄宏。例如秸侣,如果操作系統(tǒng)把每個(gè)調(diào)度隊(duì)列都保存成鏈表,則新進(jìn)程必須放置在就緒或就緒/掛起鏈表中宠互。
5)創(chuàng)建或擴(kuò)充其他數(shù)據(jù)結(jié)構(gòu)味榛。例如,操作系統(tǒng)可能為每個(gè)進(jìn)程保存著一個(gè)記賬文件予跌,可用于編制賬單和/或進(jìn)行性能評(píng)估搏色。
12.3程序加載和執(zhí)行系統(tǒng)調(diào)用exec()
程序加載和執(zhí)行系統(tǒng)調(diào)用exec()
■允許進(jìn)程“加載”一個(gè)完全不同的程序,并從main開(kāi)始執(zhí)行(即_start)
■允許進(jìn)程加載時(shí)指定啟動(dòng)參數(shù)(argc, argv)
■exec調(diào)用成功時(shí)
它是相同的進(jìn)程…
但是運(yùn)行了不同的程序
■代碼段券册、堆棧和堆(heap)等完全重寫(xiě)
12.4進(jìn)程等待與退出
父進(jìn)程等待子進(jìn)程
■wait()系統(tǒng)調(diào)用用于父進(jìn)程等待子進(jìn)程的結(jié)束
子進(jìn)程結(jié)束時(shí)通過(guò)exit()向父進(jìn)程返回一個(gè)值
父進(jìn)程通過(guò)wait()接受并處理返回值
■wait()系統(tǒng)調(diào)用的功能
有子進(jìn)程存活時(shí)频轿,父進(jìn)程進(jìn)入等待狀態(tài)垂涯,等待子進(jìn)程的返回結(jié)果
當(dāng)某子進(jìn)程調(diào)用exit()時(shí),喚醒父進(jìn)程,將exit()返回值作為父進(jìn)程中wait的返回值
有僵尸子進(jìn)程等待時(shí)航邢,wait()立即返回其中一個(gè)值
無(wú)子進(jìn)程存活時(shí)耕赘,wait()立刻返回
進(jìn)程的有序終止exit()
■進(jìn)程結(jié)束執(zhí)行時(shí)調(diào)用exit(),完成進(jìn)程資源回收
■exit()系統(tǒng)調(diào)用的功能
將調(diào)用參數(shù)作為進(jìn)程的“結(jié)果”
關(guān)閉所有打開(kāi)的文件等占用資源
釋放內(nèi)存
釋放大部分進(jìn)程相關(guān)的內(nèi)核數(shù)據(jù)結(jié)構(gòu)
檢查是否父進(jìn)程是存活著的
如存活膳殷,保留結(jié)果的值直到父進(jìn)程需要它操骡,進(jìn)入僵尸(zombie/defunct)狀態(tài),等待父進(jìn)程處理
如果沒(méi)有赚窃,它釋放所有的數(shù)據(jù)結(jié)構(gòu)册招,進(jìn)程結(jié)果(孤兒進(jìn)程)
清理所有等待的僵尸進(jìn)程
■進(jìn)程終止是最終的垃圾收集(資源回收)
其他進(jìn)程控制系統(tǒng)調(diào)用
■優(yōu)先級(jí)控制
nice()指定進(jìn)程的初始優(yōu)先級(jí)
Unix系統(tǒng)中進(jìn)程優(yōu)先級(jí)會(huì)隨執(zhí)行時(shí)間而衰減
■進(jìn)程調(diào)試支持
ptrace()允許一個(gè)進(jìn)程控制另一個(gè)進(jìn)程的執(zhí)行
設(shè)置斷點(diǎn)和查看寄存器等
■定時(shí)
sleep()可以讓進(jìn)程在定時(shí)器的等待隊(duì)列中等待指定
進(jìn)程控制v.s.進(jìn)程狀態(tài)