?? 多進(jìn)程編程的相關(guān)知識(shí)總結(jié)(一).txt
字號(hào):
一.多進(jìn)程程序的特點(diǎn)
由于UNIX系統(tǒng)是分時(shí)多用戶系統(tǒng), CPU按時(shí)間片分配給各個(gè)用戶使用, 而在實(shí)質(zhì)上
應(yīng)該
說(shuō)CPU按時(shí)間片分配給各個(gè)進(jìn)程使用, 每個(gè)進(jìn)程都有自己的運(yùn)行環(huán)境以使得在CPU
做進(jìn)程
切換時(shí)不會(huì)"忘記"該進(jìn)程已計(jì)算了一半的"半成品". 以DOS的概念來(lái)說(shuō), 進(jìn)程的切
換都
是一次"DOS中斷"處理過(guò)程, 包括三個(gè)層次:
(1)用戶數(shù)據(jù)的保存: 包括正文段(TEXT), 數(shù)據(jù)段(DATA,BSS), 棧段(STACK)
, 共享
內(nèi)存段(SHARED MEMORY)的保存.
(2)寄存器數(shù)據(jù)的保存: 包括PC(program counter,指向下一條要執(zhí)行的
指
令的地址), PSW(processor status word,處理機(jī)狀態(tài)字), SP(stack pointer,棧
指
針), PCBP(pointer of process control block,進(jìn)程控制塊指針), FP(frame
pointer,指向棧中一個(gè)函數(shù)的local變量的首地址), AP(augument pointer,指向
棧中函
數(shù)調(diào)用的實(shí)參位置), ISP(interrupt stack pointer,中斷棧指針), 以及其他的
通用寄
存器等.
(3)系統(tǒng)層次的保存: 包括proc,u,虛擬存儲(chǔ)空間管理表格,中斷處理?xiàng)?
以便于
該進(jìn)程再一次得到CPU時(shí)間片時(shí)能正常運(yùn)行下去.既然系統(tǒng)已經(jīng)處理好所有這些中
斷處理
的過(guò)程, 我們做程序還有什么要擔(dān)心的呢? 我們盡可以使用系統(tǒng)提供的多進(jìn)程的
特點(diǎn),
讓幾個(gè)程序精誠(chéng)合作, 簡(jiǎn)單而又高效地把結(jié)果給它搞出來(lái).
另外,UNIX系統(tǒng)本身也是用C語(yǔ)言寫(xiě)的多進(jìn)程程序,多進(jìn)程編程是UNIX的特點(diǎn),
當(dāng)我們
熟悉了多進(jìn)程編程后,將會(huì)對(duì)UNIX系統(tǒng)機(jī)制有一個(gè)較深的認(rèn)識(shí).首先我介紹一下多
進(jìn)程程
序的一些突出的特點(diǎn):
1.并行化
一件復(fù)雜的事件是可以分解成若干個(gè)簡(jiǎn)單事件來(lái)解決的, 這在程序員
的大腦中早就形成了這種概念, 首先將問(wèn)題分解成一個(gè)個(gè)小問(wèn)題, 將小問(wèn)
題再細(xì)分, 最后在一個(gè)合適的規(guī)模上做成一個(gè)函數(shù). 在軟件工程中也是這
么說(shuō)的. 如果我們以圖的方式來(lái)思考, 一些小問(wèn)題的計(jì)算是可以互不干擾
的, 可以同時(shí)處理, 而在關(guān)鍵點(diǎn)則需要統(tǒng)一在一個(gè)地方來(lái)處理, 這樣程序
的運(yùn)行就是并行的, 至少?gòu)娜说臅r(shí)間觀念上來(lái)說(shuō)是這樣的. 而每個(gè)小問(wèn)題
的計(jì)算又是較簡(jiǎn)單的.
2.簡(jiǎn)單有序
這樣的程序?qū)Τ绦騿T來(lái)說(shuō)不亞于管理一班人, 程序員為每個(gè)進(jìn)程設(shè)計(jì)
好相應(yīng)的功能, 并通過(guò)一定的通訊機(jī)制將它們有機(jī)地結(jié)合在一起, 對(duì)每個(gè)
進(jìn)程的設(shè)計(jì)是簡(jiǎn)單的, 只在總控部分小心應(yīng)付(其實(shí)也是蠻簡(jiǎn)單的), 就可
完成整個(gè)程序的施工.
3.互不干擾
這個(gè)特點(diǎn)是操作系統(tǒng)的特點(diǎn), 各個(gè)進(jìn)程是獨(dú)立的, 不會(huì)串位.
4.事務(wù)化
比如在一個(gè)數(shù)據(jù)電話查詢系統(tǒng)中, 將程序設(shè)計(jì)成一個(gè)進(jìn)程只處理一次
查詢即可, 即完成一個(gè)事務(wù). 當(dāng)電話查詢開(kāi)始時(shí), 產(chǎn)生這樣一個(gè)進(jìn)程對(duì)付
這次查詢; 另一個(gè)電話進(jìn)來(lái)時(shí), 主控程序又產(chǎn)生一個(gè)這樣的進(jìn)程對(duì)付, 每
個(gè)進(jìn)程完成查詢?nèi)蝿?wù)后消失. 這樣的編程多簡(jiǎn)單, 只要做一次查詢的程序
就可以了.
二.常用的多進(jìn)程編程的系統(tǒng)調(diào)用
1.fork()
功能:創(chuàng)建一個(gè)新的進(jìn)程.
語(yǔ)法:#include
#include
pid_t fork();
說(shuō)明:本系統(tǒng)調(diào)用產(chǎn)生一個(gè)新的進(jìn)程, 叫子進(jìn)程, 是調(diào)用進(jìn)程的一個(gè)復(fù)
制品. 調(diào)用進(jìn)程叫父進(jìn)程, 子進(jìn)程繼承了父進(jìn)程的幾乎所有的屬
性:
. 實(shí)際UID,GID和有效UID,GID.
. 環(huán)境變量.
. 附加GID.
. 調(diào)用exec()時(shí)的關(guān)閉標(biāo)志.
. UID設(shè)置模式比特位.
. GID設(shè)置模式比特位.
. 進(jìn)程組號(hào).
. 會(huì)話ID.
. 控制終端.
. 當(dāng)前工作目錄.
. 根目錄.
. 文件創(chuàng)建掩碼UMASK.
. 文件長(zhǎng)度限制ULIMIT.
. 預(yù)定值, 如優(yōu)先級(jí)和任何其他的進(jìn)程預(yù)定參數(shù), 根據(jù)種類不同
決定是否可以繼承.
. 還有一些其它屬性.
但子進(jìn)程也有與父進(jìn)程不同的屬性:
. 進(jìn)程號(hào), 子進(jìn)程號(hào)不同與任何一個(gè)活動(dòng)的進(jìn)程組號(hào).
. 父進(jìn)程號(hào).
. 子進(jìn)程繼承父進(jìn)程的文件描述符或流時(shí), 具有自己的一個(gè)拷貝
并且與父進(jìn)程和其它子進(jìn)程共享該資源.
. 子進(jìn)程的用戶時(shí)間和系統(tǒng)時(shí)間被初始化為0.
. 子進(jìn)程的超時(shí)時(shí)鐘設(shè)置為0.
. 子進(jìn)程的信號(hào)處理函數(shù)指針組置為空.
. 子進(jìn)程不繼承父進(jìn)程的記錄鎖.
返回值: 調(diào)用成功則對(duì)子進(jìn)程返回0, 對(duì)父進(jìn)程返回子進(jìn)程號(hào), 這也是
最方便的區(qū)分父子進(jìn)程的方法. 若調(diào)用失敗則返回-1給父進(jìn)程,
子進(jìn)程不生成.
例子:pid_t pid;
if ((pid=fork())>0) {
/*父進(jìn)程處理過(guò)程*/
}
else if (pid==0) {
/*子進(jìn)程處理過(guò)程*/
exit(0); /*注意子進(jìn)程必須用exit()退出運(yùn)行*/
}
else {
printf("fork error\n");
exit(0);
}
2.system()
功能:產(chǎn)生一個(gè)新的進(jìn)程, 子進(jìn)程執(zhí)行指定的命令.
語(yǔ)法:#include
#include
int system(string)
char *string;
說(shuō)明:本調(diào)用將參數(shù)string傳遞給一個(gè)命令解釋器(一般為sh)執(zhí)行, 即
string被解釋為一條命令, 由sh執(zhí)行該命令.若參數(shù)string為一
個(gè)空指針則為檢查命令解釋器是否存在.
該命令可以同命令行命令相同形式, 但由于命令做為一個(gè)參數(shù)放
在系統(tǒng)調(diào)用中, 應(yīng)注意編譯時(shí)對(duì)特殊意義字符的處理. 命令的查
找是按PATH環(huán)境變量的定義的. 命令所生成的后果一般不會(huì)對(duì)父
進(jìn)程造成影響.
返回值:當(dāng)參數(shù)為空指針時(shí), 只有當(dāng)命令解釋器有效時(shí)返回值為非零.
若參數(shù)不為空指針, 返回值為該命令的返回狀態(tài)(同waitpid())
的返回值. 命令無(wú)效或語(yǔ)法錯(cuò)誤則返回非零值,所執(zhí)行的命令被
終止. 其他情況則返回-1.
例子:char command[81];
int i;
for (i=1;i<8;i++) {
sprintf(command,"ps -t tty%02i",i);
system(command);
}
3.exec()
功能:執(zhí)行一個(gè)文件
語(yǔ)法:#include
int execl(path,arg0,...,argn,(char*)0)
char *path,*arg0,...,*argn;
int execv(path,argv)
char *path,*argv[];
int execle(path,arg0,...,argn,(char*)0,envp)
char *path,*arg0,...,*argn,*envp[];
int execve(path,argv,envp)
char *path,*argv[],*envp[];
int execvp(file,argv)
char *file,*argv[];
說(shuō)明:這是一個(gè)系統(tǒng)調(diào)用族, 用于將一個(gè)新的程序調(diào)入本進(jìn)程所占的內(nèi)
存, 并覆蓋之, 產(chǎn)生新的內(nèi)存進(jìn)程映象. 新的程序可以是可執(zhí)行
文件或SHELL批命令.
當(dāng)C程序被執(zhí)行時(shí),是如下調(diào)用的:
main(int argc,char *argv[],char *envp[]);
argc是參數(shù)個(gè)數(shù),是各個(gè)參數(shù)字符串指針數(shù)組,envp是新進(jìn)程的環(huán)
境變量字符串的指針數(shù)組.argc至少為1,argv[0]為程序文件名,
所以,在上面的exec系統(tǒng)調(diào)用族中,path為新進(jìn)程文件的路徑名,
file為新進(jìn)程文件名,若file不是全路徑名,系統(tǒng)調(diào)用會(huì)按PATH環(huán)
境變量自動(dòng)找對(duì)應(yīng)的可執(zhí)行文件運(yùn)行.若新進(jìn)程文件不是一個(gè)可
執(zhí)行的目標(biāo)文件(如批處理文件),則execlp()和execvp()會(huì)將該
文件內(nèi)容作為一個(gè)命令解釋器的標(biāo)準(zhǔn)輸入形成system().
arg0,...等指針指向'\0'結(jié)束的字符串,組成新進(jìn)程的有效參數(shù),
且該參數(shù)列表以一個(gè)空指針結(jié)束.反過(guò)來(lái),arg0至少必須存在并指
向新進(jìn)程文件名或路徑名.
同樣,argv是字符串指針數(shù)組,argv[0]指向新進(jìn)程文件名或路徑
名,并以一空指針結(jié)束.
envp是一個(gè)字符串指針數(shù)組,以空指針結(jié)束,這些字符串組成新進(jìn)
程的環(huán)境.
在調(diào)用這些系統(tǒng)調(diào)用前打開(kāi)的文件指針對(duì)新進(jìn)程來(lái)說(shuō)也是打開(kāi)的,
除非它已定義了close-on-exec標(biāo)志.打開(kāi)的文件指針在新進(jìn)程中
保持不變,所有相關(guān)的文件鎖也被保留.
調(diào)用進(jìn)程設(shè)置并正被捕俘的信號(hào)在新進(jìn)程中被恢復(fù)為缺省設(shè)置,
其它的則保持不變.
新進(jìn)程啟動(dòng)時(shí)按文件的SUID和SGID設(shè)置定義文件的UID和GID為有
效UID和GID.
新進(jìn)程還繼承了如下屬性:
. 附加GID.
. 進(jìn)程號(hào).
. 父進(jìn)程號(hào).
. 進(jìn)程組號(hào).
. 會(huì)話號(hào).
. 控制終端.
. alarm時(shí)鐘信號(hào)剩下的時(shí)間.
. 當(dāng)前工作目錄.
. 根目錄.
. 文件創(chuàng)建掩碼.
. 資源限制.
. 用戶時(shí)間,系統(tǒng)時(shí)間,子進(jìn)程用戶時(shí)間,子進(jìn)程系統(tǒng)時(shí)間.
. 記錄鎖.
. 進(jìn)程信號(hào)掩碼.
. 信號(hào)屏蔽.
. 優(yōu)先級(jí).
. 預(yù)定值.
調(diào)用成功后,系統(tǒng)調(diào)用修改新進(jìn)程文件的最新訪問(wèn)時(shí)間.
返回值:該系統(tǒng)調(diào)用一般不會(huì)有成功返回值, 因?yàn)樵瓉?lái)的進(jìn)程已蕩然無(wú)
存.
例子:printf("now this process will be ps command\n");
execl("/bin/ps","ps","-ef",NULL);
4.popen()
功能:初始化從/到一個(gè)進(jìn)程的管道.
語(yǔ)法:#include
FILE *popen(command,type)
char *command,type;
說(shuō)明:本系統(tǒng)調(diào)用在調(diào)用進(jìn)程和被執(zhí)行命令間創(chuàng)建一個(gè)管道.
參數(shù)command做為被執(zhí)行的命令行.type做為I/O模式,"r"為從被
執(zhí)行命令讀,"w"為向被執(zhí)行命令寫(xiě).返回一個(gè)標(biāo)準(zhǔn)流指針,做為管
道描述符,向被執(zhí)行命令讀或?qū)憯?shù)據(jù)(做為被執(zhí)行命令的STDIN或
STDOUT)該系統(tǒng)調(diào)用可以用來(lái)在程序中調(diào)用系統(tǒng)命令,并取得命令
的輸出信息或者向命令輸入信息.
返回值:不成功則返回NULL,成功則返回管道的文件指針.
5.pclose()
功能:關(guān)閉到一個(gè)進(jìn)程的管道.
語(yǔ)法:#include
int pclose(strm)
FILE *strm;
說(shuō)明:本系統(tǒng)調(diào)用用于關(guān)閉由popen()打開(kāi)的管道,并會(huì)等待由popen()
激活的命令執(zhí)行結(jié)束后,關(guān)閉管道后讀取命令返回碼.
返回值:若關(guān)閉的文件描述符不是由popen()打開(kāi)的,則返回-1.
例子:printf("now this process will call popen system call\n");
FILE * fd;
if ((fd=popen("ps -ef","r"))==NULL) {
printf("call popen failed\n");
return;
}
else {
char str[80];
while (fgets(str,80,fd)!=NULL)
printf("%s\n",str);
}
pclose(fd);
6.wait()
功能:等待一個(gè)子進(jìn)程返回并修改狀態(tài)
語(yǔ)法:#include
#include
pid_t wait(stat_loc)
int *stat_loc;
說(shuō)明:允許調(diào)用進(jìn)程取得子進(jìn)程的狀態(tài)信息.調(diào)用進(jìn)程將會(huì)掛起直到其
一個(gè)子進(jìn)程終止.
返回值:等待到一個(gè)子進(jìn)程返回時(shí),返回值為該子進(jìn)程號(hào),否則返回值為
-1.同時(shí)stat_loc返回子進(jìn)程的返回值.
例子:/*父進(jìn)程*/
if (fork()>0) {
wait((int *)0);
/*父進(jìn)程等待子進(jìn)程的返回*/
}
else {
/*子進(jìn)程處理過(guò)程*/
exit(0);
}
7.waitpid()
功能:等待指定進(jìn)程號(hào)的子進(jìn)程的返回并修改狀態(tài)
語(yǔ)法:#include
?? 快捷鍵說(shuō)明
復(fù)制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號(hào)
Ctrl + =
減小字號(hào)
Ctrl + -