Linux 進程程序替換

目錄

進程程序替換

         進程替換的原理

替換函數exec族函數

1. execv(參數格式是數組)

2.execl(參數格式是列表)

3.execvp / execlp(不帶替換程序的路徑)

4.execle / execve(需要自己設置環境變量) 


相關博客:

進程相關概念: 戳鏈接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103213364
進程創建(fork):戳鏈接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103302804
進程退出:戳鏈接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103302874
進程等待(wait()/waitpid()):戳鏈接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103302883

進程程序替換

爲什麼要進行進程替換 ?

前面說到,  fork()創建子進程, fork創建的子進程要麼和父進程執行一樣的代碼, 要麼執行不同的代碼分支(通過fork的返回值控制),

但這樣還是不夠靈活. 假如要有很多的功能已經用別的程序實現好了, 那麼就不需要在父進程中控制父子進程執行不同的代碼分

支, 讓子進程在自己的分支中完成這些功能, 而是可以直接拿一個已有的程序替換掉子進程. 使子進程的代碼完全變成所替換程序

的代碼. 這樣就方便了很多, 而且在子進程需要完成較爲複雜的功能或是多項功能時, 分支就顯得力不從心了.

所以進程往往要調用exec族中的某一個函數來進行程序替換, 讓一個進程來執行另一個程序. 當進程調用一種exec函數時,該進程

的用戶空間代碼和數據完全被新程序替換,從新程序的啓動例程開始執行。調用exec族中的函數並不會創建新進程,所以調用exec

前後被替換進程的iD並未改變。

進程替換的原理

如上圖, 進程替換時, 替換的是PCB映射在內存中的代碼和數據. 這樣, 該進程PID雖然沒有變, 但已經物是人非, 已經不是原來的那

個進程了.

如進行進程程序替換? 前面也說到了, exec族函數

替換函數exec族函數

exec族函數共有六個, 功能都是進程程序替換, 但多個不同的函數接口使得使用更加靈活. 

其中exceve()是系統調用接口, 其餘5個底層都封裝了execve().

函數原型如下 :

int execl(const char *path, const char *arg0, ... /*, (char *)0 */);
int execv(const char *path, char *const argv[]);
int execle(const char *path, const char *arg0, ... /*,(char *)0, char *const envp[]*/);
int execve(const char *path, char *const argv[], char *const envp[]);
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
int execvp(const char *file, char *const argv[]);

頭文件: unistd.h 

返回值 :

  • 這六個函數返回值相同. 
  • 當函數調用失敗, 返回 -1. 
  • 當調用成功, 即加載新的程序, 替換後的進程啓動開始執行,exec族函數不再返回 .
  • 特殊的地方是, 函數調用成功, 不返回

參數:

雖然有六個, 不好記, 但好在有規律. 可以發現, 函數名都是在exce的基礎上, 加上l, v, e,  p形成新的函數名, 加哪個字母都有各自的

含義 , 如下所示:

  • l / v 必須有一個, 也只能有一個, 帶l參數格式是列表,  帶 v 參數格式是 數組 
  • p   有p, 會自動搜索環境變量PATH, 則可以不帶路徑, 只要文件名(但文件必須放在PATH環境變量指定路徑).  沒有p, 則必須指定文件路徑
  • e    有e, 則不使用當前環境變量, 需要自己設置環境變量,  沒帶e, 則使用當前環境變量, 無需設置環境變量

如下表:

函數名 參數格式 函數是否自帶路徑(通過PATH) 是否使用當前環境變量
execl 列表 不帶, 需要制定文件路徑 使用
execlp 列表 帶, 但文件必須的放在指定目錄 使用
execle 列表 不帶, 需要制定文件路徑 不使用, 需要自己設置環境變量
execv 數組 不帶, 需要制定文件路徑 使用
execvp 數組 帶, 但文件必須的放在指定目錄 使用
execve 數組 不帶, 需要制定文件路徑 不使用, 需要自己設置環境變量

來看一下這六個函數具體如何使用.

1. execv(參數格式是數組)

#include<stdio.h>
#include<unistd.h>
int main() {
    char* arg[] = {"ls","-a","-l","/",NULL };//參數數組;//參數數組
    execv("/bin/ls", arg);
    printf("hello world!\n");
    return 0;
}

 可以看到進程被ls程序替換後, 只會執行ls的代碼, 並不會再輸出 hello world!

 如下, 還可以通過main函數的參數傳入, 我們用Shell調用ls等命令就是這個原理.

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, char* argv[]){
    char* arg[] = {"ls","-a","-l",NULL };//參數數組
    pid_t pid = fork();
    if(pid == -1){
        perror("fork");
    }
    else if(pid == 0){
        printf("替換子進程\n");
        execv("/bin/ls", argv);
    }
    else{
        sleep(1);
        printf("替換父進程\n");
        execv("/bin/ls", arg);
    }
    printf("hello world!\n");
    return 0;
}

 

2.execl(參數格式是列表)

#include<stdio.h>
#include<unistd.h>
int main(){
    execl("/bin/ls", "ls", "/", NULL);
    printf("hello world!\n");
    return 0;
}

3.execvp / execlp(不帶替換程序的路徑)

execvp

#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[]){
    execvp("ls", arg);
    printf("hello world!\n");
    return 0;
}

 execlp

#include<stdio.h>
#include<unistd.h>
int main(){
    char* arg[] = {"ls","-a","-l",NULL };//參數數組
    execlp("ls", "-a", "-l", NULL);
    printf("hello world!\n");
    return 0;
}

 

4.execle / execve(需要自己設置環境變量) 

execve

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(int argc, char* argv[]){
    char* envp[] = {"PATH=/home/test", NULL};
    execve("/bin/env", argv, envp);
    return 0;
}

execle

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
int main(){
    char* envp[] = {"PATH=/home/test", NULL};
    execle("/bin/env", "", NULL, envp);
    return 0;
}

學習完進程的創建和替換後, 就可以利用這些知識, 寫一個自己的Shell了

進程的創建在另一篇博客, 戳鏈接( ̄︶ ̄)↗https://blog.csdn.net/qq_41071068/article/details/103302804

具體代碼, 持續更新

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章