1. 程式人生 > >Linux C程式設計--程序介紹3--程序終止和等待

Linux C程式設計--程序介紹3--程序終止和等待

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow

也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!

               

程序結束

1.在Linux中任何讓一個程序結束

程序退出表示程序即將結束。在Linux中程序退出分為了正常退出和異常退出兩種。

1>正常退出

a. 在main()函式中執行return 。

b.呼叫exit()函式

c.呼叫_exit()函式

2>異常退出

a.呼叫about函式

b.程序收到某個訊號,而該訊號使程式終止。

不管 是哪種退出方式,系統最終都會執行核心中的同一程式碼。這段程式碼用來關閉程序所用已開啟的檔案描述符,釋放它所佔用的記憶體和其他資源。

 

 

3.比較以上幾種退出方式的不同點

(1)exit和return 的區別:

a.exit是一個函式,有引數。exit執行完後把控制權交給系統

b.return是函式執行完後的返回。renturn執行完後把控制權交給呼叫函式。

(2)exit和abort的區別:

a.exit是正常終止程序

b.about是異常終止。


exit()和_exit()函式

exit()和_exit()的學習

1>exit和_exit函式都是用來終止程序的。

當程式執行到exit或_exit時,系統無條件的停止剩下所有操作,清除包括PCB在內的各種資料結構,並終止本程序的執行。

2>exit在標頭檔案stdlib.h中宣告,而_exit()宣告在標頭檔案unistd.h中宣告。 exit中的引數exit_code為0代表程序正常終止,若為其他值表示程式執行過程中有錯誤發生。

3>exit()和_exit()的區別: 

a._exit()執行後立即返回給核心,而exit()要先執行一些清除操作,然後將控制權交給核心。

b. 呼叫_exit函式時,其會關閉程序所有的檔案描述符,清理記憶體以及其他一些核心清理函式,但不會重新整理流(stdin, stdout, stderr  ...).   exit函式是在_exit函式之上的一個封裝,其會呼叫_exit,並在呼叫之前先重新整理流。


exit()函式與_exit()函式最大區別就在於exit()函式在呼叫exit系統之前要檢查檔案的開啟情況,把檔案緩衝區的內容寫回檔案。由於Linux的標準函式庫中,有一種被稱作“緩衝I/O”的操作,其特徵就是對應每一個開啟的檔案,在記憶體中都有一片緩衝區。每次讀檔案時,會連續的讀出若干條記錄,這樣在下次讀檔案時就可以直接從記憶體的緩衝區讀取;同樣,每次寫檔案的時候也僅僅是寫入記憶體的緩衝區,等滿足了一定的條件(如達到了一定數量或遇到特定字元等),再將緩衝區中的內容一次性寫入檔案。這種技術大大增加了檔案讀寫的速度,但也給程式設計代來了一點兒麻煩。比如有一些資料,認為已經寫入了檔案,實際上因為沒有滿足特定的條件,它們還只是儲存在緩衝區內,這時用_exit()函式直接將程序關閉,緩衝區的資料就會丟失。因此,要想保證資料的完整性,就一定要使用exit()函式。


幾個要點: 1.不管程序如何終止,最後都會執行核心中的同一段程式碼:為相應程序關閉所有開啟描述符,釋放記憶體等等。 2.若父程序在子程序之前終止了,則子程序的父程序將變為init程序,其PID為1保證每個程序都有父程序。 3.當子程序先終止,父程序如何知道子程序的終止狀態?事實上,核心為每個終止子程序儲存了終止狀態等資訊,父程序呼叫wait等函式,就可獲取該資訊。 4.當父程序呼叫wait等函式後,核心將釋放終止程序所使用的所有記憶體,關閉其開啟的所有檔案。 5.對於已經終止、但是其父程序尚未對其呼叫wait等函式的程序,被稱為殭屍程序(即已經結束,但尚未釋放資源的)。 6.對於父程序先終止,而被init領養的程序會是殭屍程序嗎?init對每個終止的子程序,都會呼叫wait函式,獲取其終止狀態資訊。 綜上所述,子程序呼叫exit後,父程序必須呼叫wait。

程序等待

系統中的殭屍程序都要由wait系統呼叫來回收,下面就通過實戰看一看wait的具體用法:

wait的函式原型是:

#include <sys/types.h> /* 提供型別pid_t的定義 */

#include <sys/wait.h>

pid_t wait(int *status);

程序一旦呼叫了wait,就立即阻塞自己,由wait自動分析是否當前程序的某個子程序已經退出,如果讓它找到了這樣一個已經變成殭屍的子程序, wait就會收集這個子程序的資訊,並把它徹底銷燬後返回;如果沒有找到這樣一個子程序,wait就會一直阻塞在這裡,直到有一個出現為止。

引數status用來儲存被收集程序退出時的一些狀態,它是一個指向int型別的指標。但如果我們對這個子程序是如何死掉的毫不在意,只想把這個殭屍程序消滅掉,(事實上絕大多數情況下,我們都會這樣想),我們就可以設定這個引數為NULL,就象下面這樣:

pid = wait(NULL);

如果成功,wait會返回被收集的子程序的程序ID,如果呼叫程序沒有子程序,呼叫就會失敗,此時wait返回-1,同時errno被置為ECHILD。


下面給出兩個示例

1.不同型別結束程序的狀態字示例

