Linux:Linux:進程終止和進程替換
- 一、進程終止
- 1.1 進程退出場景和創建退出方式
- 1.2 exit 和 _exit區別
- 二、進程程序替換
- 2.1 進程替換函數
- 2.2 函數解釋及命名解釋
- 函數解釋
- 命名解釋
- 2.3 單進程程序替換(無子進程)
- 2.3.1 帶`l`函數進程替換(execl為例)
- 2.3.2 帶`p‘函數進程替換(execlp為例)
- 2.3.3 execv、execvp替換函數應用實例
- 2.4 進程替換其他程序,調用運行其他語言程序
- 三、進程替換時環境變量的繼承
- 3.1 進程替換時,子進程環境變量由來
- 3.2 為何父子進程間環境變量的繼承不受進程替換的影響
- 3.3 子進程獲取環境變量的3種方式
一、進程終止
1.1 進程退出場景和創建退出方式
?進程退出有3種場景:代碼執行完畢,結果正確;代碼執行完畢,結果錯誤;代碼異常終止!
?而進程退出的常見方式主要分為以下兩大類:
正常終止 | |
從main返回 | |
調用exit退出 | |
調用_exit退出 | |
異常終止 | ctrl c, 信號終止 |
1.2 exit 和 _exit區別
? exit 和 _exit都可以直接終止進程。但_exit是系統調用接口,而exit為函數調用,底層封裝了exit。
?不同的是,exit終止進程時,會刷新緩沖區,執行用戶的清理函數,關閉流等操作;而_exit則是直接“粗暴”的退出進程,不做任何其他工作!!
二、進程程序替換
?fork()創建子進程時,子進程執行的代碼和數據都是父進程的一部分。如果我們想讓子進程執行全新的代碼,訪問全新的數據,我們可以采用一種技術 —— 程序替換!而進程替換可以將命令行參數和環境變量傳遞給被替換程序的main()函數參數!!
2.1 進程替換函數
?進程替換函數有6種以exec開頭的函數,統稱為exec函數。
#include <unistd.h>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 execvpe(const char *file, char *const argv[], char *const envp[]);
?而exec函數底層都封裝了系統調用接口execve的瘋轉,以實現不同的需求!
#include <unistd.h>int execve(const char *filename, char *const argv[], char *const envp[]);
2.2 函數解釋及命名解釋
函數解釋
- 如果這些函數調用成功,也就意味著進程替換成功。此時重新加載新的程序,從啟動代碼開始執行,并且不再返回。即進程替換后,執行新程序,執行完后直接退出!!
- 如果進程替換函數執行失敗,此時返回值設為-1。
- exec函數只有出錯的返回值,沒有成功的返回值。
命名解釋
- l(list):參數采用列表形式。
- v(vector):參數采用數組。
- p(path):帶p表示執行程序時,OS會自帶去環境變量PATH中查找路徑。
- e(env):表示自己維護環境變量。
2.3 單進程程序替換(無子進程)
2.3.1 帶l
函數進程替換(execl為例)
?下面我們在一段代碼的開頭和結尾分別輸出打印相關信息,然后兩段信息輸出代碼直接調用execl
替換ls -a-l
。
【源代碼】:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main()
{printf("pid: %d, ecec command begin!\n", getpid());//進程替換,執行ls指令相關程序execl("/usr/bin/ls", "ls", "-a", "-l", NULL);printf("pid: %d, ecec command end!\n", getpid());return 0;
}
【運行結果】:
【函數參數原型解釋】:
2.3.2 帶`p‘函數進程替換(execlp為例)
【源代碼】:(頭文件省略)
int main()
{printf("pid: %d, ecec command begin!\n", getpid());execlp("pwd", "pwd", NULL);printf("pid: %d, ecec command end!\n", getpid());return 0;
}
【運行結果】:
【函數參數原型解釋】:
2.3.3 execv、execvp替換函數應用實例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>int main()
{char *const argv[] = {"ls","-l","-a",NULL};printf("pid: %d, ecec command begin!\n", getpid());、// 和execl、execlv類似,只不過下面函數時通過指針數組的方式,指明替換程序的執行方式!!execv("/usr/bin/ls", argv);execvp("ls", argv);printf("pid: %d, ecec command end!\n", getpid());return 0;
}
2.4 進程替換其他程序,調用運行其他語言程序
?上述所有的程序替換都是替換系統指令程序,那如何替換自己寫的程序。
?下面我們在c程序中創建子進程,讓子進程發送進程替換一段c++可執行程序,并且父進程等待子進程!!
【待替換C++程序】:
#include <iostream>int main()
{std::cout << "hello c++!" << std::endl;std::cout << "hello c++!" << std::endl;std::cout << "hello c++!" << std::endl;return 0;
}
【主代碼C程序】:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>extern char **environ;int main()
{pid_t id = fork();if(id == 0){printf("pid: %d, ecec command begin!\n", getpid());execl("./mytest", "mytest");//替換C++程序//替換失敗,執行下面代碼printf("pid: %d, ecec command end!\n", getpid());exit(1);}pid_t rid = waitpid(-1, NULL, 0);if(rid == id){printf("wait pid: %d success!!!\n", rid);}return 0;
}
【運行結果】:
- 為啥在c程序中,可以直接替換c++程序?根本原因在于exec函數發生的是進程替換,任何語言程序一旦運行起來就變成了進程,便可發生進程替換。系統大于一切!!
三、進程替換時環境變量的繼承
3.1 進程替換時,子進程環境變量由來
?環境變量是數據,所有可以通過地址空間實現父子間通過寫時拷貝的方式共享數據 —— 環境變量。所以當通過exec函數進行進程替換時,子進程的環境變量是直接從父進程來的。
3.2 為何父子進程間環境變量的繼承不受進程替換的影響
?父進程和子進程間通過寫時拷貝的方式,讓環境變量別子進程繼承,從而實現環境變量的全局性!但為何調用exec函數進行進程替換后,環境變量沒有發生修改,變為被替換程序的環境變量?
?原因很簡單,程序替換,只替換新程序的代碼和數據,環境變量不會被替換!!
3.3 子進程獲取環境變量的3種方式
- 操作系統直接將環境變量傳遞給子進程,子進程直接用。或者直接通過
execle、execvpe
的最后一個參數直接傳遞給子進程!
【實例】:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;int main()
{pid_t id = fork();if(id == 0){execle("./mytest", "mytest", NULL, environ);//進程替換,直接將environ傳遞給子進程exit(1);}pid_t rid = waitpid(-1, NULL, 0);if(rid == id){printf("wait pid: %d success!!!\n", rid);}return 0;
}
- 直接構造自己的環境變量表傳遞給子進程。
【實例】:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
extern char **environ;int main()
{pid_t id = fork();if(id == 0){execle("./mytest", "mytest", NULL, environ);//進程替換,直接將environ傳遞給子進程exit(1);}pid_t rid = waitpid(-1, NULL, 0);if(rid == id){char *const envp[] = {"MYENV1 = 111111111111111111","MYENV2 = 111111111111111111","MYENV3 = 111111111111111111",NULL};execle("./mytest", "mytest", NULL, envp);//直接將自己構造的函數變量表envp傳遞給子進程}return 0;
}
- 借助putenv(),新增環境變量給父進程然后傳遞給子進程
【實例】:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>extern char **environ;int main()
{pid_t id = fork();if(id == 0){char *myenv = "MYENV = 111111111111111111";putenv(myenv);//將環境變量MYENV添加到父進程環境變量表中execl("./mytest", "mytest");//直接傳遞給子進程exit(1);}pid_t rid = waitpid(-1, NULL, 0);if(rid == id){printf("wait pid: %d success!!!\n", rid);}return 0;
}