linux 程序的管理、子程序建立、程序資源的回收
今天介紹程序的程序的管理,子程序建立以及程序資源的回收
首先什麼是程序?
答:程序是程式的例項。程式是靜態的,是存放在硬碟上的,程式執行起來就形成了程序。程式從磁碟到記憶體裡之後就形成了程序。
程序又分為:使用者級程序和核心級程序
我們下面瞭解使用者級程序:
作業系統為了和管理程序,會有一個控制塊來記錄程序用到了哪些資源。叫做PCB塊。
每一個程序都有自己的一個ID叫做PID。
用以下命令可以檢視當前的程序
pstree 檢視程序樹 (init的PID為1)
ps -aux 檢視當前的程序表
ps -o pid,ppid,..... 檢視當前bash下的程序的資訊(資訊自己新增如PID PPID)
下面介紹一個建立子程序的函式fork()
#include<unistd.h>
pid_t fork (void)
功能:建立一個子程序
引數:無
返回值:成功時,在子程序中返回0,在父程序中返回子程序的PID (在A程序裡建立了B子程序,A就是B的父程序)。
失敗時返回- 1 errno被設定
子程序會繼承父程序的PCB裡的所有資源(他們除了PID不一樣其餘在你使用fork()函式之後的一切東西都一樣);
介紹函式exit()
#include <stdlib.h>
void exit(int status);
功能:使程序正常終止引數:指定退出的值。將status&0377 的數值傳遞給父程序
在程序終止之後,還有一部分資源滯留在系統中,我們需要回收程序所佔用的資源
使用wait()函式可以回收程序的資源
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:阻塞等待子程序的終止,子程序終止的時候回收子程序的資源,立即返回
引數:
status:用來儲存子程序狀態碼的地址,如果為NULL ,不希望保留子程序的退出狀態碼,可以使用巨集來檢測退出狀態
WIFEXITED(status):如果子程序正常終止,返回真
WEXITSTATUS(status):在上個巨集返回真的情況下才能被使用.獲取子程序的退出狀態碼的低8位
WIFSIGNALED(status):如果子程序是被一個訊號終止的返回真
WTERMSIG(status):返回終止子程序的訊號的編號,在上個巨集為真的情況下使用
返回值:成功返回種植的子程序的PID,失敗返回-1
殭屍程序:子程序已經終止但是父程序沒有回收他的資源,這時候子程序就是殭屍程序狀態
孤兒程序;:父程序結束了,子程序還沒結束。這時候子程序就叫做孤兒程序。並且他會過繼給INIT 1號程序(INIT當他的父程序)
建立的子程序和父程序預設是同組的。
下面程式碼演示:
1 #include<stdio.h>
2 #include<sys/types.h>
3 #include<unistd.h>
4 #include<stdlib.h>
5 #include<sys/wait.h>
6 int main(){
7 int a;
8 pid_t pid;
9 pid=fork();
10 if(pid==-1){
11 perror("fork");
12 return -1;
13 }
14 if(pid==0){//子程序
15 getchar();
16 exit(7);
17 }else{
18 wait(&a);
19 printf("回收成功!\n");
20 if(WIFEXITED(a))printf("son exit status=%d\n",WEXITSTATUS(a));
21 if(WIFSIGNALED(a))printf("kill signal num=%d\n",WTERMSIG(a));
22 }
23 return 0;
24 }
結果如下:
[email protected]:~/LIANXI/10.19$ gcc wait_a.c
[email protected]:~/LIANXI/10.19$ ./a.out
回收成功!
son exit status=7
在執行程式之後,由於在父程序中wait的存在,他會一直等子程序結束並回收其資源。當我們輸入任意指令跳過子程序中的getchar時,子程式結束。第21行程式碼:如果我們沒有去跳過getchar 而是被一個訊號中斷了程式 如 kill -9 pid(子程序的pid,利用ps -aux 檢視,或直接在程式中getpid)時,會列印終止我們程式的訊號編號。
下面介紹在程序執行過程中,載入新的映像(可執行檔案),來替換掉從父程序那裡繼承來的映像的函式:
函式: exec(3)系列函式
#include <unistd.h>
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[]);
功能:執行程式
引數:
共同點:都有exec
l:列表形式 const char *arg 和 char *const argv[] 的區別,有l的話需要將陣列的每個成員都寫在函式形式引數那裡。
V:向量形式 只需要寫陣列的名字即可
都需要在函式的第二個引數後面加上NULL
p:如果有p就是const char *file (這裡只需要填寫要執行的可執行程式的名字,但是PATH環境變數裡必須能找到這個可執行檔案)沒有就是const char *path 寫的時候需要加上路徑(比如./)
e:沒有e就是子程序繼承父程序的環境變數,有e就是給子程序傳遞新的環境變數
返回值:執行成功不返回,不成功返回-1
演示一個簡單的,程式碼如下:
首先這個程式碼輸出當前程序的環境變數:
1 #include<stdio.h>
2 extern char **environ;
3 int main(){
4 int i=0;
5 for(i=0;environ[i]!=NULL;i++){
6 printf("%s\n",environ[i]);
7 }
8 return 0;
9 }
下面這個程式碼演示把上面的程式替換為建立的子程序的程式
1 #include<stdio.h>
2 #include <unistd.h>
3 #include <unistd.h>
4 int main(){
5 pid_t pid=fork();
6 char *const envp[]={"caption=beijing","wo=cool",NULL};
7 if(pid==-1){
8 perror("fork");
9 return -1;
10 }
11 if(pid==0){
12 execle("./a","a",NULL,envp);
13 }else{
14 getchar();
15 }
16 return 0;
17 }
執行結果如下:
[email protected]:~/LIANXI/10.19$ gcc execl.c
[email protected]:~/LIANXI/10.19$ ./a.out
caption=beijing
wo=cool
[email protected]:~/LIANXI/10.19$
下面講一下在當前bash下執行一個程式發生了什麼?
首先什麼是bash?
bash也是一個程式,是作業系統和使用者互動的一個程序。
寫在bash這個大程式裡的小程式叫做內部程式
和bash 互相獨立的程式叫做外部程式
所以bash也有自己的環境變數
環境變數又分為自定義變數和環境變數,自定義變數只是適用於當前程序,環境變數是子程序可以繼承的環境變數。
當我們執行一個程式時,這個程式實在bash中執行的。其實是bash用fork函式建立了一個子程序,子程序複製了bash的映像,然後在子程序的映像中呼叫了exec系列的函式,將我們寫的程式的可執行檔案(映像)替換為子程序從父程序那裡複製來的映像。然後執行我們寫的程式的可執行檔案。
fork之後bash下建立了一個子程序,開始這個子程序和bash是共用一個PCB的,當子程序發生改變時,子程序複製了父程序的PCB並呼叫了exec系列函式,將你要執行的程式的程序的PCB替換掉你從父程序那裡複製來的PCB。然後執行你的程式。
以上為個人總結!如有不妥請指教!