Linux--進(jìn)程(fork)
一、概述
1 fork():建立父進(jìn)程完整副本,作為子進(jìn)程的執(zhí)行。
子進(jìn)程獲得父進(jìn)程數(shù)據(jù) 空間、堆和棧的副本。注意,這是子進(jìn)程所擁有的副本。父進(jìn)程和子進(jìn)程并不共享這些存儲(chǔ)空間部分。父進(jìn)程和子進(jìn)程共享正文段。

exec():
裝載一個(gè)新的程序(可執(zhí)行映像)覆蓋當(dāng)前進(jìn)程內(nèi)存空間中的映像,從而執(zhí)行不同的任務(wù)。
exec系列函數(shù)在執(zhí)行時(shí)會(huì)直接替換掉當(dāng)前進(jìn)程的地址空間。

子進(jìn)程調(diào)用exec()函數(shù)后,會(huì)替換掉原來(lái)的進(jìn)程程序,子進(jìn)程會(huì)進(jìn)行自己的進(jìn)程程序?qū)崿F(xiàn)。
CW技術(shù)(寫(xiě)時(shí)復(fù)制):
? ?

子進(jìn)程時(shí)復(fù)制過(guò)去的數(shù)據(jù)是沒(méi)用的(因?yàn)樽舆M(jìn)程執(zhí)行exec(),原有的數(shù)據(jù)會(huì)被清空)
既然很多時(shí)候復(fù)制給子進(jìn)程的數(shù)據(jù)是無(wú)效的,于是就有了Copy On Write這項(xiàng)技術(shù)。
原理:fork之后exec之前兩個(gè)進(jìn)程用的是相同的物理空間(內(nèi)存區(qū)),子進(jìn)程的代碼段、數(shù)據(jù)段、堆棧都是指向父進(jìn)程的物理空間,也就是說(shuō),兩者的虛擬空間不同,物理空間是同一個(gè)。當(dāng)父子進(jìn)程中有更改相應(yīng)段的行為發(fā)生時(shí),再為子進(jìn)程相應(yīng)的段分配物理空間。
優(yōu)點(diǎn):減少延時(shí)、減少不必要的復(fù)制(fork()時(shí)父進(jìn)程數(shù)據(jù)段是只讀)、保證數(shù)據(jù)完整性。
缺點(diǎn):fork()之后,父子進(jìn)程還要繼續(xù)進(jìn)行寫(xiě)操作,那么會(huì)產(chǎn)生大量的分頁(yè)錯(cuò)誤(頁(yè)異常中斷page-fault)。
CW例子:
#include <stdio.h>??
#include <stdlib.h>??
#include <unistd.h>??
#include <sys/types.h>??
#include <sys/stat.h>??
#include <fcntl.h>??
#include <sys/wait.h>
#include <string.h>
??
int main(){??
? ? ? ? int fd;??
? ? ? ? char A[10];??
? ? ? ? char *child = "Child output\n";??
? ? ? ? fd = open("xxx.txt",O_RDWR|O_CREAT,0666);??
? ? ? ? printf("fd:%d\n",fd);?
? ? ? ? write(fd,"xxx.txt",7);??
? ? ? ? close(fd);
? ? ? ? fd = open("foobar.txt",O_RDONLY,0);
? ? ? ? printf("fd:%d\n",fd);
? ? ? ? if(fork()==0)
? ? ? ? {??
? ? ? ? ? ? ? ? fd = 1;
? ? ? ? ? ? ? ? write(fd,child,strlen(child)+1);
? ? ? ? ? ? ? ? exit(0);??
? ? ? ? }??
? ? ? ? printf("fd:%d\n",fd);
? ? ? ? read(fd,A,sizeof(A));
? ? ? ? close(fd);?
? ? ? ? A[10]='\0';??
? ? ? ? printf("A = %s\n",A);??
? ? ? ? exit(0);??
}??
#在使用的時(shí)候,就分配實(shí)際物理內(nèi)存給子進(jìn)程,沒(méi)使用就不分配,還是用父進(jìn)程資源。
調(diào)一次,返回兩次,fork()函數(shù)調(diào)用完成以后父進(jìn)程的虛擬存儲(chǔ)空間被拷貝給了子進(jìn)程的虛擬
存儲(chǔ)空間,因此也就實(shí)現(xiàn)了共享文件等操作。主要CW技術(shù)用于虛擬的存儲(chǔ)空間映射到物理存
儲(chǔ)空間的過(guò)程,就是有進(jìn)程寫(xiě)入時(shí),內(nèi)核就會(huì)在物理存儲(chǔ)器中開(kāi)辟一個(gè)新的物理頁(yè)面,將內(nèi)容
復(fù)制到新物理頁(yè)面,再寫(xiě)入。這種用時(shí)寫(xiě)入節(jié)省時(shí)間空間。
fork()例子:
1 int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("i");? ?
}
return 0;
}
1> 一次調(diào)用,兩次返回,如果返回是0,則是子進(jìn)程,如果返回值>0,則是父進(jìn)程(返回值是子進(jìn)程的pid)。
2> 在fork()的調(diào)用處,整個(gè)父進(jìn)程空間會(huì)原模原樣地復(fù)制到子進(jìn)程中,包括指令,變量值,程序調(diào)用棧,環(huán)境變量,緩沖區(qū),等等。
3> 塊設(shè)備一般都有緩存,而字符設(shè)備一般都沒(méi)有緩存,對(duì)于磁盤(pán)這個(gè)塊設(shè)備來(lái)說(shuō),“n”并不會(huì)引起緩沖區(qū)刷出的動(dòng)作,那是全緩沖,你可以使用setvbuf來(lái)設(shè)置緩沖區(qū)大小,或是用fflush刷緩存,建議用fflush(stdout)。
2?
int main(void)
{
int i;
for(i=0; i<2; i++){
fork();
printf("ppid=%d, pid=%d, i=%d n", getppid(), getpid(), i); // 有 n
}
sleep(10);
return 0;
}
1>??6次輸出
ppid=8858, pid=8518, i=0
ppid=8858, pid=8518, i=1
ppid=8518, pid=8519, i=0
ppid=8518, pid=8519, i=1
ppid=8518, pid=8520, i=1
ppid=8519, pid=8521, i=1
2> 查看進(jìn)程:pstree -p | grep fork
|-bash(8858)-+-fork(8518)-+-fork(8519)---fork(8521)
3 自己寫(xiě)個(gè)vim
#include <unistd.h>??
#include <stdio.h>??
?#include <stdlib.h>
int main ()? ?
{? ?
? ? pid_t fpid;?
? ? int count=0;
? ? fpid=fork();
? ? if (fpid < 0)? ?
? ? ? ? printf("failure!/n");? ?
? ? else if (fpid == 0) {??
? ? ? ? printf("child/n");? ?
? ? ? ? count++;??
? ? }??
? ? else {??
? ? ? ? printf("parent/n");? ?
? ? ? ? count++;??
? ? }??
? ? printf("%d/n",count);??
? ? return 0;??
}??
函數(shù)會(huì)有兩次返回,將子進(jìn)程的PID返回給父進(jìn)程,0返回給子進(jìn)程。