Linux系統編程--3(exec 函數族,僵尸進程和孤兒進程,wait和wait_pid回收子進程)

exec 函數族

fork 創建子進程后執行的是和父進程相同的程序(但有可能執行不同的代碼分支) ,子進程往往要調用一種 exec 函數以執行另一個程序。當進程調用一種 exec 函數時,該進程的用戶空間代碼和數據完全被新程序替換,從新程序 的啟動例程開始執行。調用 exec 并不創建新進程,所以調用 exec 前后該進程的 id 并未改變。
將當前進程的.text、.data 替換為所要加載的程序的.text、.data,然后讓進程從新的.text 第一條指令開始執行, 但進程 ID 不變,換核不換殼。
其實有六種以 exec 開頭的函數,統稱 exec 函數:

1. int execl(constchar*path,const char*arg,...);
2. int execlp(constchar*file,const char*arg,...);
3. int execle(constchar*path,const char*arg,...,char*constenvp[]);
4. int execv(constchar*path,char*constargv[]);
5. int execvp(constchar*file,char*constargv[]);
6. int execve(constchar*path,char*constargv[],char*constenvp[]);

execlp 函數

加載一個進程,借助 PATH 環境變量
intexeclp(constchar*file,constchar*arg,...);

  1. 成功:無返回;
  2. 失敗:-1

參數 1:要加載的程序的名字。該函數需要配合 PATH 環境變量來使用,當 PATH 中所有目錄搜索后沒有參 數 1 則出錯返回。
該函數通常用來調用系統程序。如:ls、date、cp、cat 等命令。

編寫一個程序,實現Linux下shell命令ls -l -a

   #include<stdio.h>#include<unistd.h>#include<stdlib.h>int main(void){pid_t pid;pid=fork();if(pid==-1){   perror("fork error:");exit(1);}else if(pid>0){   sleep(1);printf("parent\n");}else{execlp("ls","ls","-l","-a",NULL);                              }   return 0;}

在這里插入圖片描述

execl 函數

這個程序可以加載自定義的函數加載一個進程, 通過 路徑+程序名 來加載。

intexecl(constchar*path,constchar*arg,...);
  1. 成功:無返回;
  2. 失敗:-1

對比 execlp,如加載"ls"命令帶有-l,-F 參數
execlp("ls","ls","-l","-F",NULL); 使用程序名在 PATH 中搜索。
execl("/bin/ls","ls","-l","-F",NULL); 使用參數 1 給出的絕對路徑搜索。

這個函數可以加載自定義的程序

被加載的程序:

#include<stdlib.h>
#include<unistd.h>
#include<stdio.h>
int main()
{while(1){sleep(1);printf("-------\n");}   return 0;
}

