wordpress 整站帶數(shù)據(jù)互動(dòng)營(yíng)銷(xiāo)的方式有哪些
文章目錄
- 1.進(jìn)程替換原理
- 2.進(jìn)程替換函數(shù)
- 2.1execl函數(shù)
- 2.2execlp函數(shù)
- 2.3execv函數(shù)
- 2.4execvp函數(shù)
- 2.5execle函數(shù)
- 2.6execve函數(shù)
- 2.7跨語(yǔ)言調(diào)用程序
- 3.總結(jié)
1.進(jìn)程替換原理
一個(gè)程序替換的函數(shù):
#include <unistd.h>`
int execl(const char *path, const char *arg, ...);
示例1:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>
int main()
{printf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);execl("/usr/bin/ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());
}
示例2:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);execl("/usr/bin/ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
從上面的代碼運(yùn)行結(jié)果,示例1中可以看出父進(jìn)程使用execl程序替換函數(shù),會(huì)直接替換程序的代碼和數(shù)據(jù);示例2中,父進(jìn)程創(chuàng)建子進(jìn)程使用execl程序替換函數(shù),要替換子進(jìn)程的代碼和數(shù)據(jù)的時(shí)候,觸發(fā)只讀權(quán)限寫(xiě)實(shí)拷貝,OS會(huì)重新開(kāi)辟空間存放替換新程序的代碼和數(shù)據(jù)!
問(wèn)題1:在兩個(gè)示例中,為什么沒(méi)有執(zhí)行以下代碼語(yǔ)句?
printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());
程序替換成功之后,
exec*
后續(xù)的代碼不會(huì)被執(zhí)行;替換失敗呢?才可能執(zhí)行后續(xù)代碼。exec*
函數(shù),只有失敗返回值,沒(méi)有成功返回值!!
問(wèn)題2:CPU是如何得知程序的入口地址?
Linux中形成的可執(zhí)行程序,是有格式的,有一個(gè)描述信息的表,該表里面就描述了當(dāng)前可能程序分了哪些段,包括代碼段,數(shù)據(jù)段等,可執(zhí)行(入口)地址就是表頭地址!CPU把可執(zhí)行程序加載進(jìn)來(lái)的時(shí)候,首先獲得表頭地址(可執(zhí)行程序地址),便可以執(zhí)行程序!
問(wèn)題3:程序替換有沒(méi)有創(chuàng)建新的進(jìn)程?
程序替換沒(méi)有創(chuàng)建新進(jìn)程,只是進(jìn)行進(jìn)程的程序代碼和數(shù)據(jù)的替換工作!
替換原理總結(jié)
用
fork
創(chuàng)建子進(jìn)程后執(zhí)行的是和父進(jìn)程相同的程序(但有可能執(zhí)行不同的代碼分支),子進(jìn)程往往要調(diào)用一種exec
函數(shù)以執(zhí)行另一個(gè)程序。當(dāng)進(jìn)程調(diào)用一種exec
函數(shù)時(shí),該進(jìn)程的用戶(hù)空間代碼和數(shù)據(jù)完全被新程序替換,從新程序的啟動(dòng)例程開(kāi)始執(zhí)行。調(diào)用exec
并不創(chuàng)建新進(jìn)程,所以調(diào)用exec
前后該進(jìn)程的id并未改變。
2.進(jìn)程替換函數(shù)
其實(shí)有六種以exec開(kāi)頭的函數(shù),統(tǒng)稱(chēng)exec函數(shù):
#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 execve(const char *path, char *const argv[], char *const envp[]);
2.1execl函數(shù)
int execl(const char *path, const char *arg, ...);
execl
替換函數(shù),參數(shù)path
中,/usr/bin/ls
作為路徑傳參,需要找到可執(zhí)行程序的位置;execl
替換函數(shù)以鏈表的方式,把功能選項(xiàng)連接起來(lái),則是要確定如何執(zhí)行可執(zhí)行程序!
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替換函數(shù)execl("/usr/bin/ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
使用命令行參數(shù)指令:
#ls -a -l
指令運(yùn)行結(jié)果:
簡(jiǎn)記:execl
替換函數(shù),arg
參數(shù)與命令行參數(shù)傳遞一致,命令行參數(shù)怎么寫(xiě),在execl函數(shù)中就怎么傳!只不過(guò)空格換逗號(hào),加NULL
。
2.2execlp函數(shù)
int execlp(const char *file, const char *arg, ...);
示例:
int execl(const char *path, const char *arg, ...);
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替換函數(shù)execlp("ls","ls","-l","-a",NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
execlp
替換函數(shù),函數(shù)中名中p
指的是環(huán)境變量PATH
,指令的默認(rèn)路徑,執(zhí)行進(jìn)程時(shí)系統(tǒng)會(huì)去默認(rèn)路徑尋找,執(zhí)行系統(tǒng)提供的指令時(shí)不需要帶上地址;參數(shù)file
中,ls
作為文件名傳參,需要在默認(rèn)路徑下找到對(duì)應(yīng)的可執(zhí)行程序;"ls","-l","-a",NULL
,execl
替換函數(shù)以鏈表的方式,把功能選項(xiàng)連接起來(lái),則是要確定如何執(zhí)行可執(zhí)行程序!
2.3execv函數(shù)
int execv(const char *path, char *const argv[]);
execv
替換函數(shù)名中的v
是指vector
(數(shù)組),把可執(zhí)行程序的執(zhí)行選項(xiàng)功能放在數(shù)組中,在數(shù)組中以NULL
為結(jié)尾!參數(shù)path
,把可執(zhí)行程序的位置作為參數(shù)傳遞;參數(shù)argv
,把自定義選項(xiàng)功能數(shù)組作為參數(shù)傳遞!
示例:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//數(shù)組參數(shù)char* const myargv[]={"ls","-a","-l",NULL};//替換函數(shù)execv("/usr/bin/ls",myargv);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
2.4execvp函數(shù)
int execvp(const char *file, char *const argv[]);
execvp
替換函數(shù)名中的v
是指vector
(數(shù)組),把可執(zhí)行程序的執(zhí)行選項(xiàng)功能放在數(shù)組中,在數(shù)組中以NULL
為結(jié)尾;函數(shù)中名中p
指的是環(huán)境變量PATH
,執(zhí)行系統(tǒng)程序時(shí)不需要帶路徑;參數(shù)file
,把可執(zhí)行程序的位置作為參數(shù)傳遞;參數(shù)argv
,把自定義選項(xiàng)功能數(shù)組作為參數(shù)傳遞!
示例:
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//數(shù)組參數(shù)char* const myargv[]={"ls","-a","-l",NULL};//替換函數(shù)execvp("ls",myargv);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
2.5execle函數(shù)
int execle(const char *path, const char *arg, ...,char *const envp[]);
execle
替換函數(shù)名是在‘execl’
函數(shù)基礎(chǔ)上,新增加了envp
參數(shù)。可以傳遞‘environ’
環(huán)境變量參數(shù),libc
中定義的全局變量environ
指向環(huán)境變量表,environ
沒(méi)有包含在任何頭文件中,所以在使用時(shí) 要用extern
聲明。因?yàn)榄h(huán)境變量通常具有全局屬性,子進(jìn)程繼承父進(jìn)程的環(huán)境變量;也可以自己設(shè)置自定義新的環(huán)境變量數(shù)組,傳遞給子進(jìn)程!
示例1:
//ohtherExe.cpp
#include <iostream>
using namespace std;int main(int argc, char *argv[], char *env[])
{cout << argv[0] << " begin running" << endl;cout << "這是命令行參數(shù): \n";for(int i=0; argv[i]; i++){cout << i << " : " << argv[i] << endl;}cout << "這是環(huán)境變量信息: \n";for(int i = 0; env[i]; i++){cout << i << " : " << env[i] << endl; }cout<<"xxxxxxxxxxxxxxxxx"<<endl; return 0;
}
//mycommand.c
//新增extern char** environ;//替換函數(shù)execle("./otherExe","otherExe","-a","-b","c",NULL,environ);
代碼運(yùn)行結(jié)果為:
示例2:
//mycommand.c
//新增部分
char *const myenv[] = {"MYVAL=1111","MYPATH=/usr/bin/XXX",NULL
};
//替換函數(shù)為
execle("./otherExe", "otherExe", "-a", "-w", "-v", NULL, myenv);
代碼運(yùn)行結(jié)果為:
2.6execve函數(shù)
int execve(const char *path, char *const argv[], char *const envp[]);
execve
替換函數(shù)名中的v
是指vector
(數(shù)組),把可執(zhí)行程序的執(zhí)行選項(xiàng)功能放在數(shù)組中,在數(shù)組中以NULL
為結(jié)尾;envp
參數(shù),傳遞環(huán)境變量為參數(shù)。
示例:
char* const myargv[]={"otherExe","-a","-l",NULL};char* const myenv[]={"MYVAL=111111","MYPATH=/usr/bin/xxx",NULL};extern char** environ;
代碼運(yùn)行的結(jié)果為:
注意:
libc
中定義的全局變量environ
指向環(huán)境變量表,environ
沒(méi)有包含在任何頭文件中,所以在使用時(shí)要用extern
聲明;我們?nèi)绻虢o子進(jìn)程傳遞環(huán)境變量,有兩種方式:①新增環(huán)境變量,在父進(jìn)程中使用putenv
函數(shù)自己設(shè)置的環(huán)境變量;②徹底替換(覆蓋)環(huán)境變量,使用帶“e”
選項(xiàng)的exec
函數(shù)(execle/execve
函數(shù)),傳遞自己設(shè)置環(huán)境變量數(shù)組;
2.7跨語(yǔ)言調(diào)用程序
在上面的例子中,我們都是使用我們自己寫(xiě)的mycommand.c
程序調(diào)用系統(tǒng)的程序(指令),那我們是否可以調(diào)用自己所寫(xiě)的程序呢?答案是當(dāng)然可以!
示例1:
python代碼
#!/usr/bin/python3print("hello Python!")
#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
#include<sys/wait.h>
#include<stdlib.h>int main()
{pid_t id=fork();if(id == 0){//childprintf("before: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());sleep(5);//替換函數(shù)execl("/usr/bin/python3", "python3", "test.py", NULL);printf("after: I am a process: pid: %d , ppid:%d\n",getpid(),getppid());exit(0);}//fatherpid_t ret=waitpid(id,NULL,0);if(ret > 0){printf("wait success, father pid:%d, ret id: %d\n",getpid(),ret);sleep(5);}return 0;
}
代碼運(yùn)行結(jié)果如下:
為什么我們可以用C語(yǔ)言程序調(diào)用Python可執(zhí)行程序?
無(wú)論什么語(yǔ)言的可執(zhí)行程序運(yùn)行起來(lái)之后變成了進(jìn)程,便可以通過(guò)在子進(jìn)程中使用exec
系列函數(shù)替換達(dá)到調(diào)用的效果!
3.總結(jié)
①exec
系列函數(shù)如果調(diào)用成功,則加載新的程序從啟動(dòng)代碼開(kāi)始執(zhí)行不再返回。如果調(diào)用出錯(cuò)則返回-1,所以exec
函數(shù)只有出錯(cuò)的返回值,而沒(méi)有成功的返回值。
②命名理解記憶
l(list) : 表示參數(shù)采用列表
v(vector) : 參數(shù)用數(shù)組
p(path) : 有p自動(dòng)搜索環(huán)境變量PATH
e(env) : 表示自己維護(hù)環(huán)境變量
③只有execve
函數(shù)是真正的系統(tǒng)調(diào)用,其它五個(gè)函數(shù)最終都調(diào)用execve
函數(shù),所以execve在man手冊(cè) 第2節(jié),其它函數(shù)在man
手冊(cè)第3節(jié)。這些函數(shù)之間的關(guān)系如下圖所示: