Linux下fork()&vfork()的區別、getenv()&setenv()函數以及殭屍進程、孤兒進程講解

在講解殭屍進程前,我們先來說說其它知識,因爲我們一會的代碼需要用一個叫fork()的系統調用來創建子進程,所以我們先來聊聊fork()這個系統調用。

fork()、vfork()的區別:

#include<unistd.h>

pid_t fork(void);
//pid_t爲int的類型別名
//fork()是一個比較特殊的函數,調用成功返回兩個值,調用失敗返回-1.
//那麼到底fork()調用成功是如何返回兩個值的呢?原來,fork()是用來
//創建子進程的,一但fork()調用成功,便會以父進程爲模板創建子進程(
//代碼段共享、數據段(寫時拷貝)的拷貝),父、子進程都從fork()的下一句指令
//開始執行,但父、子進程的執行順序無法確定,依賴於調度器的調度,父進程
//返回子進程的pid,子進程返回0,對於調用失敗的情況可能有以下的兩種情況,
//1)內存不足,內核沒有足夠的空間創建管理進程的數據結構(task_struct)
//2)進程數已經達到系統規定的上限
//與vfork()相關的還有一個叫做vfork()的函數,請看下面講解:

#include<unistd.h>
#include<sys/types.h>

pid_t vfork(void);
//vfork()的行爲基本類似fork(),但兩者有以下區別:
//1)fork()之後,子進程和父進程共享代碼段、數據段寫時拷貝,
//vfork()之後,代碼段、數據段均共享。
//2)fork()、之後父、子進程的調度順序依賴於調度器,vfork()之後,
//子進程先執行,在子進程調用exec()或_exit()之後父進程纔可運行,
//不調用exec()或_exit(),則出現段錯誤,在調用exec()或_exit()之前,
//依賴父進程的動作,則會出現死鎖。

殭屍進程:

所謂殭屍進程,是指那些父進程還在運行,子進程已經退出,但是父進程並沒有查看子進程的退出狀態,子進程一直以終止狀態(Z狀態)保存在進程表中等待父進程查看退出狀態,此時,子進程便爲殭屍進程。

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>


int main(){
  pid_t ret = fork();
  if(ret < 0){
    //fork失敗,打印錯誤信息
    perror("fork ");
    return 1;

  }else if(ret > 0){
    //father進程
    printf("father: pid_t = %d begin\n",getpid());//打印父進程pid
    sleep(30);//延時30s
    printf("father is end!\n");
  }else{
    //child進程
    printf("child: pid_t = %d is begin...\n",getpid());//打印子進程pid
    sleep(5);
    printf("child: is end\n");
  }
  return 0;
}

運行程序,利用ps aux | grep zombieprocess指令查看進程狀態,結果如下:
程序運行結果:
結果
子進程結束前:
前
子進程結束後:
後
注意查看第八列的狀態。

孤兒進程:

孤兒進程是指:父進程運行提前結束,子進程還在運行時,子進程便叫做孤兒進程。

#include<unistd.h>
#include<sys/types.h>
#include<stdio.h>
#include<stdlib.h>

int main(){
  pid_t ret = fork();
  if(ret < 0){ 
    perror("fork:");
    return 1;
  }else if(ret == 0){ 
    //child
    printf("child pid:%d is begin!\n",getpid());
    sleep(20);   
    printf("child father id:%d \n",getppid());
    printf("child is end! \n");
  }else{
    //father
    printf("father pid:%d is begin!\n",getpid());
    sleep(5);
    printf("father is end!\n");
  }
  return 0;
}

結果:
孤兒進程
1號進程爲init進程。

環境變量:

下面簡單的介紹關於環境變量的指令與函數。
echo $env_name:此命令查看相應環境變量的值。env_name爲環境變量的名字。
export env_name="env_value":此命令創建一個新的環境變量。env_name爲環境變量名,env_value爲環境變量的值。
env:查看所有環境變量。
後面再介紹關於環境變量的兩個函數getenv()、setenv():
char *getenv(const char *name);
此函數獲得相應環境變量的值,參數爲環境變量的字符串,成功返回對應變量的值字符串,失敗返回空指針。

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

int main(){
  //char *getenv(const char *name);
  char *env = getenv("PATH");
  if(env){
    printf("%s\n",env);
  }
  return 0;
}

輸出結果:

[DELL@MiWiFi-R1CL-srv class_code]$ ./environ
/usr/java/jdk/bin:/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin:/home/DELL/.local/bin:/home/DELL/bin:./
[DELL@MiWiFi-R1CL-srv class_code]$

int setenv(const char *name, const char *value, int overwrite);
此函數用於創建環境變量或改變環境變量的值,name爲所要改變或創建環境變量的值,value爲環境變量的值,overwrite爲標誌位,當環境變量不存在時,無論overwrite爲何值創建環境變量並以value賦值,當環境變量存在時,若overwrite非0時改變環境變量的值,當overwrite爲0時忽略value的值,函數調用成功返回0,失敗返回-1。

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

int main(){

  //int setenv(const char *name, const char *value, int overwrite);
  int ret = setenv("MYENV","hello world",1);
  if(ret == 0){ 
    printf("MYENV:");
    char *env = getenv("MYENV");
    if(env){
      printf("%s\n",env);
    }else{
      perror("getenv:");
    }   
  }else{
    perror("setenv:");
  }
  return 0;
}

輸出結果:

[DELL@MiWiFi-R1CL-srv class_code]$ ./environ 
MYENV:hello world
[DELL@MiWiFi-R1CL-srv class_code]$ 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章