1. 程式人生 > >linux 程序的管理、子程序建立、程序資源的回收

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。然後執行你的程式。

以上為個人總結!如有不妥請指教!偷笑