国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

這篇具有很好參考價值的文章主要介紹了【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

?? 前言

上一節(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)系:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
程序替換之后,子進程的映射關(guān)系:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
調(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ù):

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

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ù)以后的代碼了,因為直接去是該進程被替換掉了。

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
顯而易見,代碼中程序替換以后的打印內(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ā)生了寫時拷貝完成父子的分離!

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell)

2.2 execv函數(shù):

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

實現(xiàn)的功能和execl一模一樣。

path:

  • 這個是路徑,可執(zhí)行程序的路徑。

argv[]:

  • 如何執(zhí)行,和execl的唯一區(qū)別就是傳參方式的不一樣

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
代碼演示:

#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)用!

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

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)境變量:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
出現(xiàn)的情況:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
將mycmd.cpp中的getenv(“PATH”)給屏蔽掉,再執(zhí)行test結(jié)果就可以將MYPATH打印出來:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
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)境變量覆蓋了

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
我們在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_覆蓋之后的樣子:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
顯而易見,子進程的環(huán)境變量只有env_[]的內(nèi)容了?。?!所以getenv("PATH")才獲取不到?。。?/font>

正確做法:

我們將全部環(huán)境變量傳過去,將environ傳過去。

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell


補充:(重點)

  • 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é)果!!

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
即使我們將父進程中的PATH給改了,命令行中都用不了ls,execlp照樣可以找到ls并執(zhí)行它。

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

2.6 execvpe函數(shù):

int execvpe(const char *file, char *const argv[], char *const envp[]);

有了上面的基礎(chǔ)這個想必就不用再啰嗦了,只是第二個參數(shù)傳的不同,這里傳的是一個指針數(shù)組。

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
這些函數(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è)置顏色的辦法:
【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

3.1 內(nèi)建命令等問題的解決:

3.1 - 1 cd命令的處理:

在命令行中操作cd時,會跳轉(zhuǎn)路徑,但是用絕對命令時,就不行了,還是原來的路徑:

  • 一個進程也存在對應(yīng)路徑, 進程對應(yīng)的路徑可以理解成
  • 當進程啟動的時候時,在哪個路徑啟動時,這個進程所在路徑就是當前進程所啟動的路徑

一般一個進程的路徑是會被于進程繼承的,路徑的變化我們希望的是父進程路徑的變化。

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
原因就是,我們平時用的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ù):

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
驗證一下:
【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell

3.2 - 2 export的處理:

導(dǎo)入環(huán)境變量:

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
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進行程序替換的時候,是不能替換成功的!

