柚子快報激活碼778899分享:運維 【Linux】進程替換
???個人主頁:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343??系列專欄:https://blog.csdn.net/qinjh_/category_12625432.html
目錄
進程程序替換
代碼和現(xiàn)象
替換函數(shù)?
?替換原理
函數(shù)解釋?
命名理解?
簡易shell?
前言
????? hello! 各位鐵子們大家好哇。
? ? ? ? ? ? ?今日更新了Linux的進程替換的內(nèi)容 ????? 歡迎大家關(guān)注?點贊?收藏??留言?
進程程序替換
代碼和現(xiàn)象
運行后,發(fā)現(xiàn)使用了ls命令,而且打印end的語句也不見了。
exec*函數(shù)的作用:讓進程通過exec*函數(shù)把全新的程序替換到自己對應(yīng)的代碼和數(shù)據(jù),然后執(zhí)行新的程序。
exec*函數(shù)執(zhí)行完畢后,后續(xù)的代碼不見了,因為被替換了。
替換函數(shù)?
其實有六種以exec開頭的函數(shù),統(tǒng)稱exec*函數(shù):
int execl(const char *path, const char *arg, ...);int execlp(const char *file, const char *arg, ...);int execle(const char *path, const char *arg, ...,char *const envp[]);int execv(const char *path, char *const argv[]);int execvp(const char *file, char *const argv[]);int execve(const char *path, char *const argv[], char *const envp[]);
?替換原理
我們自己的代碼編譯運行后就會變成可執(zhí)行程序,運行起來后就變成進程。
進程=內(nèi)核數(shù)據(jù)結(jié)構(gòu)+代碼和數(shù)據(jù)
替換的意義是:內(nèi)核數(shù)據(jù)結(jié)構(gòu)不變,個別屬性可能會變,用新程序的代碼代替老程序的代碼。?
進程的替換沒有創(chuàng)建新的進程!所以調(diào)用exec*前后該進程的id并未改變。
站在被替換進程的角度,本質(zhì)就是這個程序由磁盤被加載到內(nèi)存中了。(馮諾依曼體系)
函數(shù)解釋?
這些函數(shù)如果調(diào)用成功則加載新的程序從啟動代碼開始執(zhí)行,不再返回。如果調(diào)用出錯則返回-1所以exec*函數(shù)只有出錯的返回值而沒有成功的返回值。
?如上圖,沒有l(wèi)ss命令,所以替換會失敗。如果替換成功,就不會向后繼續(xù)運行。所以只要繼續(xù)運行了,就一定是替換失敗了。
1 #include
2 #include
3 #include
4 #include
5 #include
6
7 int main()
8 {
9 printf("testexec ... begin!\n");
10
11 pid_t id=fork();
12 if(id==0)
13 {
14 sleep(2);
15 //child
16 execl("/usr/bin/ls","ls","-l","-a",NULL);
17 exit(1);
18 }
19
20 //father
21 int status=0;
22 pid_t rid=waitpid(id,&status,0);
23 if(rid>0)
24 {
25 printf("father wait success,child exit code:%d\n",WEXITSTATUS(status));
26 }
27 printf("testexec ... end!\n");
28 return 0;
29 }
運行結(jié)果如上圖,?進程替換成功了,父進程也等待成功。上面是用fork創(chuàng)建子進程,讓子進程去替換,讓子進程完成任務(wù),而不改變父進程。這也是進程替換的重要意義。
命名理解?
l(list) : 表示參數(shù)采用列表v(vector) : 參數(shù)用數(shù)組p(path) : 有p自動搜索環(huán)境變量PATHe(env) : 表示自己維護環(huán)境變量?
以execl為例,path表示要執(zhí)行的程序的路徑。第二個是可變參數(shù),如果我們用ls命令,后面需要選項的話,就用逗號隔開。選項帶完后,后面必須加上NULL,這是格式要求。
上面的選項參數(shù)的隔開就是l(列表)的體現(xiàn)。
?execv的第一個參數(shù)跟上面的一樣。v就是vector的意思,所以參數(shù)二就是傳數(shù)組。
帶有p的就是 用戶可以不傳執(zhí)行文件的路徑(但是文件名稱要傳)。直接告訴exec*,我想執(zhí)行誰就行。有了p,系統(tǒng)會自動在環(huán)境變量PATH中進行查找。
?
?
注意上面的參數(shù)1表示我想執(zhí)行誰,參數(shù)2表示我想怎么執(zhí)行。?二者含義不一樣。
用一個可執(zhí)行程序替換另一個可執(zhí)行程序:?
當(dāng)我們想要通過make一下就能生成兩個可執(zhí)行程序,可以通過.PHONY設(shè)置一個為目標(biāo),把想要生成的可執(zhí)行文件作為依賴方法,這樣就能同時生成兩個了。
?
上面是通過一個程序替換另一個程序的例子。?
?有了這個例子的基礎(chǔ),接下來介紹execvpe
?mypragma.cc
1 #include
2 #include
3 using namespace std;
4
5 int main(int argc,char *argv[],char* env[])
6 {
7 int i=0;
8 for(;argv[i];i++)
9 {
10 printf("argv[%d]:%s\n",i,argv[i]);
11 }
12
13 printf("------------------------\n");
14 for(i=0;env[i];i++)
15 {
16 printf("env[%d]:%s\n",i,env[i]);
17 }
18 printf("------------------------\n");
19
20
21 cout<<"hello C++,I am a C++ pragma!"< 22 cout<<"hello C++,I am a C++ pragma!"< 23 cout<<"hello C++,I am a C++ pragma!"< 24 cout<<"hello C++,I am a C++ pragma!"< 25 return 0; 26 } testexec.c? 7 int main() 8 { 9 printf("testexec ... begin!\n"); 10 11 pid_t id=fork(); 12 if(id==0) 13 { 14 char* const argv[]= 15 { 16 (char*)"mypragma", 17 NULL 18 }; 19 20 char* const envp[]= 21 { 22 (char*)"HAHA=1111", 23 (char*)"HEHE=2222", 24 NULL 25 }; 26 printf("child pid:%d\n",getpid()); 27 sleep(2); 28 execvpe("./mypragma",argv,envp); 43 exit(1); 44 } 46 //father 47 int status=0; 48 pid_t rid=waitpid(id,&status,0); 49 if(rid>0) 50 { 51 printf("father wait success,child exit code:%d\n",WEXITSTATUS(status)); 52 } 53 printf("testexec ... end!\n"); 54 return 0; 55 } 編譯運行testexec.c程序后,就會用mypragma程序替換。里面的execvpe,參數(shù)1是要替換的文件名,參數(shù)2表示怎么執(zhí)行,參數(shù)3就是環(huán)境變量。參數(shù)2和參數(shù)3都會被傳到替換文件中。所以運行結(jié)果如下圖: 對于main函數(shù)的子進程,它的父進程main本身也是bash的子進程,所以可以通過環(huán)境變量的第三方指針extern char** environ 獲取系統(tǒng)的環(huán)境變量。 所以,參數(shù)3的意義就是整體替換所有的環(huán)境變量。 事實上,只有execve是真正的系統(tǒng)調(diào)用,其它五個函數(shù)最終都調(diào)用 execve,所以execve在man手冊 第2節(jié),其它函數(shù)在 man手冊第3節(jié)。?之所以弄那么多接口,主要是為了支持不同的應(yīng)用場景。 簡易shell? 1 #include 2 #include 3 #include 4 #include 5 #include 6 #include 7 #include 8 9 #define SIZE 512 10 #define ZERO '\0' 11 #define SEP " " 12 #define NUM 32 13 #define SkipPath(p) do{ p += (strlen(p)-1); while(*p != '/') p--; }while(0) 14 15 16 // 為了方便,我就直接定義了 17 char cwd[SIZE*2]; 18 char *gArgv[NUM]; 19 int lastcode = 0; 20 21 void Die() 22 { 23 exit(1); 24 } 25 26 const char *GetHome() 27 { 28 const char *home = getenv("HOME"); 29 if(home == NULL) return "/"; 30 return home; 31 } 32 33 const char *GetUserName() 34 { 35 const char *name = getenv("USER"); 36 if(name == NULL) return "None"; 37 return name; 38 } 39 const char *GetHostName() 40 { 41 const char *hostname = getenv("HOSTNAME"); 42 if(hostname == NULL) return "None"; 43 return hostname; 44 } 45 // 臨時 46 const char *GetCwd() 47 { 48 const char *cwd = getenv("PWD"); 49 if(cwd == NULL) return "None"; 50 return cwd; 51 } 52 53 // commandline : output 54 void MakeCommandLineAndPrint() 55 { 56 char line[SIZE]; 57 const char *username = GetUserName(); 58 const char *hostname = GetHostName(); 59 const char *cwd = GetCwd(); 60 61 SkipPath(cwd); 62 snprintf(line, sizeof(line), "[%s@%s %s]> ", username, hostname, strlen(cwd) == 1 ? "/" : cwd+1); 63 printf("%s", line); 64 fflush(stdout); 65 } 66 67 int GetUserCommand(char command[], size_t n) 68 { 69 char *s = fgets(command, n, stdin); 70 if(s == NULL) return -1; 71 command[strlen(command)-1] = ZERO; 72 return strlen(command); 73 } 74 75 76 void SplitCommand(char command[], size_t n) 77 { 78 (void)n; 79 // "ls -a -l -n" -> "ls" "-a" "-l" "-n" 80 gArgv[0] = strtok(command, SEP); 81 int index = 1; 82 while((gArgv[index++] = strtok(NULL, SEP))); // done, 故意寫成=,表示先賦值,在判斷. 分割之后,strtok會返回NULL,剛好讓gArgv最后一個元素是NULL, 并且while判斷結(jié)束 83 } 84 85 void ExecuteCommand() 86 { 87 pid_t id = fork(); 88 if(id < 0) Die(); 89 else if(id == 0) 90 { 91 // child 92 execvp(gArgv[0], gArgv); 93 exit(errno); 94 } 95 else 96 { 97 // fahter 98 int status = 0; 99 pid_t rid = waitpid(id, &status, 0); 100 if(rid > 0) 101 { 102 lastcode = WEXITSTATUS(status); 103 if(lastcode != 0) printf("%s:%s:%d\n", gArgv[0], strerror(lastcode), lastcode); 104 } 105 } 106 } 107 108 void Cd() 109 { 110 const char *path = gArgv[1]; 111 if(path == NULL) path = GetHome(); 112 // path 一定存在 113 chdir(path); //更改當(dāng)前的工作路徑 114 115 // 刷新環(huán)境變量 116 char temp[SIZE*2];//臨時緩沖區(qū) 117 getcwd(temp, sizeof(temp)); //得到當(dāng)前進程的絕對路徑 118 snprintf(cwd, sizeof(cwd), "PWD=%s", temp);// 119 putenv(cwd); // 導(dǎo)入新的環(huán)境變量 120 } 121 122 int CheckBuildin() 123 { 124 int yes = 0; 125 const char *enter_cmd = gArgv[0]; 126 if(strcmp(enter_cmd, "cd") == 0) 127 { 128 yes = 1; 129 Cd(); 130 } 131 else if(strcmp(enter_cmd, "echo") == 0 && strcmp(gArgv[1], "$?") == 0) 132 { 133 yes = 1; 134 printf("%d\n", lastcode); 135 lastcode = 0; 136 } 137 return yes; 138 } 139 140 141 int main() 142 { 143 int quit = 0; 144 while(!quit) 145 { 146 // 1. 我們需要自己輸出一個命令行 147 MakeCommandLineAndPrint(); 148 149 // 2. 獲取用戶命令字符串 150 char usercommand[SIZE]; 151 int n = GetUserCommand(usercommand, sizeof(usercommand)); 152 if(n <= 0) return 1; 153 154 // 3. 命令行字符串分割. 155 SplitCommand(usercommand, sizeof(usercommand)); 156 157 // 4. 檢測命令是否是內(nèi)建命令 158 n = CheckBuildin(); 159 if(n) continue; 160 // 5. 執(zhí)行命令 161 ExecuteCommand(); 162 } 163 return 0; 164 } 柚子快報激活碼778899分享:運維 【Linux】進程替換 精彩鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。