Linux系統中,進程的執行模式劃分爲用戶模式和內核模式,當進程運行於用戶空間時屬於用戶模式,
若是在用戶程序運行過程當中出現系統調用或者發生中斷事件,就要運行操做系統(即核心)程序,進程的運行模式就變爲內核模式
在該模式下運行的進程能夠執行機器特權指令,並且該進程的運行不受用戶的干預centos
在Linux操做系統中,經過fork()系統調用來建立子進程數組
目標bash |
建立進程數據結構 |
頭文件函數 |
#include <unistd.h>spa |
函數原型操作系統 |
pid_t_result=fork(void)指針 |
參數excel |
無code |
返回值 |
-1 若是出錯 0 返回到子進程 pid 將子進程的進程id返回給父進程 |
將執行fork的操做進程稱爲父進程,被fork()建立的進程稱爲該進程的子進程,
當父進程執行fork操做時,操做系統內核執行以下任務完成進程的建立工做:
1.爲子進程分配新的內存塊和內核數據結構
2.複製原來的進程信息到新的進程,即子進程和父進程具備相同的可執行代碼
3.向運行進程集添加新的進程
4.fork執行結束後,將控制返回給2個進程,此時兩個進程可獨立執行,執行順序取決於進程調度
#include <stdio.h> main(){ int ret_from_fork,mypid; mypid=getpid(); printf("Before :my pid is %d\n",mypid ); ret_from_fork=fork(); sleep(1); printf("After:my pid is %5d,ret_from_fork:%5d,parent pid :%5d\n",getpid(),ret_from_fork,getppid()); } [root@centos1 process]# ./fork Before :my pid is 22526 After:my pid is 22526,ret_from_fork:22527,parent pid :21968 After:my pid is 22527,ret_from_fork: 0,parent pid :22526
父進程建立子進程後,子進程除了具備相同的代碼段拷貝外,也具備相同的數據段,
即父進程的全局變量名稱和值子進程也會一塊兒拷貝過去,但他們之間是獨立的,可獨立改變相互不受影響
#include <sys/types.h> //pid_t 類型的在此 #include <unistd.h> #include <stdio.h> #include <stdlib.h> //exit 用 int glob=6; //全局變量 int main(){ int local; pid_t pid; local = 8; if( (pid=fork()) == 0){ sleep(2); }else if(pid>0){ glob =60; local =80; sleep(10); } printf("glob=%d,local=%d,mypid=%d\n",glob,local,getpid()); exit (0); } [root@centos1 process]# ./var glob=6,local=8,mypid=22800 glob=60,local=80,mypid=22799 第一行輸出爲子進程,第二行未父進程的輸出,說明父子進程由相同的代碼,但他們變量之間相互不影響
通常狀況下,父進程建立子進程是爲了執行特定的任務,經過執行exec家族系列調用讓子進程執行新的任務
此時,由exec調用提供的命令的指令代碼替換子進程的代碼,至關於對子進程進行了換腦
int execl(const char*path,const char* arg0,const char* arg1,.... NULL)
int execlp(const char*path,const char* arg0,const char* arg1,.... NULL)
int execv(const char*path,const char* argv[])
int execvp(const char*path,const char* argv[])
目標 |
在指定路徑中查找並執行一個文件 |
頭文件 |
#include <unistd.h> |
函數原型 |
result=execvp(const *file,const char *argv[]) |
參數 |
file 要執行的文件名 argv 字符串數組 |
返回值 |
-1 若是出錯 若成功,execvp 沒有返回值 |
excel,excelp徹底相同
excev,execvp 徹底相同
excel,excev要求提供可執行文件的絕對路徑或相對路徑名
帶p的 使用$PATH環境變量查找程序
主要區別是:
1.可執行文件的查找方式:不是P結尾的都是完整的目錄路徑,p結尾的可只給出文件名,系統自動從環境變量中進行查找
2.參數的傳遞方式:
有2種方式,逐個列舉;將全部的參數總體構造指針數組傳遞,以函數名第5位字母來區分
l(list)的表示逐個列舉方式 ,語法爲char *arg;
字母爲v(vertor)的表示將全部參數總體構造指針數組傳遞,語法爲: *const argv[]
exec系列調用沒找到和執行文件返回-1 ,否者進程用可執行文件替換它的代碼、數據和堆棧
過程:
1.將制定的程序複製到它的進程
2.用制定的字符串數組做爲argvp[]傳給這個程序
3.運行這個程序
#include <unistd.h> #include <stdio.h> #include <stdlib.h> main(){ char *arglist[1]; pid_t pid; arglist[0]="-l"; arglist[1]="-a"; printf("parent:%d\n",getpid()); pid=fork(); if( pid==0 ){ printf("son1 pid is:%d\n",getpid() ); execvp("ls",arglist); printf("son2 pid is:%d\n",getpid() ); }else{ printf("parnet pid:%d\n",getpid() ); } printf("in %d\n",getpid()); } /** execvp 後的當前進程的代碼再也不執行 root@centos1 c]# ./exec parent:26307 parnet pid:26307 in 26307 son1 pid is:26308 [root@centos1 c]# . .. exec exec.c fork.c var.c **/
殭屍進程
當父進程尚未結束而子進程結束運行,同時父進程沒有使用wait系統調用獲取子進程的結束狀態時
子進程就成爲殭屍進程
父進程先結束不會產生殭屍進程。
殭屍進程沒有任何代碼、數據和堆棧,佔用不了多少資源,但它存在於系統的任務列表中。在進程表裏仍佔了1個位置佔用進程號
通常要避免出現這種狀況
當使用ps查看進程時 若是進程名稱旁邊出現defunct,則代表該進程爲殭屍進程
#include <stdio.h> #include <unistd.h> void parent_code(int delay){ sleep(delay); } main(){ pid_t pid; int status; pid=fork(); if(pid == 0){ } if(pid>0){ parent_code(20); } } /* [root@centos1 c]# gcc -o zombie zombie.c [root@centos1 c]# ./zombie& [1] 28577 [root@centos1 c]# ps PID TTY TIME CMD 25201 pts/0 00:00:00 bash 28577 pts/0 00:00:00 zombie 28578 pts/0 00:00:00 zombie <defunct> 28580 pts/0 00:00:00 ps */
爲了防止子進程成爲殭屍進程,通常在父進程調用wait()系統調用等待子進程的結束並獲取子進程的返回狀態
wait調用作2件事:
首先暫停調用它的進程直到子進程結束,而後取得子進程結束時傳給exit的值
目標 |
等待子進程的結束 |
頭文件 |
#include <sys/wait.h> #include <sys/types.h> |
函數原型 |
pid_t pid=wait(int *status) |
參數 |
status指向一個保存子進程返回狀態的整型變量 |
返回值 |
若是不存在子進程,返回-1 如有任何一個子進程結束,則返回該子進程的pid並保存其返回狀態在status中,同時wait調用也結束 |
#include <sys/wait.h> #include <sys/types.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> void child(int delay){ sleep(delay); exit(0); } void parent(int *status){ wait(status); } main(){ pid_t pid; int status; printf("Before:%d\n",getpid()); pid=fork(); if(pid == 0){ child(10); } if(pid >0 ){ printf("pid =%d\n",getpid()); parent(&status); printf("status =%d\n",status); } } /* [root@centos1 c]# ./a.out Before:14901 pid =14901 status =0 */
上述代碼可防止殭屍進程的產生
waitpid()亦可實現wait()調用相似功能
多數狀況下會使用waitpid()
他們的主要區別是:
1.wait()只能獲得任何一個子進程結束的狀態,當一個父進程有多個子進程時,在某個子進程結束,則wait可獲得其結束狀態
此時沒法再獲得其它子進程的結束狀態,此時容易產生其它子進程的殭屍進程
2.wait()調用屬於阻塞調用,父進程執行該指令後,器等待子進程結束以後才能執行它後面的代碼,
而waitpid()可提供非阻塞調用的方式
3.waitpid()調用能夠等待指定的子進程具備比wait多的功能
目標 |
等待某個子進程的結束 |
頭文件 |
#include <sys/wait.h> #include <sys/types.h> |
函數原型 |
pid_t pid=waitpid(pid_t pid,int *status,int options) |
參數 |
pid=-1 等待任一個子進程.與wait等效 pid>0 等待進程id時pid的子進程 pid=0 等待其組id等於調用進程組id的任一子進程 pid<-1 等待其組id等於pid絕對值的任一進程 options選項: WNOHANG 表示若是沒有任何已經結束的子進程則立刻返回,不等待 WUNTRACED 若是子進程進入暫停執行狀況則立刻返回,但結束狀態不予以理會 0 做用和wait同樣,阻塞父進程,等待子進程結束
|
返回值 |
若是不存在子進程,返回-1 如有指定子進程結束,則返回該子進程的pid並保存其返回狀態在status中,同時waitpid調用也結束 |
子進程退出狀態檢測的宏
宏 |
說明 |
WIFEXITED(status) |
若是子進程正常結束,則爲非0值,此時可調用WEXITSTATUS(status) 取得子進程exit()返回的結束代碼 |
WIFSIGNALED(status) |
若是子進程是由於信號則此宏的值爲真, 此時可用WIERMSIG(status)取得子進程因信號而終止的信號代碼 |
WIFSTOPPED(status) |
若是子進程處於暫停執行狀況則此宏爲真.採用WSTOPPSIG(status) 取得引起子進程暫停的信號代碼 |