?? 前言
上一節(jié)我們講了進程終止和進程等待等一系列問題,并做了相應(yīng)的驗證,本章將繼續(xù)對進程控制進行學習,我們將學習進程程序替換,進行相關(guān)驗證,運用系統(tǒng)進程程序替換接口,自己模擬寫一個shell,該shell能夠?qū)崿F(xiàn)執(zhí)行指令,等一系列命令行操作……
1. 進程程序替換
1.1 程序替換的概念:
概念引入:
將可執(zhí)行程序加載到內(nèi)存,并且重新調(diào)整子進程的頁表映射,使之指向新的進程的代碼和數(shù)據(jù)段,這種過程就叫做程序替換。
子進程執(zhí)行的是父進程的代碼片段,那么如果我們想讓創(chuàng)建出來的子進程,執(zhí)行全新的程序呢?
此時就要用到:進程的程序替換。
1.2 為什么要程序替換:
原因:
- 原因是我們想讓我們的子進程執(zhí)行一個全新的程序。
- 不同語言寫的功能互相調(diào)用,這就是為什么要有程序替換的原因。
我們一般在服務(wù)器設(shè)計(Linux編程)的時候,往往需要子進程干兩件種類事情:
-
- 1.讓子進程執(zhí)行父進程的代碼片段(服務(wù)器代碼)
-
- 2.讓子進程執(zhí)行磁盤中一個全新的程序(shell, 想讓客戶端執(zhí)行對應(yīng)的程序,通過我們的進程,執(zhí)行其他人寫的進程代碼等等)C/C++ -> C/C++/Python/Shell/Php/Java…
1.3 程序替換的原理:
程序替換的原理:
- 將磁盤中的程序,加載入內(nèi)存結(jié)構(gòu)。
- 重新建立頁表映射,誰執(zhí)行程序替換,就重新建立子進程的映射關(guān)系。
效果:讓我們的父進程和子進程徹底分離,并讓子進程執(zhí)行一個全新的程序!
父進程的映射關(guān)系:
程序替換之后,子進程的映射關(guān)系:
調(diào)整子進程的頁表,讓其不再與父進程代碼和數(shù)據(jù)有任何關(guān)系,而是指向自己的代碼和自己的數(shù)據(jù)區(qū)。
注意:
- 請問:這個過程有沒有創(chuàng)建新的講程呢?
- —— 沒有?。?!
小結(jié):
- 說白了就是讓
fork
創(chuàng)建子進程,不想讓子進程執(zhí)行父進程代碼片段。 - 我們想讓子進程執(zhí)行磁盤當中全新的程序,而且我們沒有創(chuàng)建新的進程。
- 因為子進程的內(nèi)核數(shù)據(jù)結(jié)構(gòu)基本沒變,只是重新建立了虛擬到物理的映射關(guān)系罷了。
- 包括子進程的
PID
都不變,壓根就沒有創(chuàng)建新的進程,只不過讓新的進程執(zhí)行了不同的程序罷了。
每一個進程都有自己的CPU上下文,進程切換時會保存CPU數(shù)據(jù)。
2. 六個exec替換函數(shù)
上述我們講了什么是程序替換,下面就要來見見豬跑了。
程序替換是由操作系統(tǒng)完成的,調(diào)用系統(tǒng)調(diào)用接口來完成操作。
- 我們?nèi)绻雸?zhí)行一個全新的程序,我們需要做幾件事情:
- 先找到這個程序在哪里? —— 程序在那里
- 程序可能攜帶選項進行執(zhí)行(也可以不攜帶) —— 怎么執(zhí)行
明確告訴OS,我想怎么執(zhí)行這個程序是什么,要不要帶選項。
我們平時在命令行中敲的指令都是一個一個可執(zhí)行程序。
- 程序替換的是子進程:(重點)
- 進程替換永遠影響的是進程的本身,子進程的替換永遠不會影響父進程,因為進程具有獨立性。
- 獨立性體現(xiàn)在內(nèi)核層面,不同進程有不同的地址空間,有不同的頁表替換只是加入新的代碼和數(shù)據(jù)。
- 重新建立的是頁表映射但并不影響內(nèi)核數(shù)據(jù)結(jié)構(gòu)的具體情況。
- 子進程雖然和父進程代碼共享數(shù)據(jù)寫實拷貝,但是一旦發(fā)生進程替換了,就認為代碼和數(shù)據(jù)發(fā)生了雙寫實拷貝,就徹底將兩個進程分開了。
- 所以引入子進程的原因就是,一方面把需求做到位,另一方面不影響父進程,因為父進程可能還要接收新的命令,再去執(zhí)行新的程序。
六個exec替換函數(shù):
2.1 execl函數(shù):
int execl(const char *path, const char *arg, ...);
path:
- 這個是路徑,可執(zhí)行程序的路徑。
arg:
- 命令行怎么寫(1s -1 -a), 這個參數(shù)就怎么填"ls",“-l”,“-a”,最后必須是NULL結(jié)尾
- 標識 “如何執(zhí)行程序的” 參數(shù)傳遞完畢
… :
- 可變參數(shù),可以傳多個參數(shù)
第一個參數(shù)是解決了,程序在哪里的問題,第二個參數(shù)往后所有的參數(shù),解決的都是程序如何執(zhí)行的問題。
代碼演示:
#include <stdio.h>
#include <unistd.h>
int main()
{
//讓我的程序執(zhí)行系統(tǒng)上的: ls -a -i這樣的一個命令
printf("我是一個進程,我的pid是 : %d\n", getpid());
//int ret = execl("/usr/bin/ls", "ls", "-l", "-a", NULL); //帶選項
//execl("/usr/bin/top", "top", NULL); //不帶選項
//execl("/usr/bin/which", "which", "pwd", NULL); //不帶選項
//下面這行代碼沒有打印出來
//一旦代碼執(zhí)行到這里,必然是進程替換失敗了
//替換失敗的情況
int ret = execl("/usr/bin/lsssss", "ls", "-l", "-a", NULL); //帶選項
printf("我執(zhí)行完畢了,我的pid : %d, ret = %d\n", getpid(), ret);
return 0;
}
一旦進程替換成功了,就不會再執(zhí)行程序替換函數(shù)以后的代碼了,因為直接去是該進程被替換掉了。
顯而易見,代碼中程序替換以后的打印內(nèi)容并沒有顯示出來,說明進程替換以后的代碼壓根就沒執(zhí)行,而是去執(zhí)行l(wèi)s進程了。
總結(jié):
- 一旦替換成功,是將當前進程的代碼和數(shù)據(jù)全部替換了!!
- 前一個printf被執(zhí)行是因為程序替換并沒有執(zhí)行。
- 所以替換上面的代碼依舊是當前進程執(zhí)行執(zhí)行,execl之后代碼就不復(fù)存在了。
所以程序替換不用判斷返回值:
不需要返回值,一旦有值返回那么必然是返回失敗了?。?!因為只要成功了,就不會有返回值,而失敗的時候,必然會繼續(xù)向后執(zhí)行!!最多通過返回值得到什么原因?qū)е碌奶鎿Q失敗!
引入進程創(chuàng)建:
- 子進程執(zhí)行程序替換,會不會影響父進程呢??
-
- 不會(因為進程具有獨立性)
- 為什么,如何做到的??
-
- 數(shù)據(jù)層面發(fā)生寫時拷貝!
-
- 當程序替換的時候,我們可以理解成為,代碼和數(shù)據(jù)都發(fā)生了寫時拷貝完成父子的分離!
)
2.2 execv函數(shù):
int execv(const char *path, char *const argv[]);
實現(xiàn)的功能和execl
一模一樣。
path:
- 這個是路徑,可執(zhí)行程序的路徑。
argv[]:
- 如何執(zhí)行,和execl的唯一區(qū)別就是傳參方式的不一樣
代碼演示:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
printf("我是父進程,我的pid是:%d\n", getpid());
pid_t id = fork();
if(id == 0)
{
//子進程
//我們要子進程執(zhí)行全新的程序,以前我們是子進程執(zhí)行父進程的代碼片段
printf("我是子進程,我的pid是:%d\n", getpid());
//char* const argv_[] = {
// (char*)"ls",
// (char*)"-l",
// (char*)"-a",
// (char*)"-i",
// NULL
//};
char* const argv_[] = {
(char*)"top",
NULL
};
//execv("/usr/bin/ls", argv_);
execv("/usr/bin/top", argv_);
}
//一定是父進程
int status = 0;
int ret = waitpid(id, &status, 0);
if(ret == id)
{
sleep(2);
printf("進程等待成功!\n");
}
return 0;
}
程序替換不僅可以替換成指令,還可以替換成我們自己寫的可執(zhí)行程序。
- 用exec系列,這種系統(tǒng)級的函數(shù),可以把任何語言耦合到一起。
- 任何程序都可以用系統(tǒng)級接口調(diào)用其他語言的。
- 所以說操作系統(tǒng)是所有技術(shù)的基座。
2.3 execlp函數(shù):
int execlp(const char *file, const char *arg, ...);
file:
- 你想執(zhí)行什么程序。 —— 找到它
- 執(zhí)行指令的時候,默認的搜索路徑,在哪里搜索呢?—— 環(huán)境變量PATH
- 命名帶p的,可以不帶路徑,只說出你要執(zhí)行哪一個程序即可!
arg:
- 想如何執(zhí)行它
代碼演示:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
//有時候不想讓父進程做一件事,只想讓子進程做一件事
//將進程創(chuàng)建引入進來
int main()
{
printf("我是父進程,我的pid是:%d\n", getpid());
pid_t id = fork();
if(id == 0)
{
//子進程
//我們要子進程執(zhí)行全新的程序,以前我們是子進程執(zhí)行父進程的代碼片段
printf("我是子進程,我的pid是:%d\n", getpid());
execlp("ls", "ls", "-a", "-l", "-i", NULL);//這里出現(xiàn)了兩個ls,含義一樣嗎?-- 不一樣!
//第一個參數(shù)是供系統(tǒng)去找要執(zhí)行誰的指令,后面一坨是表示如何執(zhí)行該指令
exit(100); //只要執(zhí)行了exit,就意味著,execl系列的函數(shù)失敗了 -- 進程替換失敗了
}
//一定是父進程
int status = 0;
int ret = waitpid(id, &status, 0);
if(ret == id)
{
sleep(2);
printf("wait success, ret : %d, 我所等待子進程的退出碼: %d, 退出信號是: %d\n", ret, (status >> 8) & 0xFF, status & 0x7F);
}
return 0;
}
作用和execI和execv是一樣的,也是執(zhí)行一個新的程序。
2.4 execvp函數(shù):
int execvp(const char *file, char *const argv[]);
file:
- PATH找,只要程序名即可。
argv[]:
- 如何執(zhí)行,將命令行參數(shù)字符串,統(tǒng)一放入數(shù)組中即可完成調(diào)用!
2.5 execle函數(shù):
int execle(const char *path, const char *arg, ..., char * const envp[]);
envp[]:
- 環(huán)境變量
execle:test.c程序替換代碼演示:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int main()
{
//環(huán)境變量的指針聲明
extern char** environ;
printf("我是父進程,我的pid是:%d\n", getpid());
pid_t id = fork();
if(id == 0)
{
//子進程
//我們要子進程執(zhí)行全新的程序,以前我們是子進程執(zhí)行父進程的代碼片段
printf("我是子進程,我的pid是:%d\n", getpid());
//絕對路徑
//execl("/home/Zh_Ser/linux/lesson16/mycmd", "mycmd", NULL);
//相對路徑
//execl("./mycmd", "mycmd", NULL);
//我們來手動導(dǎo)入一個環(huán)境變量
char* const env_[] = {
(char*)"MYPATH=You Can See Me!!",
NULL
};
//e: 添加環(huán)境變量給目標進程,是覆蓋式的!
//execle("./mycmd", "mycmd", NULL, env_);
//execle("/usr/bin/ls", "ls", NULL, env_);
execle("./mycmd", "mycmd", NULL, environ);
exit(100); //只要執(zhí)行了exit,就意味著,execl系列的函數(shù)失敗了 -- 進程替換失敗了
}
//一定是父進程
int status = 0;
int ret = waitpid(id, &status, 0);
if(ret == id)
{
sleep(2);
printf("進程等待成功!\n");
}
return 0;
}
上述代碼代碼在程序替換的時候,執(zhí)行了./mycmd,目的是手動導(dǎo)入環(huán)境變量的時候,執(zhí)行./mycmd獲取導(dǎo)入的環(huán)境變量。
mycmd.cpp代碼演示:
#include <iostream>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
using namespace std;
int main()
{
extern char** environ;
cout << "打印環(huán)境變量" << endl;
for (int i = 0; environ[i]; i++)
{
printf("%d: %s\n", i, environ[i]);
}
cout << "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++" << endl;
//根據(jù)環(huán)境變量名,獲取環(huán)境變量的內(nèi)容
cout << "PATH:" << getenv("PATH") << endl;
cout << "----------------------------------------------" << endl;
cout << "MYPATH:" << getenv("MYPATH") << endl;
cout << "----------------------------------------------" << endl;
//程序崩潰了 -- 因為環(huán)境變量里根本就沒有MYPATH
cout << "Hello World!" << endl;
cout << "Hello World!" << endl;
cout << "Hello World!" << endl;
cout << "Hello World!" << endl;
return 0;
}
mycmd是為了獲取環(huán)境變量。
如果我們用這種方式導(dǎo)入環(huán)境變量:
出現(xiàn)的情況:
將mycmd.cpp中的getenv(“PATH”)給屏蔽掉,再執(zhí)行test結(jié)果就可以將MYPATH打印出來:
e: 添加環(huán)境變量給目標進程,是覆蓋式的!所以環(huán)境變量只剩下MYPATHT。
子進程會繼承父進程的環(huán)境變量的?。。ㄖ攸c)
- 子進程會繼承父進程的環(huán)境變量,當父進程調(diào)用fork()創(chuàng)建子進程時,子進程會繼承父進程的所有環(huán)境變量。
- 當子進程調(diào)用execlp()等函數(shù)執(zhí)行其他程序時,子進程也會繼承父進程的環(huán)境變量。
- 如果需要在子進程中更改環(huán)境變量,可以使用setenv()或putenv()等函數(shù)進行更改。
- 但是,更改的環(huán)境變量只會影響當前進程和它的子進程,并不會影響父進程或其他進程的環(huán)境變量。
如何理解覆蓋?(重點)
- 當子進程調(diào)用execle()函數(shù)替換自己的程序時,可以傳遞一個新的環(huán)境變量數(shù)組,以覆蓋子進程繼承的父進程的環(huán)境變量。如果不傳遞新的環(huán)境變量數(shù)組,子進程會繼承父進程的環(huán)境變量。因此,如果在調(diào)用execle()函數(shù)時沒有傳遞新的環(huán)境變量數(shù)組,子進程的環(huán)境變量不會被覆蓋。
- 如果傳遞了新的環(huán)境變量數(shù)組,則子進程的環(huán)境變量將被替換為新的環(huán)境變量數(shù)組中的值。這可能會導(dǎo)致子進程無法訪問父進程中的一些環(huán)境變量,除非在新的環(huán)境變量數(shù)組中顯式地包含它們。
驗證execle覆蓋了子進程會繼承父進程的環(huán)境變量:
- 我們執(zhí)行test程序的時候
- 調(diào)用execle接口,程序替換去執(zhí)行mytest
- 既然mytest是替換了子進程,它就會繼承父進程的全部環(huán)境變量
- execle函數(shù)我們傳了一個env_將子進程的環(huán)境變量覆蓋了
我們在mycmd程序開始的地方,加了查看全部環(huán)境變量的代碼:
extern char** environ;
for (int i = 0; environ[i]; i++)
{
printf("%d: %s\n", i, environ[i]);
}
目的是通過該代碼查看子進程(mycmd)的環(huán)境變量,被execle傳的env_覆蓋之后的樣子:
顯而易見,子進程的環(huán)境變量只有env_[]的內(nèi)容了?。?!所以getenv("PATH")
才獲取不到?。。?/font>
正確做法:
我們將全部環(huán)境變量傳過去,將environ傳過去。
補充:(重點)
- ls 是一個常見的系統(tǒng)命令,它通常位于系統(tǒng)的某個標準路徑(如 /bin 或 /usr/bin)。即使 PATH 為空,execlp() 會檢查這些標準路徑,找到 ls 的可執(zhí)行文件并執(zhí)行它。
- 可能是直接在execlp中定義好的路徑了,所以 PATH 環(huán)境變量沒了也可以找到。
詳細說明:
- 如果PATH環(huán)境變量為空,execlp()函數(shù)會無法在環(huán)境變量中查找可執(zhí)行文件的路徑。但是,execlp()函數(shù)會檢查一些默認路徑,例如/bin、/usr/bin等,來查找可執(zhí)行文件。因此,即使PATH為空execlp()函數(shù)也可能會在這些默認路徑中找到可執(zhí)行文件并執(zhí)行它。
- 但是,如果在默認路徑中也找不到可執(zhí)行文件,則execlp()函數(shù)會執(zhí)行失敗,并將errno設(shè)置為ENOENT,表示無法找到可執(zhí)行文件。因此,如果需要執(zhí)行特定路徑下的可執(zhí)行文件,最好使用execv()或execve()等函數(shù),并指定可執(zhí)行文件的完整路徑。這樣可以避免依賴PATH環(huán)境變量來查找可執(zhí)行文件的路徑。
驗證:
- 我們在在mycmd程序中再進行程序替換
- 用execlp函數(shù)第一個參數(shù)是在PATH路徑下找的可執(zhí)行文件
- mycmd進程的環(huán)境變量中只有一句話(只有MYPATH了)
- 但是我們照樣可以在mycmd中進行程序替換執(zhí)行出ls的結(jié)果!!
即使我們將父進程中的PATH給改了,命令行中都用不了ls,execlp照樣可以找到ls并執(zhí)行它。
2.6 execvpe函數(shù):
int execvpe(const char *file, char *const argv[], char *const envp[]);
有了上面的基礎(chǔ)這個想必就不用再啰嗦了,只是第二個參數(shù)傳的不同,這里傳的是一個指針數(shù)組。
這些函數(shù)原型看起來很容易混,但只要掌握了規(guī)律:
- l (list) : 表示參數(shù)采用列表
- v (vector) : 參數(shù)用數(shù)組
- p (path) : 有p自動搜索環(huán)境變量PATH
- e (env) : 表示自己維護環(huán)境變量
為什么有那么多的接口?
- 目的是:適配應(yīng)用場景
- 其實上述函數(shù)都是對系統(tǒng)接口的封裝
嚴格意義來說不是系統(tǒng)接口,是基于系統(tǒng)接口之上的封裝。
真正意義上的系統(tǒng)接口:
int execve(const char *filename, char *const argv[], char *const envp[]);
上述6個函數(shù)在執(zhí)行時都會調(diào)用execve()函數(shù),將參數(shù)列表和環(huán)境變量數(shù)組轉(zhuǎn)換為execve()函數(shù)所需的格式,并調(diào)用execve()函數(shù)來執(zhí)行可執(zhí)行文件。因此,execve()函數(shù)是這些函數(shù)的底層實現(xiàn)。
3. 實現(xiàn)簡易版shell
只要我們懂得了程序替換的原理,會用程序替換的接口,就很好理解:
- shell本身執(zhí)行起來就是個死循環(huán)
- 我們命令行就是去執(zhí)行其他程序
- shell創(chuàng)建子進程,將子進程給替換掉就ok了
- 過程中要獲取輸入指令等操作…
myshell代碼實現(xiàn):
#include <stdio.h>
#include <string.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#define SEP " "
#define NUM 1024
#define SIZE 128
char command_line[NUM];
char* command_args[SIZE];
char env_buffer[NUM];
extern char** environ;
//對應(yīng)上層的內(nèi)建命令
int ChangeDir(const char* new_path)
{
chdir(new_path);
return 0;//調(diào)用成功
}
void PutEnvInMyShell(char* new_env)
{
putenv(new_env);
}
int main()
{
//shell本質(zhì)就是一個死循環(huán)
while(1)
{
//不關(guān)心獲取這些屬性的接口,搜索一下都有
//1.顯示提示符
printf("[用戶名@我的主機名 當前目錄]# ");
fflush(stdout);
//2.獲取用戶輸入
memset(command_line, '\0', sizeof(command_line));
//從鍵盤獲取,標準輸入,stdin,獲取到的是C風格的字符串(stdio.h結(jié)尾的),'\0'結(jié)尾
fgets(command_line, NUM, stdin);
command_line[strlen(command_line) - 1] = '\0';//清空\n回車
//printf("%s\n", command_line);
//3. "ls -a -l -i" -> "ls" "-a" "-l" "-i" 字符串切分 -- 因為這些參數(shù)一定得以列表或者數(shù)組方式傳遞給程序替換接口
//shell必須切分,因為必須調(diào)用execl函數(shù)
//將第一個字符串地址用0號下標指向,第二個字符串地址用1號下標指向
command_args[0] = strtok(command_line, SEP);
int index = 1;
//給ls命令添加顏色: 如果提取出來的程序名是ls -- 1下標設(shè)置成改顏色的
if(strcmp(command_args[0], "ls") == 0) command_args[index++] = (char*)"--color=auto";
//strtok截取成功返回字符串起始地址
//截取失敗,返回NULL
while(command_args[index++] = strtok(NULL, SEP));
//for debug
//int i = 0;
//for(i = 0; i < index; i++)
//{
// printf("%d : %s\n", i, command_args[i]);
//}
//4.TODO -- 編寫后面的邏輯,內(nèi)建命令(由父Shell自己實現(xiàn)的自己調(diào)用的一個函數(shù))
if(strcmp(command_args[0], "cd") == 0 && command_args[1] != NULL)
{
//讓調(diào)用方進行路徑切換,父進程
ChangeDir(command_args[1]);
continue;
}
//走到這里一定是將命令行參數(shù)解析完了,包括命令 + 選項
//將環(huán)境變量的信息導(dǎo)入在了父進程的上下文當中
if(strcmp(command_args[0], "export") == 0 && command_args[1] != NULL)
{
//環(huán)境變量列表(是個指針數(shù)組,每個元素是個指針指向一個環(huán)境變量)
//我們傳的是一個字符串首地址,但是環(huán)境變量的內(nèi)容還是我們自己維護的
//目前,環(huán)境變量信息在comman_line,會被清空,那么環(huán)境變量當然就沒有了
//所以此處我們需要自己保存一下環(huán)境變量的內(nèi)容
strcpy(env_buffer, command_args[1]);
PutEnvInMyShell(env_buffer);
//PutEnvInMyShell(command_args[1]);//MYENV=112233
continue;
}
//5.創(chuàng)建進程,執(zhí)行
//如果自己直接程序替換的話,就把自己寫的shell給替換了
pid_t id = fork();
if(id == 0)
{
//子進程
//6.程序替換
//execvpe(command_args[0], command_args, environ);
execvp(command_args[0], command_args);
exit(1);//執(zhí)行到這里,子進程一定替換失敗了
}
int status = 0;
pid_t ret = waitpid(id, &status, 0);
if(ret > 0)
{
printf("等待子進程成功: sig: %d, code: %d\n", status & 0x007F, (status & 0xFF00) >> 8);
}
}//end while
return 0;
}
ls設(shè)置顏色的辦法:
3.1 內(nèi)建命令等問題的解決:
3.1 - 1 cd命令的處理:
在命令行中操作cd時,會跳轉(zhuǎn)路徑,但是用絕對命令時,就不行了,還是原來的路徑:
- 一個進程也存在對應(yīng)路徑, 進程對應(yīng)的路徑可以理解成
- 當進程啟動的時候時,在哪個路徑啟動時,這個進程所在路徑就是當前進程所啟動的路徑
一般一個進程的路徑是會被于進程繼承的,路徑的變化我們希望的是父進程路徑的變化。
原因就是,我們平時用的cd時做過處理的cd:
- 我們知道指令都是一些可執(zhí)行程序
- 執(zhí)行可執(zhí)行程序就是去執(zhí)行其他程序,程序替換了
- 如果我們創(chuàng)建的子進程跳轉(zhuǎn)路徑
- 子進程退出之后,只是子進程的路徑跳轉(zhuǎn)了
- 并不影響父進程的路徑,會發(fā)現(xiàn)命令行路徑還是沒變
- 顯然這這種做法是不可取的
就不能用程序替換的方式來執(zhí)行一些特殊的命令了:
- 我們可以在父進程將一些命令單獨處理
- 讓其不進行程序替換
重點:
- 程序替換影響的是子進程和父進程沒關(guān)系,子進程一 跑就完了,曾經(jīng)所有的操作就沒有意義,路徑切換就沒意義了,所以我們要讓父進程的路徑發(fā)生變化。
- 如果有些行為,是必須讓父進程shell執(zhí)行的,不想讓子進程執(zhí)行,絕對不能創(chuàng)建子進程!只能是父進程自己實現(xiàn)對應(yīng)的代碼!
內(nèi)建命令:
- 我們把由父進程自己提供的代碼或者提供的邏輯(在命令行上體現(xiàn)的也是一個命令),但是這部分命令不是子進程執(zhí)行的,而是父進程自己執(zhí)行的,我們叫做內(nèi)建命令。
- 由shell自己執(zhí)行的命令,我們稱之為內(nèi)建(內(nèi)置bind- in)命令。
更改工作目錄的函數(shù):
驗證一下:
3.2 - 2 export的處理:
導(dǎo)入環(huán)境變量:
export不是一個可執(zhí)行程序和cd,ls,cat等指令不同:
export是一個shell內(nèi)置命令,用于設(shè)置環(huán)境變量。它并不是一個可執(zhí)行程序,而是由shell解釋器直接執(zhí)行的命令。當我們在shell中使用export命令時,它會將指定的環(huán)境變量設(shè)置為當前shell進程的環(huán)境變量,以便后續(xù)的命令或程序可以使用該環(huán)境變量。
所以用execvp進行程序替換的時候,是不能替換成功的!
注意:
- 環(huán)境變量列表(是個指針數(shù)組,每個元素是個指針指向一個環(huán)境變量)
- 我們傳的是一個字符串首地址,但是環(huán)境變量的內(nèi)容還是我們自己維護的
- 目前,環(huán)境變量信息在comman_line,會被清空(環(huán)境變量要一直維護),那么環(huán)境變量當然就沒有了
- 所以此處我們需要自己保存一下環(huán)境變量的內(nèi)容
環(huán)境變量是數(shù)據(jù),進程替換不是替換進程的代碼和數(shù)據(jù)嗎?文章來源:http://www.zghlxwxcb.cn/news/detail-463746.html
- 但是環(huán)境變量是屬于系統(tǒng)的數(shù)據(jù)
- 子進程在執(zhí)行程序替換時
- 當前進程的環(huán)境變量數(shù)據(jù),不會被替換掉
- 而且是以父進程為模版繼承下來的
- 所以才會讓父進程以內(nèi)建命令的方式putenv,子進程就能直接獲取了
環(huán)境變量的數(shù)據(jù),在進程的上下文中:文章來源地址http://www.zghlxwxcb.cn/news/detail-463746.html
- 環(huán)境變量會被子進程繼承下去,所以他會有全局屬性。
- 當我們進行程序替換的時候,當前進程的環(huán)境變量非但不會被替換,而且是繼承父進程的?。?/li>
到了這里,關(guān)于【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!