#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> int main(void) { pid_t fpid; //fpid表示fork函數返回的值 int count=0; fpid=fork(); if (fpid < 0) printf("error in fork!\n"); else if (fpid == 0) { printf("i am the child process, my process id is %d\n",getpid()); printf(" my parent process id is %d\n",getppid()); count++; } else { printf("i am the parent process, my process id is %d\n",getpid()); count++; } printf("count 統計結果是: %d\n",count); while(1);//無限阻塞 return 0; }
fork 函數,建立子進程。數組
函數原型:app
關於其返回值:函數
fork函數一次調用,兩次返回。子進程中返回0,父進程中,返回子進程的ID。若是fork失敗,返回-1.而且不會建立子進程,同時錯誤代碼errno會被設置。spa
fork的讀時共享,寫時複製機制。子進程擁有和父進程同樣的0-3G用戶空間,可是3-4G內核空間中PCB(進程控制塊)的進程ID號並不相同。子進程和父進程有如此多相同的地方,若是僅僅是讀取0-3G用戶空間的數據,則沒有必要複製一份父進程的數據到內存,但若是須要寫入數據,就必須開闢新的空間了。3d
——————————————————》》》分割線指針
可是,子進程建立出來若是所作的任務和父進程徹底一致,那麼也就沒有必要建立子進程的意義了。一般而言,子進程被建立,是要作與父進程不同的事情。code
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> int main(void) { pid_t fpid; int count=0; fpid=fork(); if (fpid < 0) printf("error in fork!\n"); else if (fpid == 0) { char *const argv[]={"ls","-l",NULL}; printf("i am a child process\n"); execvp("ls",argv); printf("where is my process?\n"); } else { printf("i am a parent process\n"); } while(1); return 0; }
execvp:屬於exec族中的一個函數。blog
當進程調用了exec族的某一函數以後,該進程執行的程序被替換成全新的程序,新程序從其main函數開始執行。因爲調用exec函數不會建立新的進程,因此替換先後的進程ID並不會改變。exec只是用磁盤上的一個新程序替換了當前進程的正文段、數據段、堆段和棧段。進程
在上面的程序中,使用了ls –l這個指令,能夠發現該指令確實正確執行了,可是顯示的列表中,a.out沒有顏色信息。其次,在char *const argv[]={"ls","-l",NULL};中,第一個「ls」字符串只有一個佔位符的做用,該字符串不管是什麼都不會影響程序的運行結果,由於在execvp("ls",argv);中已經指定了ls程序,在argv指針數組中的argv[0]只用做佔位符。哪怕將其改成」hehe」:內存
程序依然正常運行,exec函數會從argv[1]開始,直到NULL結束。
如今新建一個upper.c文件:
/* upper.c */ #include<stdio.h> #include <ctype.h> int main(void) { in ch; while((ch=getchar())!=EOF) putchar(toupper(ch));//小寫轉大寫 return 0; }
把以前的test.c改爲:
#include<stdio.h> #include<sys/types.h> #include<unistd.h> #include<stdlib.h> int main(void) { pid_t fpid; int count=0; fpid=fork(); if (fpid < 0) printf("error in fork!\n"); else if (fpid == 0) { char *const argv[]={"upper",NULL}; printf("i am a child process\n"); execv("./app",argv); printf("where is my process?\n"); } else { printf("i am a parent process\n"); } while(1); return 0; }
先編譯upper.c生成app,而後編譯test.c生成a.out,運行a.out:
exec函數運行以後,只有出錯纔會返回,這也就意味着,exec函數後面的語句不會被執行。一樣,這裏的char *const argv[]={"upper",NULL};中的「upper」字符串也能夠是其餘任何字符串(即便爲NULL也行,但一般使用能表明其含義的名稱)。
wait和waitpid函數:
殭屍進程: 子進程退出,父進程沒有回收子進程資源(PCB),則子進程變成殭屍進程。
孤兒進程: 父進程先於子進程結束,則子進程成爲孤兒進程,子進程的父進程成爲1號進程init進程,稱爲init進程領養孤兒進程。
一個進程在終止時會關閉全部文件描述符,釋放在用戶空間分配的內存,但它的PCB還保留着,內核在其中保存了一些信息:若是是正常終止則保存着退出狀態,若是是異常終止則保存着致使該進程終止的信號是哪一個。這個進程的父進程能夠調用wait或waitpid獲取這些信息,並完全清除掉這個進程。咱們知道一個進程的退出狀態能夠在Shell中用特殊變量$?查看(echo $?),由於Shell是它的父進程,當它終止時Shell調用wait或waitpid獲得它的退出狀態同時完全清除掉這個進程。若是一個進程已經終止,可是它的父進程還沒有調用wait或waitpid對它進行清理,這時的進程狀態稱爲殭屍(Zombie)進程。任何進程在剛終止時都是殭屍進程,正常狀況下,殭屍進程都馬上被父進程清理了。
目前,只用知道,wait和waitpid的基本用法便可,至於到底怎麼應用在程序中,能夠暫時不理會。
若是使用了WNOHANG參數調用waitpid,即便沒有子進程退出,它也會當即返回,不會像wait那樣永遠等下去。