关于fork函数的使用
基本必備知識
fork函數的基本說明
介紹
fork函數用于創建一個新的進程,下面引用一段話介紹一下:
一個進程,包括代碼、數據和分配給進程的資源。fork()函數通過系統調用創建一個與原來進程幾乎完全相同的進程,也就是兩個進程可以做完全相同的事,但如果初始參數或者傳入的變量不同,兩個進程也可以做不同的事。
一個進程調用fork()函數后,系統先給新的進程分配資源,例如存儲數據和代碼的空間。然后把原來的進程的所有值都復制到新的新進程中,只有少數值與原來的進程的值不同。相當于克隆了一個自己
函數原型
pid_t fork( void);其中pid_t是一個宏定義,其本身是int型的數據。
函數的返回
fork函數調用非常的神奇,一次調用將會有兩次放回,一次是在父進程之中返回,另一次是在子進程里面返回。
而父進程的的返回又分為兩種情況:
- 返回值大于零:成功創建子進程并返回子進程的PID
- 返回值為-1:發生錯誤,創建子進程失敗
- 返回值為0:子進程的返回值是0
wait函數與waitpid函數的基本說明
waitpid函數
waitpid()會暫時停止目前進程的執行,直到有信號來到或子進程結束.
函數原型
pid_t waitpid(pid_t pid, int *statusp, int options);pid參數有四種情況:
- pid > 0:只等待進程ID等于pid的子進程,不管其它已經有多少子進程運行結束退出了,只要指定的子進程還沒有結束,waitpid就會一直等下去。
- pid = 0:waitpid所等待的進程是同一個進程組里的任意一個子進程。
- pid = -1:wait所等待的進程是父進程的任意一個子進程。
- pid < -1:waitpid所等待的進程是一個指定進程組中的任何子進程,這個進程組的ID等于pid的絕對值。
- 如果statusp是一個空指針,則表示父進程不關心子進程結束的狀態。
- 如果statusp不是一個空指針,則會在status放上關于導致返回的子進程的狀態信息,相關信息請看下面的表格
| WIFEXITED(status) | 如果子進程通過調用exit或者一個返回(returm)正常終止,就返回真。 |
| WEXITSTATUS(status) | 返回一個正常終止的子進程的退出狀態。 |
| WIFSIGNALED(status) | 如果子進程是因為一個未被捕獲的信號終止的,那么就返回真。 |
| WTERMSIG(status) | 返回導致子進程終止的信號的編號。 |
| WIFSTOPPED(status) | 如果引起返回的子進程當前是停止的,那么就返回真。 |
| WSTOPSIG(status) | 返回引起子進程停止的信號的編號。 |
| WIFCONTINUED(status) | 如果子進程收到SIGCONT信號重新啟動,則返回真。 |
可以通過將options設置為常量WNOHANG、WUNTRACED和WCONTINUED或是它們的組合來修改默認行為。
| WNOHANG | 如果等待集合中的任何子進程都還沒有終止,那么就立即返回(返回值為0)。默認的行為是掛起調用進程,直到有子進程終止。在等待子進程終止的同時,如果還想做些有用的工作,這個選項會有用。 |
| WUNTRACED | 掛起調用進程的執行,直到等待集合中的一個進程變成已終止或者被停止。返回的PID為導致返回的已終止或被停止子進程的PID。默認的行為是只返回已終止的子進程。當你想要檢查已終止和被停止的子進程時,這個選項會有用。 |
| WCONTINUED | 掛起調用進程的執行,直到等待集合中一個正在運行的進程終止或等待集合中一個被停止的進程收到SIGCONT信號重新開始執行。 |
函數的返回
- 如過成功則返回子進程的 PID
- 如果參數options為WNOHANG則返回 0
- 如果發生了其他的錯誤則會返回 -1
wait函數
函數原型
pid_t wait(int *status);其實調用wait(&status)等價于調用waitpid(- 1, &status, 0 ),這里就不對這個函數做過多的解釋了。
函數的返回
- 如過成功則返回子進程的 PID
- 如果發生了錯誤則會返回 -1
signal函數的基本說明
函數的聲明
void (*signal(int sig, void (*func)(int)))(int);signal函數用于修改信號與其相關聯的默認行為,例如接收到SIGKILL信號默認行為就是終止接收進程,接收到SIGCHLD信號默認行為就是忽略這個信號。但有一點特殊的是,SIGSTOP和SIGKILL,這兩個的信號的默認行為是無法修改的。
參數
- sig:待修改關聯的信號
- func:修改關聯信號的新處理函數
fork函數的使用例子
例子1
代碼:
void fork0() {if (fork() == 0) {printf("Hello from child,pid = %d\n",getpid());}else {printf("Hello from parent,pid = %d\n",getpid());} }它輸出的內容是:
Hello from parent,pid = 18913 Hello from child,pid = 18914在fork函數的介紹里面有說過,fork函數是一次調用兩次返回,我們就可以通過在父進程與子進程中返回的內容的不同來分辨當前進程是父進程還是子進程,從而達到我們所需要的效果。
fork函數在子進程之中返回 0,所以Hello from child,pid = 18914應是由子進程在完成 if 條件語句后轉跳到printf函數進行輸出。而父進程中fork函數返回的是子進程的pid(18914),則Hello from parent,pid = 18913應是由父進程進行輸出。
例子2
代碼:
void fork1() {int x = 1;pid_t pid = fork();if (pid == 0) {printf("Child has x = %d\n", ++x);} else {printf("Parent has x = %d\n", --x);}printf("Bye from process %d with x = %d\n", getpid(), x); }輸出到屏幕的內容如下:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 1 Parent has x = 0 Child has x = 2 Bye from process 16 with x = 0 Bye from process 17 with x = 2父進程調用fork函數創建了一個新的進程活后,此時系統之中就會出現了兩個基本一樣的進程,無論是代碼或是數據都基本一樣,就相當于把父進程復制了一份,但由于fork函數返回的值不同,會有相應的邏輯判斷,從而導致基本一樣的進程執行的代碼卻不太一樣。
例子3
代碼:
void fork3() {printf("L0\n");fork();printf("L1\n"); fork();printf("L2\n"); fork();printf("Bye\n"); }輸出到屏幕的內容如下:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 3 L0 from process 18 L1 from process 18 L1 from process 19 L2 from process 18 L2 from process 20 L2 from process 19 L2 from process 21 Bye from process 18 Bye from process 22 Bye from process 20 Bye from process 23 Bye from process 19 Bye from process 24 Bye from process 21 Bye from process 25主要是理解了fork函數的使用后,弄清楚它的邏輯,既能夠很清楚的理解它的輸出為什么是這樣。當然,不同的機器跑出來的結果有可能會不一樣,個別內容的輸出順序可能會跟我上面的不一樣,這得看CPU如何處理這些進程。
下面是一個大致的流程圖:
例子4
代碼:
void fork5() {printf("L0 from process %d,ppid:%d\n", getpid(),getppid());if (fork() == 0) {printf("L1 from process %d,ppid:%d\n", getpid(),getppid()); if (fork() == 0) {printf("L2 from process %d,ppid:%d\n", getpid(),getppid());}}printf("Bye from process %d,ppid:%d\n", getpid(),getppid()); }輸出到屏幕的內容如下:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 5 L0 from process 38,ppid:4 Bye from process 38,ppid:4 L1 from process 39,ppid:38 Bye from process 39,ppid:1 L2 from process 40,ppid:39 Bye from process 40,ppid:1下面是一個大致的流程圖:
例子5
代碼:
void fork18() {printf("fork");if (fork() == 0){printf("2018\n");}printf("2019\n"); }你覺得這個代碼所輸出的內容會是什么呢?
輸出到屏幕的內容如下:
其實這printf的緩沖機制有關,printf并不會直接把內容直接打印到屏幕上,而是放在了緩存之中,所以在執行fork的時候,緩沖區的內容也會被放到子進程相應的地方。一般來說,緩沖區滿后或是遇到了換行符才會把內容輸出到屏幕上。
例子6
代碼:
void fork9_handler(int sig) {printf("Process %d received signal %d\n", getpid(), sig);exit(0); } void fork9() {int child_status;signal(SIGINT,fork9_handler);if (fork() == 0) {printf("HC: hello from child\n");while(1);} else {printf("HP: hello from parent\n");wait(&child_status);printf("CT: child has terminated\n");}printf("Bye\n"); }由于在子進程中有死循環,所以該程序無法正常的結束,可以另一個終端窗口發送SIGINT信號給子進程令程序正常結束。所以輸出入下:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 9 HP: hello from parent HC: hello from child Process 90 received signal 2 CT: child has terminated Bye但是我在運行上面的程序時,在子進程死循環時掛起進程,再發送SIGINT信號給子進程,然而此時父進程卻不能夠正常的退出了:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 9 HP: hello from parent HC: hello from child ^Z [1]+ 已停止 ./f 9 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ psPID TTY TIME CMD4 tty1 00:00:00 bash17 tty1 00:00:00 f18 tty1 00:00:01 f19 tty1 00:00:00 ps hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ kill -2 18 Process 18 received signal 2 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ psPID TTY TIME CMD4 tty1 00:00:00 bash17 tty1 00:00:00 f18 tty1 00:00:01 f <defunct>20 tty1 00:00:00 ps hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ fg 1 ./f 9 ^Z [1]+ 已停止 ./f 9 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ psPID TTY TIME CMD4 tty1 00:00:00 bash17 tty1 00:00:00 f18 tty1 00:00:01 f <defunct>21 tty1 00:00:00 ps且在另一個終端查看進程時仍能夠看到父進程還在運行著,但是拿著同樣的程序在另外一臺機器上跑確是能夠正常的退出,或許這是Windows子系統在某方面沒做好吧。
于是我又再次修改了代碼,進行實驗:
運行結果如下:
hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ ./f 9 HP: hello from parent HC: hello from child ^Z [1]+ 已停止 ./f 9 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ psPID TTY TIME CMD4 tty1 00:00:00 bash16 tty1 00:00:00 f17 tty1 00:00:01 f18 tty1 00:00:00 ps hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ kill -2 17 Process 17 received signal 2 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ fg 1 ./f 9 Process 16 received signal 17 hpj@黃培紀:/mnt/d/code/C/Computer Systems/ch8$ psPID TTY TIME CMD4 tty1 00:00:00 bash19 tty1 00:00:00 ps這一次父進程能夠正常的退出了,所以我個人懷疑是wait函數在Window子系統下會發生一些未知的錯誤。
參考資料
- wait()和waitpid()的參數解析
- waitpid_百度百科
- Computer Systems: A Programmer’s Perspective
- fork()函數詳解
- Signal ()函數用法和總結
總結
以上是生活随笔為你收集整理的关于fork函数的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 80ms 求解世上最难数独 —— DFS
- 下一篇: JTAG to AXI Master的A