exec族函數詳解及循環建立子進程

  前言:以前也知道exec族函數,但沒有徹底掌握,昨天又從新學習了一遍,基本徹底掌握了,還有一些父子進程和循環建立子進程的問題,還要介紹一下環境變量,今天分享一下。算法

  1、環境變量

  先介紹下環境的概念和特性,再舉例子吧。shell

  環境變量,是指在操做系統中用來指定操做系統運行環境的一些參數。一般具有如下特徵:編程

  ① 字符串(本質) ② 有統一的格式:名=值[:值] ③ 值用來描述進程環境信息。數組

  存儲形式:與命令行參數相似。char *[]數組,數組名environ,內部存儲字符串,NULL做爲哨兵結尾。bash

  使用形式:與命令行參數相似。ide

  引入環境變量表:須聲明環境變量。extern char ** environ;   函數

  環境變量跟不少東西有關係,例如接下來的exec族函數,這也是爲何要先介紹下環境變量的緣由,對理解exec族函數頗有幫助;例如,Linux是什麼樣的系統?多用戶多任務開源系統,每一個用戶的登陸信息環境變量都會記錄。舉例一下經常使用的環境變量:學習

  • PATH

  可執行文件的搜索路徑。ls命令也是一個程序,執行它不須要提供完整的路徑名/bin/ls,然而一般咱們執行當前目錄下的程序a.out卻須要提供完整的路徑名./a.out,這是由於PATH環境變量的值裏面包含了ls命令所在的目錄/bin,卻不包含a.out所在的目錄。PATH環境變量的值能夠包含多個目錄,用:號隔開。在Shell中用echo命令能夠查看這個環境變量的值:編碼

  $ echo $PATHurl

  • SHELL

  當前Shell,它的值一般是/bin/bash。

  • TERM

  當前終端類型,在圖形界面終端下它的值一般是xterm,終端類型決定了一些程序的輸出顯示方式,好比圖形界面終端能夠顯示漢字,而字符終端通常不行。

  • LANG

  語言和locale,決定了字符編碼以及時間、貨幣等信息的顯示格式。

  • HOME

  當前用戶主目錄的路徑,不少程序須要在主目錄下保存配置文件,使得每一個用戶在運行該程序時都有本身的一套配置

  介紹跟環境變量相關的函數:

  char *getenv(const char *name);  //獲取環境變量

  int setenv(const char *name, const char *value, int overwrite);  //添加或改變環境變量

  int unsetenv(const char *name);  //刪除

  2、fork函數及循環建立子進程

  先說一個問題,學會fork並寫程序時,可能都會遇到一個問題以下:

  

  ./a.out的輸出跑到終端上了,想過爲何?接下來我會解釋這個問題。

  1.fork函數

  原型以下:

  pid_t fork(void);

  很好理解建立一個子進程,但須要真正理解這個函數須要理解:執行一次返回兩次,就是有兩個返回值,以下:

  (1)返回子進程的pid

  (2)返回0

  2.getpid、getppid函數

  兩個函數原型,以下:

  pid_t getpid(void);  //獲取當前進程ID
  pid_t getppid(void);  //獲取父進程ID

  3.fork建立子進程

  主要建立一個子進程,並打印當前進程和父進程ID,而且打下了當前父進程的父進程ID,想一下父進程的父進程ID會是多少呢?程序以下:

  

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

int main(void) { pid_t pid; pid = fork(); if (pid == -1 ) { perror("fork"); exit(1); } else if (pid > 0) {    //parent //sleep(1); //保證子進程先執行
        printf("I'm parent pid = %d, parentID = %d\n", getpid(), getppid()); } else if (pid == 0) {    //child
        printf("child pid = %d, parentID = %d\n", getpid(), getppid()); } return 0; }
View Code

  程序很簡單再也不解釋,但要說明幾個問題,結果以下:

  

  看到結果知道了父進程也有父進程的ID,並查找一下它,是bash其實就是shell,shell經過某種方式創子進程(就是咱們程序中的父進程),而後子進程再建立子進程。

對了,還有一個問題,有一個sleep函數,註釋是:確保子進程先執行,父子進程的執行順序是由CPU的調度算法決定,但爲啥我註釋了sleep,仍是父進程先執行。說點題外話吧,APUE(unix環境高級編程)的做者作過實驗,98%的機率的都是父進程先執行。

  4.循環建立子進程

  接下來怎麼建立多個子進程,直接給正確的程序吧,先演示一下有些問題的代碼,以下:

  

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

int main(int argc, char *argv[]) { int n = 5, i;                //默認建立5個子進程

    if (argc == 2) { n = atoi(argv[1]); } for (i = 0; i < n; i++)    //出口1,父進程專用出口
        if (fork() == 0) break;            //出口2,子進程出口,i不自增

    if (n == i) { //sleep(n);
        printf("I am parent, pid = %d\n", getpid()); } else { //sleep(i);
        printf("I'm %dth child, pid = %d\n", i+1, getpid()); } return 0; }
View Code

  演示結果:

  

  會出現最開始的問題:輸出跑到終端上。接下來解釋爲何會出現這個問題?

  緣由:shell、父進程和子進程都搶奪CPU,shell當父進程執行return 0,認爲父進程執行完了,返回到終端,當子進程還沒執行完,就輸出到終端了。

  3、exec族函數 

  其實有七種以exec開頭的函數,統稱exec函數:

  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[]);

  int execve(const char *path, char *const argv[], char *const envp[]);   //真的系統調用

  主要函數紅色部分的說明:

  l (list)                           命令行參數列表

  p (path)                       搜素file時使用path變量

  v (vector)                    使用命令行參數數組

  e (environment)       使用環境變量數組,不使用進程原有的環境變量,設置新加載程序運行的環境變量

  1.execlp函數

  加載一個進程,藉助PATH環境變量  

  參數說明:

  file:文件名,經過PATH環境變量查找

  arg:命令行參數,要強掉一下,第一個arg至關於arg[0],並要以NULL結尾

  ...:可變參數

  經過調用ls來舉例:

  execlp("ls","ls","-l",NULL);

  其實,能夠試一下第二個標紅的參數,隨便寫也不會有錯誤的,說明內核並不使用第二個參數。

  程序示例以下:

  

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

int main(int argc, char *argv[]) { printf("========================\n"); char *argvv[] = {"ls", "-l", "-F", "R", "-a", NULL}; pid_t pid = fork(); if (pid == 0) { //execl("/bin/ls", "ls", "-l","-F", "-a", NULL); //execv("/bin/ls", argvv);
        execlp("ls","ls","-l","-F","-a",NULL); perror("execlp"); exit(1); } else if (pid > 0) { sleep(1); printf("parent\n"); } return 0; }
View Code

  演示結果就不展現了,能夠本身在終端手動輸入命令,進行對照。

  2.execl函數

  加載一個進程, 經過 路徑+程序名 來加載。

  跟execlp的主要區別在於不是經過環境變量獲取了,相對路徑也是能夠的。

  上面程序註釋部分有這個程序。

  3.execv函數

  int execv(const char *path, char *const argv[]);

  注意「v」使用命令行參數。

  上面程序註釋部分有這個程序。

  就不一一舉例了,有興趣能夠本身試一下。

  總結:有問題,歡迎及時評論、交流與學習。

相關文章
相關標籤/搜索