前言:以前也知道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; }
程序很簡單再也不解釋,但要說明幾個問題,結果以下:
看到結果知道了父進程也有父進程的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; }
演示結果:
會出現最開始的問題:輸出跑到終端上。接下來解釋爲何會出現這個問題?
緣由: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; }
演示結果就不展現了,能夠本身在終端手動輸入命令,進行對照。
2.execl函數
加載一個進程, 經過 路徑+程序名 來加載。
跟execlp的主要區別在於不是經過環境變量獲取了,相對路徑也是能夠的。
上面程序註釋部分有這個程序。
3.execv函數
int execv(const char *path, char *const argv[]);
注意「v」使用命令行參數。
上面程序註釋部分有這個程序。
就不一一舉例了,有興趣能夠本身試一下。
總結:有問題,歡迎及時評論、交流與學習。