進程程序替換
什么是進程程序替換?
指在一個正在運行的進程中,將原來的程序替換成新的程序的過程。
eg:如果我們想讓fork創(chuàng)建出來的子進程執(zhí)行全新的任務,此時就需要進程程序替換
為什么要進程程序替換呢?
我們一般在服務器設計的時候(linux編程的時候)往往需要子進程干兩種事情
- 讓子進程執(zhí)行父進程的代碼片段(服務器代碼)
- 讓子進程執(zhí)行磁盤中的一個全新的程序,eg:通過我們的進程執(zhí)行其他人寫的進程代碼等等,可以使得我們的c/c++代碼調(diào)用c/c++/Python/Shell/Java等代碼
進程程序替換的原理
假設此時父進程執(zhí)行的是a.exe,fork創(chuàng)建子進程之后,原本子進程要繼承父進程的代碼和數(shù)據(jù),頁表等映射也都是一樣的,此時將磁盤中的b.exe加載如內(nèi)存結構,使得子進程重新建立頁表的映射關系,誰執(zhí)行程序替換就重新建立誰的映射(此時是子進程)
效果:此時做到了讓父子進程分離,并且讓子進程執(zhí)行一個全新的程序,至于如何做到,是有OS系統(tǒng)中的系統(tǒng)調(diào)用接口完成的。
如何進行程序替換
替換函數(shù)一共有六種,先用 execl第一種函數(shù)舉例子
我們要執(zhí)行一個全新的程序,需要做如下幾件事情
- 先找到這個程序在哪里
- 程序可能攜帶選項進行執(zhí)行,也可以不攜帶,(也就是程序怎么執(zhí)行的)我們需要明確告訴OS,我想怎么執(zhí)行這個程序,要不要帶選項
- eg:命令行怎么寫ls -l -a 第二個參數(shù)就怎么填 “l(fā)s”, “-l”, "-a"并且參數(shù)的最后一定是NULL,表示【如何執(zhí)行程序】參數(shù)傳遞完畢
發(fā)現(xiàn)此時執(zhí)行了第一個printf,然后執(zhí)行了ls命令,但是第二個printf沒有打印了,為什么?
一旦替換成功,是將當前進程的代碼和數(shù)據(jù)全部替換,后面的printf是原來進程的代碼,所以改代碼早就被替換了,改代碼不存在了。
所以該程序替換函數(shù)需要返回值嗎?
int ret = execl(…)
一旦替換成功,就會執(zhí)行新的進程代碼,就不會有返回值
而失敗的話,必然會順著原來的進程代碼執(zhí)行,最多通過返回值得到是什么原因?qū)е碌奶鎿Q失?。?/p>
引入子進程創(chuàng)建
子進程執(zhí)行程序替換,會影響父進程嗎?
不會,因為進程具有獨立性
如何做到的?
數(shù)據(jù)層面發(fā)生寫實拷貝,程序替換的時候,我們可以理解為,代碼和數(shù)據(jù)都發(fā)生了寫實拷貝完成父子的分離。
19 printf("我是父進程,我的PID是:%d\n",getpid());
20 pid_t id = fork();
21 if(id == 0) {
22 //我們想讓子進程執(zhí)行全新的程序,以前是執(zhí)行父進程的代碼片段
23 printf("我是子進程,我的PID是:%d\n", getpid());
24 execl("/usr/bin/ls","ls","-a","-l",NULL);
25 printf("子進程替換進程失敗\n");
26 exit(1);//只要執(zhí)行了exit,意味著,execl系列的函數(shù)失敗了
27
28 }
29 //一定是父進程
30 int status = 0;
31 printf("我是父進程,我的PID: %d,我準備等子進程啦!\n",getpid());
32 int ret = waitpid(id,&status,0);//阻塞式等待子進程
33 if(ret == id) {
34 sleep(1);
35 printf("父進程等待成功!子進程的退出碼是: %d \n",(status>>8)&0XFF);
36 }
37 return 0;
38 }
不同程序替換函數(shù)之間的區(qū)別
int execl(const char *path, const char *arg, …);
int execv(const char *path, char *const argv[]);
execv VS execl
二者幾乎是一樣的,只有傳參方式的區(qū)別,execl傳參的方式是list列表傳參,execv是vector數(shù)組傳參
int execvp(const char *file, char *const argv[]);
函數(shù)名中有p和v表示使用該函數(shù)時,可以不帶路徑,并且函數(shù)第二個參數(shù)傳參的時候是vector傳參
系統(tǒng)接口調(diào)用其他語言的函數(shù)
調(diào)用c++函數(shù)
調(diào)用Python
替換函數(shù)execle
int execle(const char *path, const char *arg, …,char *const envp[]);
函數(shù)名中的e表示環(huán)境變量
第三個參數(shù)既可以自己傳自定義的環(huán)境變量
也可以傳系統(tǒng)定義的環(huán)境變量,二者有所區(qū)分
上述代碼中:mycomd.cc調(diào)用了getenv函數(shù),打印出環(huán)境變量,而我們在mtproc.cc中先用execl調(diào)用mycomd,發(fā)現(xiàn)此時只有環(huán)境變量PATH那一行代碼被打印出來了,到MYPATH這行代碼時,就無法再運行下去了,因為此時在系統(tǒng)中沒有環(huán)境變量MYPATH。
如圖所示,如果我們導出環(huán)境變量變量的話,此時發(fā)現(xiàn)都可以打印出來了。
但是如果我們用execle函數(shù)自定義導入環(huán)境變量的話。
如果此時導入自定義變量,發(fā)現(xiàn)PATH系統(tǒng)自帶的環(huán)境變量打印不出,說明添加環(huán)境變量給目標進程,是覆蓋式的,只要傳入自定義的環(huán)境變量,那么原來的環(huán)境變量,就失去作用了。
如果execle傳入的是系統(tǒng)定義的環(huán)境變量,那么我們export導入的環(huán)境變量還是有用的。
問題:為什么程序替換會有那么多借口?
為了適配更多的應用場景
execve為什么是單獨的?
int execve(const char *path, char *const argv[], char *const envp[]);
只有這個函數(shù)是系統(tǒng)接口,上面的函數(shù)都是對這個函數(shù)的封裝
總結:
對于替換函數(shù)
l(list) : 表示參數(shù)采用列表
v(vector) : 參數(shù)用數(shù)組
p(path) : 有p自動搜索環(huán)境變量PATH
e(env) : 表示自己維護環(huán)境變量文章來源:http://www.zghlxwxcb.cn/news/detail-448697.html
簡易版shell實現(xiàn)
shell的大致原理,以ls為例
shell從用戶讀入字符串,shell建立一個新的進程,然后在那個進程中運行l(wèi)s程序并等待ls進程結束,然后shell讀取新的一行輸入,建立一個新進程,在這個進程中運行程序,并等待這個進程結束。所以要寫一個shell,需要循環(huán)以下過程:文章來源地址http://www.zghlxwxcb.cn/news/detail-448697.html
- 獲取命令行
- 解析命令行
- 建立一個子進程
- 替換子進程
- 父進程等待子進程退出
僅僅支持一些簡單的命令
1 #include<stdio.h>
2 #include<string.h>
3 #include<stdlib.h>
4 #include<unistd.h>
5 #include<sys/types.h>
6 #include<sys/wait.h>
7
8 #define SEP " "
9 #define NUM 1024
10 #define SIZE 128
11 char command_line[NUM];
12 char *command_args[SIZE];
13 char env_buffer[NUM];//fou test
14 extern char*enviorn;
15
16 int ChangDir(const char * new_path)
17 {
18 chdir(new_path);
19 return 0;//成功
20
21 }
22 void putEnvInMyShell(char *new_env)
23 {
24 putenv(new_env);//此時是新增環(huán)境變量不是覆蓋
25 }
26 int main()
27 {
28 //shell本質(zhì)上就是一個死循環(huán)
29 while(1)
30 {
31 //1.顯示提示符
32 printf("[zjt@大帥比 當前目錄]# ");
33 fflush(stdout);//強制刷新屏幕,否則上述信息回儲存在緩沖區(qū)中
34 //2.獲取用戶輸入
35 memset(command_line,'\0',sizeof(command_line)*sizeof(char));
36 fgets(command_line,NUM,stdin);//鍵盤,標準輸入,stdin,獲取的是c風格的字符串,'\0';
37 command_line[strlen(command_line)-1] = '\0';//清空'\n'
38 //3."ls -a -l -i" -> "ls" "-a" "-l" "-i"字符串切分
39 command_args[0] = strtok(command_line,SEP);
40 int index = 1;
41 //給ls命令添加顏色
42 if(strcmp(command_args[0]/*程序名*/,"ls") == 0)
43 command_args[index++] = (char*)"--color=auto";
44 //strtok截取成功,返回字符串起始地址
45 //截取失敗,返回NULL;
46 while(command_args[index++] = strtok(NULL,SEP));
47 //按照SEP進行字符串切割,第一次調(diào)用時,第一個參數(shù)指向要分割的字符串
48 //以后每次調(diào)用第一個參數(shù)都指向NULL,表示繼續(xù)分割上一次的剩余部分
49 //最終根據(jù)分割字符分割字符串
50 //TODO,編寫后面的邏輯,內(nèi)建命令
51 if(strcmp(command_args[0], "cd") == 0 && command_args[1] != NULL)
52 {
53 ChangDir(command_args[1]);//讓調(diào)用方進行路徑切換,父進程
54 continue;
55
56 }
57 if(strcmp(command_args[0], "export") == 0 && command_args[1] != NULL)
58 {
59 //目前,環(huán)境變量信息在command_line,會被清空
60 //此處我們需要保存一下環(huán)境變量內(nèi)容
61 strcpy(env_buffer,command_args[1]);
62 putEnvInMyShell(env_buffer);
63 continue;
64
65 }
66 //5.創(chuàng)建進程,執(zhí)行
67 pid_t id = fork();
68 if(id == 0)
69 {
70 //child
71 //6.程序替換
72 execvp(command_args[0],/*我們保存的要執(zhí)行的程序名*/command_args);
73 exit(1);
74 //此時子進程調(diào)用一定失敗
75 }
76 int status = 0;
W> 77 pid_t ret = waitpid(id, &status, 0);
78 // if(ret > 0)
79 // {
80 // printf("子進程等待成功,進程退出碼為:%d,退出信號為:%d\n",(status>>8)&0xFF,status&0xFF);
81 //
82 // }
83 }
84 }
到了這里,關于進程程序替換+簡易版shell實現(xiàn)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!