#include <sys/types.h>#include <sys/wait.h>#include <stdio.h>/* 處理並列印狀態字的子函式*/void h_exit(int status){  if(WIFEXITED(status))   printf("normal termination, exit status=%d \n", WEXITSTATUS(status));  else if(WIFSIGNALED(status))  {  printf("abnormal termination, signal number =%d %s\n", WTERMSIG(status),  #ifdef WCOREDUMP   WCOREDUMP(status) ? " )" : "(core file generated)");  #else   ") ");  #endif  }} /*主函式。示範三種結束程序的不同方式,並呼叫h_exit函式處理返回狀態字*/int main(){  pid_t pid;  int status;  /*子程式正常退出 */  if((pid=fork())<0)  {   printf("fork error \n");   exit(0);  }  else if (pid==0)   exit(7);  if(wait(&status)!=pid)  /*等待子程序*/  {   printf("wait error \n");   exit(0);  }  h_exit(status);   /*列印狀態 */  /*子程序abort終止 */  if((pid=fork())<0)  {   printf("fork errof\n");   exit(0);  }  else if(pid==0)   /*子程序*/   abort();   /*產生訊號SIGABRT終止程序*/  if(wait(&status)!=pid)  /*等待子程序*/  {   printf("wait error.\n");   exit(0);  }  h_exit(status);   /*列印狀態*/  /* 子程序除零終止 */  if((pid=fork())<0)  {   printf("fork errof\n");   exit(0);  }  else if(pid==0)   /*子程序 */   status /=0;   /*除數為0產生SIGFPE */  if(wait(&status)!=pid)  {   printf("wait error.\n");   exit(0);  }  h_exit(status);   /*列印狀態*/  exit(0);}

2.waitpid的使用

#include <sys/types.h>#include <sys/wait.h>#include <stdio.h>int main()pid_t pid; if((pid=fork())<0) {  printf("fork error.\n");  exit(0); } else if(pid==0) {  if((pid=fork())<0)  {   printf("fork error.\n");   exit(0);  }  else if(pid>0)   exit(0);  sleep(2);  printf("second child, parent pid=%d \n", getppid());  exit(0); } if(waitpid(pid, NULL, 0)!=pid) {  printf("waitpid error.\n");  exit(0); } exit(0);}

說明:

WIFEXITED(status)如果子程序正常結束則為非0 值。

WEXITSTATUS(status)取得子程序exit()返回的結束程式碼,一般會先用WIFEXITED 來判斷是否正常結束才能使用此巨集。


WIFSIGNALED(status)如果子程序是因為訊號而結束則此巨集值為真
WTERMSIG(status) 取得子程序因訊號而中止的訊號程式碼,一般會先用WIFSIGNALED 來判斷後才使用此巨集。


WIFSTOPPED(status) 如果子程序處於暫停執行情況則此巨集值為真。一般只有使用WUNTRACED 時才會有此情況。
WSTOPSIG(status) 取得引發子程序暫停的訊號程式碼,一般會先用WIFSTOPPED 來判斷後才使用此巨集。


wait函式

wait函式的原型為:pid_t wait(int *status)

  當程序退出時,它向父程序傳送一個SIGCHLD訊號,預設情況下總是忽略SIGCHLD訊號,此時程序狀態一直保留在記憶體中,直到父程序使用wait函式收集狀態資訊,才會清空這些資訊.

  用wait來等待一個子程序終止執行稱為回收程序.

  當父程序忘了用wait()函式等待已終止的子程序時,子程序就會進入一種無父程序的狀態,此時子程序就是殭屍程序.

  wait()要與fork()配套出現,如果在使用fork()之前呼叫wait(),wait()的返回值則為-1,正常情況下wait()的返回值為子程序的PID.

如果先終止父程序,子程序將繼續正常進行,只是它將由init程序(PID 1)繼承,當子程序終止時,init程序捕獲這個狀態.


waitpid函式

1)waitpid的概述:

  .waitpid函式的原型為pid_t waitpid(pid_t pid,int *status,int options)

  .從本質上講,系統呼叫waitpid是wait的封裝,waitpid只是多出了兩個可由使用者控制的引數pid和options,為程式設計提供了靈活性.

  2)waitpid的引數說明:

  引數pid的值有以下幾種型別:

  pid>0時,只等待程序ID等於pid的子程序,不管其它已經有多少子程序執行結束退出了,只要指定的子程序還沒有結束,waitpid就會一直等下去.

  pid=-1時,等待任何一個子程序退出,沒有任何限制,此時waitpid和wait的作用一模一樣.

  pid=0時,等待同一個程序組中的任何子程序,如果子程序已經加入了別的程序組,waitpid不會對它做任何理睬.

  pid<-1時,等待一個指定程序組中的任何子程序,這個程序組的ID等於pid的絕對值.

  引數options的值有以下幾種型別:

  如果使用了WNOHANG引數,即使沒有子程序退出,它也會立即返回,不會像wait那樣永遠等下去.

  如果使用了WUNTRACED引數,則子程序進入暫停則馬上返回,但結束狀態不予以理會.

  Linux中只支援WNOHANG和WUNTRACED兩個選項,這是兩個常數,可以用"|"運算子把它們連線起來使用,比如:

  ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);

  如果我們不想使用它們,也可以把options設為0,如:ret=waitpid(-1,NULL,0);

  waitpid的返回值比wait稍微複雜一些,一共有3種情況:

3)waitpid的返回值:

  當正常返回的時候waitpid返回收集到的子程序的程序ID;

  如果設定了選項WNOHANG,而呼叫中waitpid發現沒有已退出的子程序可收集,則返回0;

  如果呼叫中出錯,則返回-1,這時errno會被設定成相應的值以指示錯誤所在;

  當pid所指示的子程序不存在,或此程序存在,但不是呼叫程序的子程序,waitpid就會出錯返回,這時errno被設定為ECHILD.


           

給我老師的人工智慧教程打call!http://blog.csdn.net/jiangjunshow

這裡寫圖片描述