fork()函数详解
目錄
1.基本了解:
2.fork函數(shù)的了解:
3.僵死進(jìn)程:?
4. fork和多線程:
1.多線程中某個(gè)線程調(diào)用 fork(),子進(jìn)程會(huì)有和父進(jìn)程相同數(shù)量的線程嗎?
2.父進(jìn)程被加鎖的互斥鎖 fork 后在子進(jìn)程中是否已經(jīng)加鎖?
5.寫時(shí)拷貝:
1.基本了解:
一個(gè)進(jìn)程,包括代碼、數(shù)據(jù)和分配給進(jìn)程的資源。fork 函數(shù)會(huì)新生成一個(gè)進(jìn)程,調(diào)用 fork 函數(shù)的進(jìn)程為父進(jìn)程,新生成的進(jìn)程為子進(jìn)程。在父進(jìn)程中返回子進(jìn)程的 pid,在子進(jìn)程中返回 0,失敗返回-1。
為什么兩個(gè)進(jìn)程的fpid不同呢,這與fork函數(shù)的特性有關(guān)。fork調(diào)用的一個(gè)奇妙之處就是它僅僅被調(diào)用一次,卻能夠返回兩次,它可能有三種不同的返回值:
????1)在父進(jìn)程中,fork返回新創(chuàng)建子進(jìn)程的進(jìn)程ID;
??? 2)在子進(jìn)程中,fork返回0;
??? 3)如果出現(xiàn)錯(cuò)誤,fork返回一個(gè)負(fù)值;
一個(gè)進(jìn)程調(diào)用fork()函數(shù)后,系統(tǒng)先給新的進(jìn)程分配資源,例如存儲(chǔ)數(shù)據(jù)和代碼的空間。然后把原來的進(jìn)程的所有值都復(fù)制到新的新進(jìn)程中,只有少數(shù)值與原來的進(jìn)程的值不同。相當(dāng)于克隆了一個(gè)自己。
2.fork函數(shù)的了解:
pid_t? fork(void);
函數(shù)返回類型 pid_t 實(shí)質(zhì)是 int 類型,Linux 內(nèi)核 2.4.0 版本的定義是:
typedef? int? _kenrnel_pid_t;
typedef??_kenrnel_pid_t? pid_t;
?fork 函數(shù)會(huì)新生成一個(gè)進(jìn)程,調(diào)用 fork 函數(shù)的進(jìn)程為父進(jìn)程,新生成的進(jìn)程為子進(jìn)程。
在父進(jìn)程中返回子進(jìn)程的 pid,在子進(jìn)程中返回 0,失敗返回-1(此時(shí)才能更好理解他的返回值)。
舉個(gè)例子:
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <assert.h>6 7 int main()8 { char * s = NULL;9 int n = 0;10 11 pid_t pid = fork();12 assert( pid != -1 );13 if ( pid == 0 )14 {15 s = "child";16 n = 4;17 }18 else19 {20 s = "parent";21 n = 10;22 }23 24 int i = 0;25 26 for(; i < n; i++ )27 {28 printf("pid=%d,s=%s\n",getpid(),s);29 sleep(1);30 }31 32 exit(0);33 }來看看運(yùn)行結(jié)果:
?共打印了4次child,10次parent。fork()產(chǎn)生的子進(jìn)程打印了4次child.
此時(shí)還應(yīng)注意fork()產(chǎn)生的子進(jìn)程和父進(jìn)程之間的并發(fā)問題。
3.僵死進(jìn)程:?
(1) 僵死進(jìn)程概念:子進(jìn)程先于父進(jìn)程結(jié)束,父進(jìn)程沒有調(diào)用 wait 獲取子進(jìn)程退出碼。
(2)僵死進(jìn)程的危害:
- 僵死進(jìn)程的PID還占據(jù)著,意味著海量的子進(jìn)程會(huì)占據(jù)滿進(jìn)程表項(xiàng),會(huì)使后來的進(jìn)程無法fork.
- 僵尸進(jìn)程的內(nèi)核棧無法被釋放掉,為啥會(huì)留著它的內(nèi)核棧,因?yàn)樵跅5淖畹投?#xff0c;有著thread_info結(jié)構(gòu),它包含著 struct_task 結(jié)構(gòu),這里面包含著一些退出信息
(3)如何處理僵死進(jìn)程:父進(jìn)程通過調(diào)用 wait()完成。
wait函數(shù):
?pid_t? wait(int *status);
??進(jìn)程一旦調(diào)用了wait,就立即阻塞自己,由wait自動(dòng)分析是否當(dāng)前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出,如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程,wait就會(huì)收集這個(gè)子進(jìn)程的信息,并把它徹底銷毀后返回;如果沒有找到這樣一個(gè)子進(jìn)程,wait就會(huì)一直阻塞在這里,直到有一個(gè)出現(xiàn)為止。?
下面看例子:
1 #include <stdio.h>2 #include <stdlib.h>3 #include <unistd.h>4 #include <string.h>5 #include <assert.h>6 #include <sys/wait.h>7 int main( int argc, char* argv[], char* envp[])8 { char * s = NULL;9 int n = 0;10 11 pid_t pid = fork();12 assert( pid != -1 );13 if ( pid == 0 )14 {15 s = "child";16 n = 4;17 }18 else19 {20 s = "parent";21 n = 10;22 23 int val = 0;24 int id = wait(&val);25 26 if ( WIFEXITED(val) )27 {28 printf("id=%d,val=%d\n",id,WEXITSTATUS(val));29 }30 }31 int i = 0;32 33 for(; i < n; i++ )34 {35 printf("pid=%d,s=%s\n",getpid(),s);36 sleep(1);37 }38 39 exit(0);40 }?運(yùn)行結(jié)果如圖:
引入wait函數(shù)可以使先處理完子進(jìn)程,再去處理父進(jìn)程。可以有效避免僵死進(jìn)程。
4. fork和多線程:
1.多線程中某個(gè)線程調(diào)用 fork(),子進(jìn)程會(huì)有和父進(jìn)程相同數(shù)量的線程嗎?
在Linux中,fork的時(shí)候只復(fù)制當(dāng)前線程到子進(jìn)程,也就是說除了調(diào)用fork的線程外,其他線程在子進(jìn)程中“蒸發(fā)”了。
2.父進(jìn)程被加鎖的互斥鎖 fork 后在子進(jìn)程中是否已經(jīng)加鎖?
調(diào)用fork的時(shí)候,會(huì)復(fù)制父進(jìn)程的所有鎖到子進(jìn)程中。
假設(shè)在fork之前,一個(gè)線程對(duì)某個(gè)鎖進(jìn)行的lock操作,即持有了該鎖,然后另外一個(gè)線程調(diào)用了fork創(chuàng)建子進(jìn)程。可是在子進(jìn)程中持有那個(gè)鎖的線程卻"消失"了,從子進(jìn)程的角度來看,這個(gè)鎖被“永久”的上鎖了,因?yàn)樗某钟姓摺罢舭l(fā)”了。
5.寫時(shí)拷貝:
傳統(tǒng)的fork()系統(tǒng)調(diào)用直接把所有的資源復(fù)制給新創(chuàng)建的進(jìn)程。這種實(shí)現(xiàn)過于簡單并且效率低下,因?yàn)樗截惖臄?shù)據(jù)也許并不共享,更糟的情況是,如果新進(jìn)程打算立即執(zhí)行一個(gè)新的映像,那么所有的拷貝都將前功盡棄。Linux的fork()使用寫時(shí)拷貝(copy-on-write)頁實(shí)現(xiàn)。
寫時(shí)拷貝是-一種可以推遲甚至免除拷貝數(shù)據(jù)的技術(shù)。內(nèi)核此時(shí)并不復(fù)制整個(gè)進(jìn)程地址空間,而是讓父進(jìn)程和子進(jìn)程共享同一個(gè)拷貝。只有在需要寫入的時(shí)候,數(shù)據(jù)才會(huì)被復(fù)制,從而使各個(gè)進(jìn)程擁有各自的拷貝。也就是說,資源的復(fù)制只有在需要寫入的時(shí)候才進(jìn)行,在此之前,只是以只讀方式共享。這種技術(shù)使地址空間上的頁的拷貝被推遲到實(shí)際發(fā)生寫入的時(shí)候。在頁根本不會(huì)被寫入的情況下——舉例來說,fork()后立即調(diào)用exec()—-它們就無需復(fù)制了。fork()的實(shí)際開銷就是復(fù)制父進(jìn)程的頁表以及給子進(jìn)程創(chuàng)建惟一的進(jìn)程描述符。在一般情況下,進(jìn)程創(chuàng)建后都會(huì)馬上運(yùn)行一個(gè)可執(zhí)行的文件,這種優(yōu)化可以避免拷貝大量根本就不會(huì)被使用的數(shù)據(jù)(地址空間里常常包含數(shù)十兆的數(shù)據(jù))。由于Unix強(qiáng)調(diào)進(jìn)程快速執(zhí)行的能力,所以這個(gè)優(yōu)化是很重要的。?
總結(jié)
以上是生活随笔為你收集整理的fork()函数详解的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数学建模——主成分分析及spss软件操作
- 下一篇: Linux fork函数