加載程序:

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(void)
{pid_t pid;pid=fork();if(pid==-1){   perror("fork error:");exit(1);}else if(pid>0){   sleep(1);printf("parent\n");}else{// execlp("ls","ls","-l","-h",NULL);// execl("/bin/ls","ls","-l","-h",NULL);execl("./execl_test","execl_test","NULL");                      }   return 0;
}

execvp 函數

加載一個進程,使用自定義環境變量 env intexecvp(constcharfile,constcharargv[]);
變參形式:

  1. argv[] (main 函數也是變參函數,形式上等同于 intmain(intargc,char*argv0,…))

變參終止條件

  1. NULL 結尾
  2. 固參指定 execvp 與 execlp 參數形式不同,原理一致。

將當前進程信息打印到文件中

 int dup2(int oldfd, int newfd);完成文件描述符拷貝
#include<unistd.h>
#include<fcntl.h>                                                     
#include<stdio.h>
#include<stdlib.h>int main()
{int fd; //打開文件,文件名為ps.out,只寫方式打開,文件不存在創建,存在截斷>為0.指定打開文件權限為644fd=open("ps.out",O_WRONLY|O_CREAT|O_TRUNC,0644);//返回為0為打開失敗if(fd<0){perror("open ps.out error");exit(1);}   dup2(fd,STDOUT_FILENO);//dup2(3,1);fd,stdoutexeclp("ps","ps","ax",NULL);//隱式回收,進程結束時,會把所有打開的文件都關閉掉return 0;
}

exec 函數族一般規律

exec 函數一旦調用成功即執行新的程序,不返回。只有失敗才返回,錯誤值-1。所以通常我們直接在 exec 函數 調用后直接調用 perror()和 exit(),無需 if 判斷。

  1. l (list) 命令行參數列表
  2. p (path) 搜素 file 時使用 path 變量
  3. v(vector)使用命令行參數數組
  4. e(environment) 使用環境變量數組,不使用進程原有的環境變量,設置新加載程序運行的環境變量

事實上,只有 execve 是真正的系統調用,其它五個函數最終都調用 execve,所以 execve 在 man 手冊第 2 節, 其它函數在 man 手冊第 3 節。這些函數之間的關系如下圖所示。

在這里插入圖片描述

僵尸進程和孤兒進程

孤兒進程

孤兒進程: 父進程先于子進程結束,則子進程成為孤兒進程,子進程的父進程成為 init 進程,稱為 init 進程領 養孤兒進程。
創建孤兒進程

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>int main()
{pid_t pid;pid=fork();if(pid==-1){perror("fork");exit(1);}else if(pid>0){sleep(1);printf("I am parent pid= %d,parentID= %d\n",getpid(),getppid());}else if(pid==0){printf("child pid = %d,parentID=%d \n",getpid(),getppid());          sleep(3);printf("child pid = %d,parentID=%d \n",getpid(),getppid());}   return 0;
}

在這里插入圖片描述

僵尸進程

僵尸進程: 進程終止,父進程尚未回收,子進程殘留資源(PCB)存放于內核中,變成僵尸(Zombie)進程。
特別注意,僵尸進程是不能使用 kill 命令清除掉的。因為 kill 命令只是用來終止進程的,而僵尸進程已經終止

用wait或者waitpid回收或者殺死他爸

回收子進程

wait 函數

一個進程在終止時會關閉所有文件描述符,釋放在用戶空間分配的內存,但它的 PCB 還保留著,內核在其中保 存了一些信息:
如果是正常終止則保存著退出狀態,
如果是異常終止則保存著導致該進程終止的信號是哪個。
這個 進程的父進程可以調用 wait 或 waitpid 獲取這些信息,然后徹底清除掉這個進程。
我們知道一個進程的退出狀態可 以在 Shell 中用特殊變量$?查看,因為 Shell 是它的父進程,當它終止時 Shell 調用 wait 或 waitpid 得到它的退出狀態 同時徹底清除掉這個進程。
父進程調用 wait 函數可以回收子進程終止信息。該函數有三個功能:

  1. 阻塞等待子進程退出

  2. 回收子進程殘留資源

  3. 獲取子進程結束狀態(退出原因)。

    pid_t wait(int*status); 成功:清理掉的子進程 ID;失敗:-1(沒有子進程) 
    

當進程終止時,操作系統的隱式回收機制會:1.關閉所有文件描述符 2. 釋放用戶空間分配的內存。內核的 PCB 仍存在。其中保存該進程的退出狀態。(正常終止→退出值;異常終止→終止信號)

可使用 wait 函數傳出參數 status 來保存進程的退出狀態。借助宏函數來進一步判斷進程終止的具體原因
宏函 數可分為如下三組

  1. 1

    WIFEXITED(status) 為非 0 → 進程正常結束 
    WEXITSTATUS(status) 如上宏為真,使用此宏 → 獲取進程退出狀態 (exit 的參數)` 
    

示例代碼:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>int main(void)
{pid_t pid,wpid;pid=fork();int status;if(pid==0){// pritnf("---child,my parent= %d,going to sleep 10s\n",getppid());sleep(3);printf("----------------child die--------------\n");return 100 ;                                                               }else if(pid>0){wpid=wait(&status);if(wpid==-1){perror("wait error:");exit(1);}   if(WIFEXITED(status)){printf("child exit with %d\n",WEXITSTATUS(status));}   while(1){// printf("I am parent,pid= %d,myson = %d\n",getpid(),pid);sleep(1);}   }else{perror("fork");return 1;}   return 0;
}

在這里插入圖片描述

  1. 2

     WIFSIGNALED(status) 為非 0 → 進程異常終止 WTERMSIG(status) 如上宏為真,使用此宏 → 取得使進程終止的那個信號的編號。 
    

示例代碼

int main(void)
{pid_t pid,wpid;pid=fork();int status;if(pid==0){// pritnf("---child,my parent= %d,going to sleep 10s\n",getppid());sleep(20);printf("----------------child die--------------\n");return 100 ;}else if(pid>0){wpid=wait(&status);if(wpid==-1){perror("wait error:");exit(1);}   if(WIFEXITED(status)){printf("child exit with %d\n",WEXITSTATUS(status));}   if(WIFSIGNALED(status)){printf("child killed by %d\n",WTERMSIG(status));}   while(1){// printf("I am parent,pid= %d,myson = %d\n",getpid(),pid);sleep(1);}   }else{perror("fork");return 1;}   return 0;
} 

在這里插入圖片描述
3. 3

    WIFSTOPPED(status) 為非 0 → 進程處于暫停狀態 WSTOPSIG(status) 如上宏為真,使用此宏 → 取得使進程暫停的那個信號的編號。 WIFCONTINUED(status) 為真 → 進程暫停后已經繼續運行

示例代碼

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>int main(void)
{pid_t pid,wpid;pid=fork();if(pid==0){// pritnf("---child,my parent= %d,going to sleep 10s\n",getppid());         sleep(10);printf("----------------child die--------------\n");}else if(pid>0){wpid=wait(NULL);if(wpid==-1){perror("wait error:");exit(1);}   while(1){// printf("I am parent,pid= %d,myson = %d\n",getpid(),pid);sleep(1);}   }else{perror("fork");return 1;}   return 0;
}

waitpid 函數

作用同 wait,但可指定 pid 進程清理,可以不阻塞。

pid_t waitpid(pid_t pid,int* status,in options); 成功:返回清理掉的子進程 ID;失敗:-1(無子進程)

特殊參數和返回情況:
參數 pid:

  1. >0 回收指定 ID 的子進程
  2. -1 回收任意子進程(相當于 wait)
  3. 0 回收和當前調用 waitpid 一個組的所有子進程
  4. <-1 回收指定進程組內的任意子進程
  5. 如果傳的是進程組ID,則回收全部該組內全部進程,kill也可以用

返回 0:參 3 為 WNOHANG(指定為非阻塞),且子進程正在運行。
注意:一次 wait 或 waitpid 調用只能清理一個子進程,清理多個子進程應使用循環。

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<sys/wait.h>int main(int argc,char *argv[])
{int n=5,i; //默認創建5個進程pid_t p,q;pid_t wpid;if(argc==2){n=atoi(argv[1]);}   for(i=0;i<n;i++){ //出口1,父進程專用出口p=fork();if(p==0){break;       //出口2,子進程出口,i不自增}else if(i==3){q=p;}   }   //父進程if(n==i){sleep(n);printf("I am parent,pid= %d\n",getpid(),getgid());do{ wpid=waitpid(-1,NULL,WNOHANG);//====wait(NULL);if(wpid>0){n--;}   //if wpid==0 說明子進程正在運行,sleep(1);}while(n>0);                                                               //循環回收多個子進程//while(wait(NULL));//while(1);printf("wait finish\n");//打印子進程}else{sleep(i);printf("I am %dth child,pid= %d,gpid=%d\n",i+1,getpid(),getgid());}return 0;
}

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/383246.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/383246.shtml
英文地址,請注明出處:http://en.pswp.cn/news/383246.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

交換機MAC地址學習和轉發數據幀的原理

1 &#xff1a;交換機 MAC 地址學習在交換機初始化的&#xff0c;也就是剛啟動的時候&#xff0c;交換機的MAC地址表是沒有任何MAC地址和端口的映射條目的 當PCA要想和PCC&#xff0c;PCB,PCD進行通信時&#xff0c;當該二層數據幀通過端口E1/0/1發送到交換機上時&#xff0c…

Linux系統編程---4(進程間通信IPC,管道)

進程間通信目的 數據傳輸&#xff1a;一個進程需要將它的數據發送給另一個進程資源共享&#xff1a;多個進程之間共享同樣的資源。通知事件&#xff1a;一個進程需要向另一個或一組進程發送消息&#xff0c;通知它&#xff08;它們&#xff09;發生了某種事件&#xff08;如進…

沖突域 廣播域簡單解釋

網絡互連設備可以將網絡劃分為不同的沖突域、廣播域。但是&#xff0c;由于不同的網絡互連設備可能工作在OSI模型的不同層次上。因此&#xff0c;它們劃分沖突域、廣播域的效果也就各不相同。如中繼器工作在物理層&#xff0c;網橋和交換機工作在數據鏈路層&#xff0c;路由器工…

Linux系統編程---5(共享存儲映射,存儲映射I/O,mmap函數,父子進程間通信,匿名映射)

共享存儲映射 文件進程間通信 使用文件也可以完成 IPC&#xff0c;理論依據是&#xff0c;fork 后&#xff0c;父子進程共享文件描述符。也就共享打開的文件。 編程&#xff1a;父子進程共享打開的文件。借助文件進行進程間通信。 測試代碼 /*** 父子進程共享打開的文件描述…

變量的存取

一、預備知識―程序的內存分配 一個由c/C編譯的程序占用的內存分為以下幾個部分 1、棧區&#xff08;stack&#xff09;― 由編譯器自動分配釋放 &#xff0c;存放函數的參數值&#xff0c;局部變量的值等。其操作方式類似于數據結構中的棧。 2、堆區&#xff08;heap&#xff…

Linux下文件的多進程拷貝

大文件拷貝 假設有一個超大文件&#xff0c;需對其完成拷貝工作。為提高效率&#xff0c;可采用多進程并行拷貝的方法來實現。假設文件 大小為 len&#xff0c;共有 n 個進程對該文件進行拷貝。那每個進程拷貝的字節數應為 len/n。但未必一定能整除&#xff0c;我們可 以選擇讓…

linux下cron定時任務的總結

cron是linux系統下一個自動執行指定任務的程序&#xff0c;即包含“時間”、“路徑”、“自動執行腳本”等要素 當我們要增加全局性的計劃任務時&#xff0c;一種方式是直接修改/etc/crontab。但是&#xff0c;一般不建議這樣做&#xff0c;/etc/cron.d目錄就是為了解決這種問…

Linux系統編程---6(信號的機制,信號4要素,Linu常規信號表,定時器)

信號的概念 信號在我們的生活中隨處可見&#xff0c; 如&#xff1a;古代戰爭中摔杯為號&#xff1b;現代戰爭中的信號彈&#xff1b;體育比賽中使用的信號槍… 他們都有共性&#xff1a; 簡單不能攜帶大量信息&#xff0c;只能帶一個標志。滿足某個特設條件才發送。 Unix 早…

python httplib2的安裝

window下python安裝httplib2 https://pypi.python.org/pypi/httplib2地址下下載httplib2安裝包&#xff0c;并解壓縮 方法一、我的電腦->屬性->高級->環境變量->系統變量 在系統變量里找到PATH&#xff0c;雙擊PATH&#xff0c;在結尾加上 ";C:\Python25&…

Linux系統編程----7(信號集,信號屏蔽,信號捕捉)

信號集操作函數 內核通過讀取未決信號集來判斷信號是否應被處理。信號屏蔽字 mask 可以影響未決信號集。而我們可以在應 用程序中自定義 set 來改變 mask。已達到屏蔽指定信號的目的。 信號集設定 sigset_t set; //typedef unsigned long sigset_t;int sigemptyset(sigset_t…

Linux系統編程----8(競態條件,時序競態,pause函數,如何解決時序競態)

競態條件(時序競態)&#xff1a; pause 函數 調用該函數可以造成進程主動掛起&#xff0c;等待信號喚醒。調用該系統調用的進程將處于阻塞狀態(主動放棄 cpu) 直 到有信號遞達將其喚醒&#xff0c;等不到一直等 int pause(void); 返回值&#xff1a;-1 并設置 errno 為 EINTR…

Linux系統編程---8(全局變量異步I/O,可重入函數)

全局變量異步 I/O 分析如下父子進程交替 數數 程序。當捕捉函數里面的 sleep 取消&#xff0c;程序即會出現問題。請分析原因。 #include<stdio.h> #include<signal.h> #include<unistd.h> #include<stdlib.h>intn0,flag0; void sys_err(char* s…

http使用post上傳文件時,請求頭和主體信息總結

請求頭必須配置如下行&#xff1a; Content-Type : multipart/form-data; boundary---12321 boundary---12321位文件的分界線 body如下&#xff1a; "-----12321\r\n" //分割文件時加-- "Content-Disposition: form-data; name\"…

iconv 文件編碼轉換

iconv 文件編碼轉換 http://qq164587043.blog.51cto.com/261469/63349 linux shell 配置文件中默認的字符集編碼為UTF&#xff0d;8 。UTF&#xff0d;8是unicode的一種表達方式&#xff0c;gb2312是和unicode都是字符的編碼方式&#xff0c;所以說gb2312跟utf&#xff0d;8的…

Linu系統編程---9(SIGCHLD 信號,信號傳參,中斷系統調用)

SIGCHLD 信號 SIGCHLD 的產生條件 子進程終止時子進程接收到 SIGSTOP 信號停止時子進程處在停止態&#xff0c;接受到 SIGCONT 后喚醒時 借助 SIGCHLD 信號回收子進程 子進程結束運行&#xff0c;其父進程會收到 SIGCHLD 信號。該信號的默認處理動作是忽略。可以捕捉該信號…

Linu系統編程---10(Linux的終端,線路規程,網絡終端,進程組)

終端 輸入輸出設備的總稱 在 UNIX 系統中&#xff0c;用戶通過終端登錄系統后得到一個 Shell 進程&#xff0c;這個終端成為 Shell 進程的控制終端&#xff08;Controlling Terminal&#xff09;&#xff0c; 進程中&#xff0c;控制終端是保存在 PCB 中的信息&#xff0c;而 …

PCRE函數簡介和使用示例

PCRE是一個NFA正則引擎&#xff0c;不然不能提供完全與Perl一致的正則語法功能。但它同時也實現了DFA&#xff0c;只是滿足數學意義上的正則。 PCRE提供了19個接口函數&#xff0c;為了簡單介紹&#xff0c;使用PCRE內帶的測試程序(pcretest.c)示例用法。 1. pcre_compile 原型…

Linux系統編程---11(會話,守護進程,創建守護進程)

會話 創建會話 創建一個會話需要注意以下6點注意事項 調用進程不能是進程組組長&#xff0c;該進程變成新會話首進程該進程成為一個新進程組的組長進程需要root權限&#xff08;nbuntu不需要&#xff09;新會話丟棄原有的控制終端&#xff0c;該會話沒有控制終端該調用進程是…

判斷一段文件是UTF-8編碼還是GB2312的編碼方式

分類&#xff1a; 算法 cpp2012-03-10 16:01 7120人閱讀 評論(2) 收藏 舉報null生活c對于只包含中文和英文的文本中判斷編碼方式是非常簡單的&#xff0c;中文的編碼方式最常用的是GBK&#xff0c;字符集更大的如GBK向下兼容GB2312&#xff0c;其中包含的的很多一部分字符是我們…

判斷文件的編碼方式

/*功能&#xff1a;實現文件編碼格式的判斷通過一個文件的最前面三個字節&#xff0c;可以判斷出該的編碼類型&#xff1a;ANSI&#xff1a;        無格式定義&#xff1b;(第一個字節開始就是文件內容)Unicode&#xff1a;       前兩個字節為FFFE&#xff1b;…