【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell
注意:

  • 環(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ù)嗎?

  • 但是環(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

  1. 環(huán)境變量會被子進程繼承下去,所以他會有全局屬性。
  2. 當我們進行程序替換的時候,當前進程的環(huán)境變量非但不會被替換,而且是繼承父進程的?。?/li>

到了這里,關(guān)于【Linux】進程控制 — 進程程序替換 + 實現(xiàn)簡易shell的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務(wù),不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費用

相關(guān)文章

  • 【Linux】Linux進程控制 --- 進程創(chuàng)建、終止、等待、替換、shell派生子進程的理解…

    【Linux】Linux進程控制 --- 進程創(chuàng)建、終止、等待、替換、shell派生子進程的理解…

    柴犬: 你好啊,屏幕前的大帥哥or大美女,和我一起享受美好的今天叭?????? 1. 在調(diào)用fork函數(shù)之后, 當執(zhí)行的程序代碼轉(zhuǎn)移到內(nèi)核中的fork代碼后 ,內(nèi)核需要分配 新的內(nèi)存塊 和 內(nèi)核數(shù)據(jù)結(jié)構(gòu) 給子進程, 內(nèi)核數(shù)據(jù)結(jié)構(gòu)包括PCB、mm_struct和頁表,然后構(gòu)建起映射關(guān)系 ,同時

    2024年01月16日
    瀏覽(25)
  • Linux進程控制【進程程序替換】

    Linux進程控制【進程程序替換】

    ?個人主頁: Yohifo ??所屬專欄: Linux學習之旅 ??每篇一句: 圖片來源 ??操作環(huán)境: CentOS 7.6 阿里云遠程服務(wù)器 Good judgment comes from experience, and a lot of that comes from bad judgment. 好的判斷力來自經(jīng)驗,其中很多來自糟糕的判斷力。 子進程 在被創(chuàng)建后,共享的是 父進程 的代碼

    2024年01月17日
    瀏覽(28)
  • [Linux 進程控制(二)] 進程程序替換

    [Linux 進程控制(二)] 進程程序替換

    首先,我們要認識到,我們之前fork()所創(chuàng)建的子進程,執(zhí)行的代碼,都是父進程的一部分(用if-else分流或者執(zhí)行同樣的代碼)! 如果我們想讓子進程執(zhí)行新的程序呢? 執(zhí)行全新的代碼和訪問全新的數(shù)據(jù),不再和父進程有瓜葛,這種技術(shù)就叫做程序替換 ,下面我們就來學習一

    2024年03月14日
    瀏覽(32)
  • 【Linux初階】進程替換的應(yīng)用 - 簡易命令行解釋器的實現(xiàn)

    【Linux初階】進程替換的應(yīng)用 - 簡易命令行解釋器的實現(xiàn)

    ??hello,各位讀者大大們你們好呀?? ????系列專欄:【Linux初階】 ????本篇內(nèi)容:使用代碼手段實現(xiàn)一個簡易的命令行解釋器,其中功能包括:打印輸出提示符、獲取用戶輸入、字符串切割、執(zhí)行命令、ls指令下?lián)碛蓄伾崾?、cd、echo; ????作者簡介:計算機海洋的

    2024年02月07日
    瀏覽(93)
  • Linux--進程控制(2)--進程的程序替換(奪舍)

    Linux--進程控制(2)--進程的程序替換(奪舍)

    目錄 進程的程序替換 0.相關(guān)函數(shù) 1.先看現(xiàn)象 ?2.解釋原理 3.將代碼改成多進程版 ?4.使用其它的替換函數(shù),并且認識函數(shù)參數(shù)的含義 5.其它 ?關(guān)于進程替換我們需要了解的6個函數(shù): 函數(shù)解釋: 這些函數(shù)如果調(diào)用成功則加載新的程序從啟動代碼開始執(zhí)行,不再返回。 如果調(diào)用出

    2024年04月29日
    瀏覽(23)
  • 【Linux】Linux進程控制及程序替換

    【Linux】Linux進程控制及程序替換

    ?? 作者: 阿潤菜菜 ?? 專欄: Linux系統(tǒng)編程 在linux中fork是一個很重要的函數(shù),它可以已存在進程中創(chuàng)建一個新進程。新進程為子進程,而原進程為父進程。 fork函數(shù)返回兩個值,一個是子進程的進程號(pid),另一個是0。 父進程可以通過pid來區(qū)分自己和子進程,子進程可

    2024年02月02日
    瀏覽(61)
  • 【linux】進程替換的應(yīng)用|shell解釋器的實現(xiàn)

    【linux】進程替換的應(yīng)用|shell解釋器的實現(xiàn)

    當我們學過了進程替換之后,本篇文章可以根據(jù)進程替換的知識帶你自主實現(xiàn)一個shell命令行 實現(xiàn)步驟 1.顯示命令行提示 2.讀取輸入指令以及對應(yīng)選項 3.分割第二步的指令以及選項到命令行參數(shù)表中 4.處理內(nèi)建命令 5.進程替換 我們通過觀察bash的命令行提示發(fā)現(xiàn)他是由三部分

    2024年04月26日
    瀏覽(100)
  • 『Linux從入門到精通』第 ? 期 - 學會了程序替換,我決定手寫一個簡易版shell玩一玩...

    『Linux從入門到精通』第 ? 期 - 學會了程序替換,我決定手寫一個簡易版shell玩一玩...

    ??作者簡介: 花想云 ,在讀本科生一枚,C/C++領(lǐng)域新星創(chuàng)作者,新星計劃導(dǎo)師,阿里云專家博主,CSDN內(nèi)容合伙人…致力于 C/C++、Linux 學習。 ?? 專欄簡介:本文收錄于 Linux從入門到精通 ,本專欄主要內(nèi)容為本專欄主要內(nèi)容為Linux的系統(tǒng)性學習,專為小白打造的文章專欄。

    2024年02月14日
    瀏覽(18)
  • 【探索Linux】—— 強大的命令行工具 P.10(進程的控制——創(chuàng)建、終止、等待、程序替換)

    【探索Linux】—— 強大的命令行工具 P.10(進程的控制——創(chuàng)建、終止、等待、程序替換)

    前面我們講了C語言的基礎(chǔ)知識,也了解了一些數(shù)據(jù)結(jié)構(gòu),并且講了有關(guān)C++的一些知識,也學習了一些Linux的基本操作,也了解并學習了有關(guān)Linux開發(fā)工具vim 、gcc/g++ 使用、yum工具以及git 命令行提交代碼也相信大家都掌握的不錯,上一篇文章我們了解了關(guān)于進程的地址空間,今

    2024年02月08日
    瀏覽(20)
  • [Linux]進程控制精講,簡單實現(xiàn)一個shell

    [Linux]進程控制精講,簡單實現(xiàn)一個shell

    目錄 前言 進程創(chuàng)建 fork函數(shù)初識 寫時拷貝 fork常見用法 fork調(diào)用失敗的原因 進程終止 進程退出場景 進程退出碼 查看進程退出碼 退出碼的含義 進程常見退出方法 exit VS _exit exit函數(shù) _exit函數(shù) 二者的區(qū)別 return退出 進程等待 進程等待必要性 進程等待的方法 wait方法 waitpid方法

    2023年04月26日
    瀏覽(15)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包