天天看點

程序與程式(fork、vfork的使用、exit)程序與程式Linux/UNIX系統檢視程序程序的正常退出程序的異常終止回收子程序

程序與程式

​ 程式就是存儲在磁盤上的可執行指令、資料的靜态實體。

​ 程序就是被作業系統讀取、加載到記憶體中的正在執行的程式。

程序的分類

​ **守護程序:**由系統在開機時通過啟動腳本激活的,總處于活動狀态,一般運作在背景維護系統的正常運作,程序号為1

​ **批處理程序:**系統會每隔一段時間集中處理該類程序的相關指令,會有一定的延時,但避免了狀态切換的耗時是以執行效率提高。

​ **互動程序:**在執行時需要使用者輸入一些資料否則無法進行

Linux/UNIX系統檢視程序

​ 簡答形式:ps 以簡略方式顯示目前目前終端有控制權的程序

​ 詳細顯示:ps auwx

​ a 所有使用者的目前終端有控制權的程序

​ u 顯示詳細資訊

​ x 包含無控制權的程序

​ w 以更高大的列寬顯示

程序的資訊清單

USER		程序屬主 
PID		   程序IP
%CPU		CPU使用率
%MEM		記憶體使用率
VSZ			使用虛拟記憶體的大小(KB)
RSS			使用實體記憶體的大小(KB)
TTY			終端裝置号,如果是?表示無終端控制
STAT		程序的狀态
	O	就緒态,所有準備工作都已經完成,等待被系統進行。
	R	運作狀态的程序,Linux系統中沒有就緒态,就緒态也用R表示
	S	可被喚醒的睡眠,當收到系統終端、擷取到資源、收到信号都可以使程序喚醒轉入運作狀态
	D	不可以喚醒的睡眠,隻能被系統調用wake_up喚醒
	T	暫停,收到信号SIGSTOP程序将轉為暫停狀态,收到SIGCONT信号轉為進行狀态
	Z	僵屍狀态,程序已經結束運作,但其父程序還沒有回收它的資源
	
	s	會話首程序
	l	多線程程序
	<	高優先級程序
	n	低優先級程序
	+	前台程序
START		程序的開始時間
TIME			程序的運作時間
COMMAND	啟動程序的程式
           

父程序和子程序

​ 1、一個程序可以建立另一個程序,建立者叫父程序,被建立者叫子程序。

​ 2、父程序啟動子程序後,子程序會在作業系統的高度下與父程序同時執行。

​ 3、子程序先于父程序結束時,會向父程序發送SIGCHLD信号,父程序接收到SIGCHLD信号後就應該回收子程序的相關資源,如果父程序沒有及時回收子程序,則子程序會變為僵屍程序。

​ 4、父程序先于子程序結束,子程序就會變成孤兒程序,同時會被init程序收養,即成為init的子程序,而init程序也叫孤兒院。

程序辨別符

​ 1、每個程序都會有一個以非負數表示的唯一的辨別,也就是程序ID,相當于程序的身份證号。

​ 2、程序在任何時候都是唯一的,但可以重用。當一個程序結束時,它的程序ID就可以被其他程序使用(延遲重用)。

pid_t getpid(void);
功能:擷取目前程序的程序ID
    
pid_t getppid(void);
功能:擷取目前程序的父程序ID
           

fork建立程序

pid_t fork(void);
功能:建立一個子程序
傳回值:
    成功則傳回兩次,子程序傳回0,父程序傳回子程序ID。
    失敗則傳回一次	-1
           

1、通過fork建立的子程序會拷貝父程序的代碼段、資料段、靜态資料段、堆、棧、IO緩沖區

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void func()
{
    printf("我是函數%s\n",__func__);
}
int num=0;
int main()
{
    static int num_s;
    int* p = malloc(4);
    pid_t pid = fork();
    printf("haha\n");
    if(0 == pid)
    {   
        func();
        *p = 1234;
        num = 1234;
        num_s = 1234;
        printf("我是子程序%u %p %p %p\n",pid,p,&num,&num_s);
    }   
    else
    {   
        func();
        *p = 4567;
        num = 4567;
        num_s = 4567;
        printf("我是父程序%u %p %p %p\n",pid,p,&num,&num_s);
    }   
}
           

2、程序建立完成後,父子程序各自獨立進行,先後順序不确定

3、當程序的總數超過系統限制,fork将建立失敗

4、通過fork建立的子程序會繼承父程序的信号處理方式

vfork建立程序

pid_t vfork(void);
功能:建立子程序
傳回值:
	失敗傳回 -1
    成功傳回兩次,子程序傳回0,父程序傳回子程序ID
           

差別:

vfork建立的子程序不會複制父程序的代碼段資源,而是通過exec系列函數直接加載一個可執行檔案啟動子程序

