1. 程式人生 > >【linux】程序建立、等待、終止

【linux】程序建立、等待、終止

建立程序fork函式
函式原型:pid_t fork(void);
返回值:子程序返回0;父程序返回子程序pid,失敗返回-1(失敗的原因:1記憶體不夠;2系統程序數量太多);
注意:fork之前,父程序獨立執行,fork之後,父子兩個執行流分別執行。但是父子程序誰先執行是由系統排程決定。
寫實拷貝:父程序建立子程序後,子程序室父程序的副本;父子程式碼共享,父子不寫入時,資料也是共享。當任意一方試圖寫入時,便以寫實拷貝的方式各自建立一份副本。
這裡寫圖片描述
程式碼:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3
4 int main() 5 { 6 pid_t pid = fork(); 7 if(pid>0){ 8 sleep(3); 9 printf("I am father:%d\n",getpid()); 10 }else if(pid==0){ 11 printf("I am child:%d\n",getpid()); 12 sleep(1); 13
}else{ 14 perror("fork"); 15 } 16 return 0; 17 }

這裡寫圖片描述
vfork函式:也是建立程序,用法跟fork相似;
不同的是:
1、vfork一個子程序,父子程序共享地址空間,而fork的子程序具有獨立的地址空間。
2、vfork保證子程序先執行,直到呼叫exec或者exit之後父程序才執行。

程式碼:

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<stdlib.h>
  4 int
flag = 100;//設定一個全域性變數100 5 int main() 6 { 7 pid_t pid = vfork(); 8 if(pid>0){ 9 sleep(2); 10 flag = 10;//父程序把flag變為1011 printf("father:%d;flag = %d\n",getpid(),flag); 12 exit(0); 13 }else if(pid==0){ 14 flag = 0; //子程序把flag變為0printf("child:%d,father_id=%d,flag=%d\n",getpid(),getppid(),flag); 16 sleep(5); 17 }else{ 18 perror("fork"); 19 } 20 return 0; 21 }

結果:
這裡寫圖片描述
我們看到子程序改變了父程序的的值,因為子程序實在父程序的地址空間執行。

程序等待wait和waitpid
程序等待必要性:
1、子程序退出,如果父程序不管不顧,就可能造成殭屍程序的問題,造成記憶體洩露。
2、父程序需要知道子程序的退出狀態,完成的任務結果如何。
3、父程序通過程序等待的方式,回收子程序資源,獲取子程序退出狀態。
函式原型:pid_t wait(int *status)
返回值:成功返回被等待程序的pid;失敗返回-1;
引數:輸出型引數,獲取子程序的退出狀態,不關心可以設定為NULL;
status&0x7f==0:表示程式碼執行完了,正常退出;
status&0x7f!=0:表示異常退出,具體值表示使該程序退出的訊號的編碼。
status>>8 & 0xff的值表示子程序的退出碼。
呼叫wait函式會發生什麼?
1. 如果有子程序在執行,父程序進入阻塞狀態(可以理解為什麼事情都沒有做,一直在檢測子程序是否在執行)
2.如果父程序在呼叫wait前子程序已經終止,wait可立即獲得子程序的終止狀態(退出碼,退出資訊),子程序的終止狀態是體現在status引數上的,另外wait還會返回所終止的子程序的識別符號。
3.如果當前沒有子程序,則會出錯返回-1。
4.如果有一個子程序終止,那麼wait便返回。
這裡寫圖片描述

  1 #include<stdio.h>
  2 #include<unistd.h>
  3 #include<sys/types.h>
  4 #include<sys/wait.h>
  5  
  6 int main()
  7 {
  8     int ret=fork();
  9     if(ret>0){
 10         int status=0;
 11         int wait_id = wait(&status);
 12         if(wait_id>0){
 13             printf("child has success dead!,id=%d\n",wait_id);
 14         }else{
 15             printf("child has false dead!,%d\n",wait_id);
 16         }
 17     }else if(ret==0){
 18         printf("I am child,id=%d\n",getpid());
 19         sleep(5);
 20         exit(0);
 21     }else{
 22         perror("fork");
 23     }
 24     return 0;
 25 }     

這裡寫圖片描述

waitpid函式:也是建立程序,用法跟wait相似;
函式原型:pid_t waitpid(pid_t pid,int *status,int options)
pid:檢測子程序的pid
status:子程序的終止狀態,如果不是空指標,則終止程序的終止資訊就存放在它所指向的單元內。不關心終止狀態可以將status製成NULL。
options:指定引數,預設情況下waitpid與wait做的事情都是一樣的,為阻塞式監測子程序終止狀態;當options=WNOHANG時,此時為非阻塞方式,就是監測時如果子程序沒有終止,呼叫者可以做其他事情,另外還有兩個引數分別為WCONTINUED和WUNTRACED,這兩個引數是跟作業控制有關的。
這裡寫圖片描述
結果:
這裡寫圖片描述
非阻塞等待:
這裡寫圖片描述
結果:
這裡寫圖片描述
WNOHANHG:若子程序沒有結果,waipid返回0,不予等待,若正常結束,則返回該子程序的pid。
WIFEXITED(status):表示若為正常終止子程序返回的狀態,則為真(檢視程序是否是正常退出),這個巨集用來指出子程序是否為正常退出的,如果是,它會返回一個非零值;
WEXITSTATUS(status):若WIFEXITED非零,提取子程序的退出碼(檢視程序退出碼)。

程序終止_exit()和exit()
檢視程序退出碼: echo $?

#include<unistd.h>
void _exit(int status)
引數:status定義了程式的終止狀態,父程序通過wait獲取該值。

#include<unistd.h>
void exit(int statsu)

_exit()系統呼叫 exit() 庫函式
exit()做的三件事:
1,執行使用者通過atexit或者on_exit定義的清理函式
2,關閉所有開啟流,所有的快取資料均被寫入。
3,呼叫_exit;

思考題:vfork建立的子程序, 直接return為什麼會出現崩潰?
結束子程序的呼叫是exit()而不是return,如果你在vfork中return了,那麼,這就意味main()函式return了,注意因為函式棧父子程序共享,所以整個程式的棧就出現問題了。