進程的幾種終止方式(Termination)
(1)正常退出
從main函數返回[return]
調用exit
調用_exit或者_Exit
最後一個線程從其啟動處返回
從最後一個線程調用pthread_exit (最後兩點見後面博客)
(2)異常退出
調用abort 產生SIGABOUT信號
由信號終止 Ctrl+C [SIGINT]
最後一個線程對取消請求做出響應
從圖中可以看出,_exit 函數的作用是:直接使進程停止運行,清除其使用的內存空間,並清除其在內核的各種數據結構;exit 函數則在這些基礎上做了一些小動作,在執行退出之前還加了若干道工序。exit() 函數與 _exit() 函數的最大區別在於exit()函數在調用exit 系統調用前要檢查文件的打開情況,把文件緩沖區中的內容寫回文件。也就是圖中的“清理I/O緩沖”。另外注意_exit是一個系統調用,exit是一個c庫函數。
int main()
{
pid_t result;
result=fork();
if(result<0)
ERR_EXIT("fork error");
if(result==0)
{
printf("This is the _exit test.Child pid=%d\n",getpid());
printf("Output the content in Child!");
_exit(0);
}
else
{
printf("This is the exit test.Parent pid=%d\n",getpid());
printf("Output the content in Parent!");
exit(0);
}
return 0;
}
結果分析:
子進程中運行_exit(0)並未將This is the content in Child打印出來,而父進程中運行的exit(0)將This is the content in Parent打印出來了。說明,exit(0)會在終止進程前,將緩沖I/O內容清理掉,所以即使printf裡面沒有 \n也會被打印出來,而_exit(0)是直接終止進程,並未將緩沖I/O內容清理掉,所以不會被打印出來。
終止處理程序:atexit
按照ISO C的規定,一個進程可以登記多至32個函數,這些函數將由exit自動調用,我們稱這些函數為終止處理函數,並調用atexit函數來登記這些函數
void exitHa1()
{
printf("when exit,the num is one\n");
}
void exitHa2()
{
printf("when exit,the num is two\n");
}
int main()
{
printf("In main ,pid=%d\n",getpid());
atexit(exitHa1);
atexit(exitHa2);
return 0;
}
我們可以看出的是:先注冊的後執行。
如果將return -0替換成abort()異常退出的話,那麼程序運行的結果如上圖。
exec函數族
exec替換進程印象
在進程的創建上,Unix采用了一個獨特的方法,它將進程創建與加載一個新進程映象分離。這樣的好處是有更多的余地對兩種操作進行管理。
當我們創建了一個進程之後,通常將子進程替換成新的進程映象,這可以用exec系列的函數來進行。當然,exec系列的函數也可以將當前進程替換掉。
exec只是用磁盤上的一個新程序替換了當前進程的正文段, 數據段, 堆段和棧段.並沒有創建新進程,所以進程的ID是不變的。
exec函數族:
int execve(const char *filename, char *const argv[],
char *const envp[]);
#include
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
說明:
execl,execlp,execle(都帶“l”, 代表list)的參數個數是可變的,參數以必須一個空指針結束。
execv和execvp的第二個參數是一個字符串數組(“v”代表“vector”,字符串數組必須以NULL結尾),新程序在啟動時會把在argv數組中給定的參數傳遞到main。
名字最後一個字母是“p”的函數會搜索PATH環境變量去查找新程序的可執行文件。如果可執行文件不在PATH定義的路徑上,就必須把包括子目錄在內的絕對文件名做為一個參數傳遞給這些函數;
/*總結:l代表可變參數列表,與v互斥;v表示該函數取一個argv[]矢量;p代表在path環境變量中搜索file文件;e表示該函數取envp[]數組,而不使用當前環境*/
int main()
{
printf("Test the execlp:\n");
pid_t pid=fork();
if(pid==0)
{
if(execlp("/bin/pwd","pwd",NULL)==-1)
ERR_EXIT("execlp pwd error");
}
wait(NULL);
pid=fork();
if(pid==0)
{
if(execlp("/bin/ls","-l",NULL)==-1)
ERR_EXIT("execlp ls -l error");
}
wait(NULL);
printf("Test the execve:\n");
char *argv_execve[]={"/bin/data","+%F",NULL};
pid=fork();
if(pid==0)
{
if(execve("/bin/date",argv_execve,NULL)==-1)
ERR_EXIT("execve error");
}
wait(NULL);
return 0;
}
另外,如果你理解execv, 那麼execle和他的區別就是, 前者的調用參數是以數組形式給的,而後者則是以列表方式給,也就是execle(path, arg1, arg2, ..., envp), 並且提供了環境變量參數;
#include
#include
#include
int main()
{
char *envp[]={"PATH=/tmp","USER=shan",NULL};
if(fork()==0)
{
if(execle("/bin/dir","dir",NULL,envp)<0)
perror("execle error!");
}
return 0;
}
System系統調用
system()函數調用“/bin/sh -c command”執行特定的命令,阻塞當前進程直到command命令執行完畢,system函數執行時,會調用fork、execve、waitpid等函數。
原型:
[cpp] view plaincopy
int system(const char *command);自己利用execl實現system: