環境變量概念
我們用語言寫的文件編好后變成了程序,./ 運行的時候他就會變成一個進程被操作系統調度并運行,運行完畢進程相關資源被釋放,因為它是一個bash的子進程,所以它退出之后進入僵尸狀態,bash回收他的退出結果,我們就能夠得到他的退出結果,這是我們之前說的,現在的問題是我寫的可執行程序和系統自帶的指令有什么區別呢?答案是沒有任何區別,只不過人家寫的軟件被納入到Linux基本指令的范疇,你寫的只能你自己玩,本質上都叫做可執行程序,為什么運行我自己寫的程序必須要帶 ./ 呢,如果想讓我的程序不帶 ./ 該怎么解決呢?
這里就可以找到比較好的切入點,. 就是當前路徑,/ 是路徑分隔符,所以 ./myproc 是我們在用相對路徑的方式來定位可執行程序,換句話說,如果你愿意,你也可以從根目錄開始,/home/cyx/day12/myproc,用絕對路徑的方式訪問自己的程序
ls可以不帶 ./ 就能運行的原因在于,默認的程序在系統當中,會存在一個環境變量,它能夠幫我們通過該變量在系統中特定的路徑下幫我們搜索對應的ls命令,執行一條命令的前提,一定是要先找到他,找不到還執行啥呢?系統中存在的環境變量叫做PATH,讀取環境變量的內容(echo $PATH)
綜合剛剛所說為什么執行pwd、ls這些命令可以不帶路徑,原因是因為它們在特定的環境變量所指明的若干路徑當中,Shell會從PATH環境變量(一串用冒號分隔的目錄路徑)中從左到右依次搜索這些目錄,尋找與命令名稱匹配的可執行文件,找到就自動執行了,不用帶路徑,而我們自己寫的可執行程序對應的路徑是自己的路徑,沒有在環境變量里,所以執行時無法直接找到我們的可執行程序,必須要用戶指明出他對應的路徑
上圖可以證明,(which ls)可以看到他在環境變量/usr/bin/路徑下,他就不用帶 ./ 運行了,換言之,我們想不用帶 ./ 就能運行也可以實現,在PATH環境變量所支持的眾多:分隔的一個個路徑當中,把你的路徑追加到后面(export PATH=$PATH:/home/cyx/day12)
- 注意可能會寫出這樣的命令(export PATH=/home/cyx/day12),?這種方式是不對的,不是把PATH是整體全部換成自己的路徑,這樣會把把別人的路徑全部覆蓋,如果你不小心誤操作了環境變量,不用擔心,將xshell全部關閉,重新再登錄就可以了
還有另一種方式,把可執行程序拷貝到/usr/bin路徑下(sudo cp -rf myproc /usr/bin)
- 在Linux中,把可執行程序,拷貝到系統默認路徑下,這種讓我們可以直接訪問的方式,相當于Linux下軟件的安裝:你們用windows當中的工具幫你們安裝軟件,本質是把可執行程序、配置文件,全部拷貝到你系統特定的某些路徑下,比如C盤的Profile,這個動作就叫做安裝,不想要了可以刪掉(sudo rm /usr/bin/myproc -rf)
- 刪掉的動作其實就是某種意義上的卸載
?環境變量相關函數
1. exec 函數族
- 這些函數用于替換當前進程的映像為新的程序。如果執行成功,原程序的代碼、數據、堆棧等會被新程序覆蓋,但進程ID不變
#include <unistd.h>extern char **environ; 全局變量,指向當前進程的環境變量數組(格式為?KEY=VALUE)
示例:for (char **env = environ; *env; env++) {printf("%s\n", *env);}int execl(const char *pathname, const char *arg, .../* (char *) NULL */);
int execlp(const char *file, const char *arg, .../* (char *) NULL */);
int execle(const char *pathname, const char *arg, .../*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);
-
命名規則:
- 后綴?
l
:參數通過可變參數列表傳遞,參數列表以NULL結尾 - 后綴?
v
:參數通過數組傳遞,數組元素以?NULL
?結尾 - 后綴?
p
:在?PATH
?中搜索可執行文件 - 后綴?
e
:允許自定義環境變量(通過?envp
?參數)
- 后綴?
- 錯誤處理:所有?
exec
?函數失敗時會返回?-1
,需檢查?errno
。 - 環境變量:
execle
?和?execvpe
?可以覆蓋默認環境變量,其他函數繼承調用進程的環境
2?getenv
-
功能:獲取指定環境變量的值。
#include <stdlib.h>char *getenv(const char *name);參數:name:環境變量名返回值:成功返回變量值(字符串),失敗返回?NULL示例:char *path = getenv("PATH");
3 putenv
- 功能:設置或修改環境變量(格式為?
KEY=VALUE
)
#include <stdlib.h>int putenv(char *string);參數:string:環境變量字符串(如?"PATH=/usr/bin")返回值:成功返回?0,失敗返回非?0注意:修改會影響當前進程及其子進程示例:putenv("MY_VAR=123");
實操
makefile
myproc:proc.cgcc -o $@ $^
.PHONY:clean
clean:rm -f myproc
exec/otherproc.cc
#include <iostream>
#include <unistd.h>using namespace std;int main()
{for(int i = 0; i < 5; i++){cout << "-------------------------------------------------------------------------"<< endl;cout << "我是另一個程序,我的pid是: " << getpid() << endl;cout << " MYENV: " << (getenv("MYENV")==NULL?"NULL":getenv("MYENV")) << endl;cout << " PATH: " << (getenv("PATH")==NULL?"NULL":getenv("PATH")) << endl;cout << "-------------------------------------------------------------------------"<< endl;sleep(1);}return 0;
}
proc.c
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>int main()
{extern char **environ;pid_t id = fork();if(id == 0){//childprintf("我是子進程: %d\n", getpid());//execl: 如果替換成功,不會有返回值,如果替換失敗,一定有返回值 ==》如果失敗了,必定返回 ==》 只要有返回值,就失敗了//不用對該函數進行返回值判斷,只要繼續向后運行一定是失敗的!//execl("/bin/ls", "ls", "-a", "-ln", NULL); //lsssss: 不存在//char *const myargv[] = {// "ls",// "-a",// "-l",// "-n",// NULL//};//execv("/bin/ls", myargv); //lsssss: 不存在//execlp("ls", "ls", "-a", "-l", "-n", NULL);//execvp("ls", myargv);//char *const myenv[] = {// "MYENV=YouCanSeeMe",// NULL//};putenv("MYENV=YouCanSeeMe");//覆蓋式寫入,只執行我們定義的環境變量,系統環境變量就失效了,可以傳environ全局環境變量數組execle("./exec/otherproc", "otherproc", NULL, environ); exit(1);}sleep(1);//fatherint status = 0;printf("我是父進程: %d\n", getpid());waitpid(id, &status, 0);printf("child exit code: %d\n", WEXITSTATUS(status));return 0;
}