子程序建立成功前,子程序暫時借用父程序的相關資源來加載子程序,而此時的父程序是阻塞狀态,隻有子程序建立成功後,父程序才繼續運作。

​ 通過vfork建立的子程序,不會繼承父程序的信号處理方式

int execl(const char *path, const char *arg, ...);
path:可執行檔案的路徑
arg:指令行參數,至少一個,且以NULL結尾
    
int execlp(const char *file, const char *arg, ...);
file:可執行檔案名,該檔案必須存儲在path環境變量的路徑下
arg:指令行參數,至少一個,且以NULL結尾
    
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
argv:存儲指令行參數的字元串數組,最後一個必須是NULL
           

system建立程序

int system(const char *command);
功能:加載一個可執行程式建立子程序
傳回值:成功傳回0,失敗傳回-1
差別:子程序執行時,父程序阻塞,子程序執行結束後父程序才繼續
           

程序的正常退出

1、從main函數return

2、調用标準C函數exit

​ 無論在任何時候的任何位置調用此函數的程序會立即結束

void exit(int status);
功能:結束目前程序,該函數一旦執行,不會傳回
status:程序的結束狀态碼,與main函數中的傳回值一樣,反映了程序是如何結束的,父程序調用相關函數可以擷取status的低8位,常用的參數:EXIT_SUCCESS,EXIT_FAILURE
           

​ 程序在執行結束前會先完成以下事情:

先執行atexit/on_exit注冊的函數,然後再調用_exit/_Exit函數

int atexit(void (*function)(void));
int on_exit(void (*function)(int , void *), void *arg);
功能:向核心注冊一個函數,程序結束時自動執行被注冊的函數

void (*function)(int , void *)
int:exit函數的參數或者return傳回的值
void*:on_exit注冊時的第二個參數arg
           

3、調用_exit/_Exit函數

void _exit(int status)
功能:Linux系統提供的程序結束函數
    
void _Exit(int status)
功能:标準庫提供的等價于_exit的程序結束函數,目的是為了提高代碼的相容性
           

1、status可以是exit函數的參數,也可以是使用者提供的,父程序調用相關函數可以擷取status的低8位

2、沖刷所有檔案的緩沖區,關閉所有檔案

3、把子程序托付給init程序收養

4、向父程序發送SIGCHLD信号

4、程序的最後一個線程執行了傳回語句

5、程序的最後一個線程調用了pthread_exit函數

程序的異常終止

​ 1、程序接收到某些信号(他殺)

​ 2、調用abort函數(自殺)

​ 3、最後一個線程對"取消"請求做出響應

回收子程序

pid_t wait(int *status);
功能:等待所有子程序結束,回收子程序,如果所有子程序都在運作,則父程序阻塞
傳回值:
    如果沒有子程序則傳回錯誤ECHILD
    隻要有一個子程序結束,則傳回子程序的程序ID和狀态碼
    
pid_t waitpid(pid_t pid, int *status, int options);
功能:等待指定的子程序結束,回收子程序
pid: 
    < -1	等待程序組号為abs(pid)的程序結束
    = -1	等待任意子程序結束
    =  0	等待組程序ID為目前程序ID的程序結束
    >  0	等待程序ID為pid的程序結束
options:
	WNOHANG 如果沒有子程序則立即傳回
	WUNTRACED	子程序停止立即傳回
    WCONTINUED	子程序由暫停狀态轉為繼續狀态并傳回
傳回值:
    如果沒有子程序則傳回錯誤ECHILD
    隻要有一個子程序結束,則傳回子程序的程序ID和狀态碼
解析status的宏
	WIFEXITED(status)	檢查程序是否正常退出
	WEXITSTATUS(status)	擷取status的低八位,但在還有結果為真時才有意義
	WIFSIGNALED(status)	檢查程序是否被信号終止
	WTERMSIG(status)	擷取導緻程序終止的信号,隻有WIFSIGNALED為真時才有意義
	WCOREDUMP(status)	檢查程序是否由于核心轉儲結束(段錯誤),隻有WIFSIGNALED為真時才有意義
	WIFSTOPPED(status)	檢查程序是否為暫停狀态
	WSTOPSIG(status)	擷取導緻程序暫停的信号,隻有WIFSTOPPED為真時才有意義
	WIFCONTINUED(status)當程序由暫停轉換為繼續運作時傳回真
           

當父程序收到子程序的SIGCHLD信号時,就應該調用wait/waitpid函數回收子程序。

如果子程序已經結束,在調用wai/waited函數之前子程序處于僵屍狀态,調用後子程序會立即消失。

如果不關心子程序結束狀态,status的參數可以為NULL。

繼續閱讀