進(jìn)程是資源管理的最小單位统舀,線程是程序執(zhí)行的最小單位。進(jìn)程管理著資源(比如說CPU劳景,主存誉简,文件等),而將線程分配到某個CPU上執(zhí)行盟广。在操作系統(tǒng)設(shè)計上闷串,從進(jìn)程到線程,最主要目的是更好地支持多CPU系統(tǒng)和減小上下文切換的開銷衡蚂。
進(jìn)程的狀態(tài)
系統(tǒng)為了充分利用資源窿克,對進(jìn)程區(qū)分了不同的狀態(tài)骏庸,為“新建毛甲、運(yùn)行、阻塞具被、就緒和完成”:
- 新建:進(jìn)程正在被創(chuàng)建
- 運(yùn)行:進(jìn)程正在運(yùn)行
- 阻塞:進(jìn)程正在等待某一事件發(fā)生
- 就緒:系統(tǒng)正在等待CPU來執(zhí)行命令
- 完成:進(jìn)程已經(jīng)結(jié)束玻募,系統(tǒng)正在回收資源
CPU按時間片分配給各個進(jìn)程使用,每個進(jìn)程都有自己的運(yùn)行環(huán)境以使得在CPU做進(jìn)程切換(上下文切換)時不會“忘記”該進(jìn)程已計算了一部分的“半成品”一姿。從DOS的概念講七咧,進(jìn)程的切換都是一次“DOS中斷”處理過程,包括三個層次 :
1)用戶數(shù)據(jù)的保存:包括正文段(TEXT)叮叹、數(shù)據(jù)段(DATA艾栋,BSS)、棧段(STACK)蛉顽、共享內(nèi)存段(SHARED MEMORY)的保存
2)寄存器數(shù)據(jù)的保存:包括PC(指向下一條指令地址)蝗砾,PSW(處理機(jī)狀態(tài)字),SP(棧指針)携冤,PCBP(進(jìn)程控制塊指針)悼粮,F(xiàn)P(指向棧中一個函數(shù)的local變量的首地址),AP(指向棧中函數(shù)調(diào)用的實參位置)曾棕,ISP(中斷棧指針)以及其他的通用寄存器等
3)系統(tǒng)層次的保存:包括proc扣猫,u,虛擬存儲空間管理表格翘地,中斷處理棧申尤。以便于該進(jìn)程再一次得到CPU時間片時能正常運(yùn)行癌幕。
常用的多進(jìn)程編程的系統(tǒng)調(diào)用
創(chuàng)建一個新的進(jìn)程
#include <unistd.h>
#include <sys/types.h>
pid_t fork();
調(diào)用產(chǎn)生一個新的進(jìn)程,叫“子進(jìn)程”昧穿,是調(diào)用進(jìn)程的一個復(fù)制品序芦。調(diào)用進(jìn)程叫“父進(jìn)程”,子進(jìn)程繼承了父進(jìn)程幾乎所有屬性粤咪。
子進(jìn)程是對父進(jìn)程的一個拷貝谚中。
進(jìn)程:
- 代碼段(程序代碼)
- 堆棧段(局部變量、函數(shù)返回地址寥枝、函數(shù)參數(shù))
- 數(shù)據(jù)段(全局變量宪塔、常數(shù))
在linux中,系統(tǒng)調(diào)用fork后囊拜,內(nèi)核為完成系統(tǒng)調(diào)用fork要進(jìn)行幾步操作:
- 為新進(jìn)程在進(jìn)程表中分配一個表項某筐。系統(tǒng)對一個普通用戶可以同時運(yùn)行的進(jìn)程數(shù)是有限的,對超級用戶沒有該限制冠跷,但是不能超過進(jìn)程表的最大表項的數(shù)目南誊。
- 給子進(jìn)程一個唯一的進(jìn)程標(biāo)識號(PID)。該進(jìn)程標(biāo)識號其實就是該表項在進(jìn)程表中的索引蜜托。
- 復(fù)制一個父進(jìn)程的進(jìn)程表項的副本給子進(jìn)程抄囚。內(nèi)核初始化子進(jìn)程的進(jìn)程表項時,是從父進(jìn)程處拷貝的橄务。所以子進(jìn)程擁有與父進(jìn)程一樣的uid幔托、當(dāng)前目錄、當(dāng)前根蜂挪、用戶文件描述符等重挑。
- 把與父進(jìn)程相連的文件表和索引節(jié)點(diǎn)表的引用數(shù)加1.這些文件自動地與該子進(jìn)程相連。
- 內(nèi)核為子進(jìn)程創(chuàng)建用戶級上下文棠涮。內(nèi)核為子進(jìn)程的代碼段分配內(nèi)存谬哀,并復(fù)制父進(jìn)程的區(qū)內(nèi)容,生成的是進(jìn)程的靜態(tài)部分严肪。
- 生成進(jìn)程的動態(tài)部分史煎,然后對父進(jìn)程返回子進(jìn)程的pid,對子進(jìn)程返回0.
簡單說诬垂,fork()調(diào)用成功后劲室,分別返回兩個整數(shù)——對父進(jìn)程返回>0的整數(shù),對子進(jìn)程返回0.
舉個例子:
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h> // exit()
int main()
{
pid_t pid; // 進(jìn)程號
char *message;
int n;
printf("fork program starting\n");
pid = fork(); // <==> pid_t fork(void) 包含在頭文件<sys/types.h>和<unistd.h>中
switch(pid) // 進(jìn)程會根據(jù)pid不同结窘,有選擇地執(zhí)行各自的代碼
{
case -1:
perror("fork failed");
exit(1);
case 0: // fork函數(shù)返回新進(jìn)程的pid很洋,新進(jìn)程為0(即子進(jìn)程)
message = "This is the childprocess";
n = 5;
break;
default:
message = "This is the parentprocess";
n = 3;
break;
}
for(; n>0; n--){
puts(message);;
sleep(1);
}
exit(0);
}
執(zhí)行效果如下:
下面是這個程序在執(zhí)行過程中的示意圖:
父進(jìn)程和子進(jìn)程的執(zhí)行的代碼都和process1_fork.c中代碼一致,但是fork根據(jù)不同進(jìn)程返回不同的PID隧枫,其實父子進(jìn)程的實際有效代碼部分是不同的:
換句話說就是喉磁,進(jìn)程會根據(jù)PID的不同谓苟,有選擇地執(zhí)行各自的代碼。