exec族函數(shù)的作用
exec函數(shù)族的作用是根據(jù)指定的文件名找到可執(zhí)行文件,并用它來(lái)取代調(diào)用進(jìn)程的內(nèi)容,換句話說(shuō),就是在調(diào)用進(jìn)程內(nèi)部執(zhí)行一個(gè)可執(zhí)行文件。這里的可執(zhí)行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執(zhí)行的腳本文件。
與一般情況不同,exec函數(shù)族的函數(shù)執(zhí)行成功后不會(huì)返回,因?yàn)檎{(diào)用進(jìn)程的實(shí)體,包括代碼段,數(shù)據(jù)段和堆棧等都已經(jīng)被新的內(nèi)容取代,只留下進(jìn)程ID等一些表面上的信息仍保持原樣,頗有些神似"三十六計(jì)"中的"金蟬脫殼"。看上去還是舊的軀殼,卻已經(jīng)注入了新的靈魂。只有調(diào)用失敗了,它們才會(huì)返回一個(gè)-1,從原程序的調(diào)用點(diǎn)接著往下執(zhí)行。
現(xiàn)在我們應(yīng)該明白了,Linux下是如何執(zhí)行新程序的,每當(dāng)有進(jìn)程認(rèn)為自己不能為系統(tǒng)和用戶做出任何貢獻(xiàn)了,他就可以發(fā)揮最后一點(diǎn)余熱,調(diào)用任何一個(gè)exec,讓自己以新的面貌重生;或者,更普遍的情況是,如果一個(gè)進(jìn)程想執(zhí)行另一個(gè)程序,它就可以fork出一個(gè)新進(jìn)程,然后調(diào)用任何一個(gè)exec,這樣看起來(lái)就好像通過(guò)執(zhí)行應(yīng)用程序而產(chǎn)生了一個(gè)新進(jìn)程一樣。
事實(shí)上第二種情況被應(yīng)用得如此普遍,以至于Linux專門為其作了優(yōu)化,我們已經(jīng)知道,fork會(huì)將調(diào)用進(jìn)程的所有內(nèi)容原封不動(dòng)的拷貝到新產(chǎn)生的子進(jìn)程中去,這些拷貝的動(dòng)作很消耗時(shí)間,而如果fork完之后我們馬上就調(diào)用exec,這些辛辛苦苦拷貝來(lái)的東西又會(huì)被立刻抹掉,這看起來(lái)非常不劃算,于是人們?cè)O(shè)計(jì)了一種"寫時(shí)拷貝(copy-on-write)"技術(shù),使得fork結(jié)束后并不立刻復(fù)制父進(jìn)程的內(nèi)容,而是到了真正實(shí)用的時(shí)候才復(fù)制,這樣如果下一條語(yǔ)句是exec,它就不會(huì)白白作無(wú)用功了,也就提高了效率。
對(duì)于新程序的命令行參數(shù)和環(huán)境表有長(zhǎng)度大小的限制,對(duì)于linux來(lái)講這個(gè)限制是4096個(gè)字節(jié)。執(zhí)行了exec函數(shù)的進(jìn)程不改變以下進(jìn)程特征:
1>進(jìn)程ID和父進(jìn)程ID
2>實(shí)際用戶ID和實(shí)際組ID
3>進(jìn)程組ID和附加組ID
4>控制終端
5>會(huì)話ID
6>時(shí)鐘預(yù)留著時(shí)間
7>當(dāng)前工作目錄和根目錄
8>文件創(chuàng)建屏蔽字和文件鎖
9>信號(hào)屏蔽字和未處理信號(hào)集
10>資源限制
返回值
如果執(zhí)行成功則函數(shù)不會(huì)返回,執(zhí)行失敗則直接返回-1,失敗原因存于errno 中。
應(yīng)用時(shí)注意
大家在平時(shí)的編程中,如果用到了exec函數(shù)族,一定記得要加錯(cuò)誤判斷語(yǔ)句。因?yàn)榕c其他系統(tǒng)調(diào)用比起來(lái),exec很容易受傷,被執(zhí)行文件的位置,權(quán)限等很多因素都能導(dǎo)致該調(diào)用的失敗。最常見(jiàn)的錯(cuò)誤是:
1.找不到文件或路徑,此時(shí)errno被設(shè)置為ENOENT;
2.數(shù)組argv和envp忘記用NULL結(jié)束,此時(shí)errno被設(shè)置為EFAULT;
3.沒(méi)有對(duì)要執(zhí)行文件的運(yùn)行權(quán)限,此時(shí)errno被設(shè)置為EACCES。
l表示以參數(shù)列表的形式調(diào)用
v表示以參數(shù)數(shù)組的方式調(diào)用
e表示可傳遞環(huán)境變量
p表示PATH中搜索執(zhí)行的文件,如果給出的不是絕對(duì)路徑就會(huì)去PATH搜索相應(yīng)名字的文件,如PATH沒(méi)有設(shè)置,則會(huì)默認(rèn)在/bin,/usr/bin下搜索。
另:調(diào)用時(shí)參數(shù)必須以NULL結(jié)束。原進(jìn)程打開(kāi)的文件描述符是不會(huì)在exec中關(guān)閉的,除非用fcntl設(shè)置它們的“執(zhí)行時(shí)關(guān)閉標(biāo)志(close on exec)”而原進(jìn)程打開(kāi)的目錄流都將在新進(jìn)程中關(guān)閉。
例子
#include
int main(int argc, char *argv[])
{
char *envp[]={"PATH=/tmp", "USER=lei", "STATUS=testing", NULL};
char *argv_execv[]={"echo", "excuted by execv", NULL};
char *argv_execvp[]={"echo", "executed by execvp", NULL};
char *argv_execve[]={"env", NULL};
if(fork()==0) {
if(execl("/bin/echo", "echo", "executed by execl", NULL)<0)
perror("Err on execl");
}
if(fork()==0) {
if(execlp("echo", "echo", "executed by execlp", NULL)<0)
perror("Err on execlp");
}
if(fork()==0) {
if(execle("/usr/bin/env", "env", NULL, envp)<0)
perror("Err on execle");
}
if(fork()==0) {
if(execv("/bin/echo", argv_execv)<0)
perror("Err on execv");
}
if(fork()==0) {
if(execvp("echo", argv_execvp)<0)
perror("Err on execvp");
}
if(fork()==0) {
if(execve("/usr/bin/env", argv_execve, envp)<0)
perror("Err on execve");
}
}
/* execv example */
#include
#include
#include
void main(int argc, char *argv[])
{
int i;
printf("Command line arguments:
");
for (i=0; iprintf("[-] : %s
", i, argv);
printf("About to exec child with arg1 arg2 ...
");
execv("CHILD.EXE", argv);
perror("exec error");
exit(1);
}
Windows XP SP3網(wǎng)絡(luò)診斷工具文件中的命令
Exec:xpnetdiag.exe是Windows XP SP3網(wǎng)絡(luò)診斷工具文件,用于診斷當(dāng)前網(wǎng)絡(luò)的連接狀況。
fscommand("exec");
flash 中在放映文件內(nèi)執(zhí)行應(yīng)用程序用。
CISCO中的EXEC
在Cisco 路由器中,命令解釋器稱為EXEC,EXEC解釋用戶鍵入的命令并執(zhí)行相應(yīng)
的操作,在輸入EXEC命令前必須先登錄到路由器上?;诎踩?,EXEC設(shè)置了
兩個(gè)訪問(wèn)權(quán)限:用戶級(jí)和特權(quán)級(jí),用戶級(jí)可執(zhí)執(zhí)行的命令是特權(quán)級(jí)命令的子集。
在特權(quán)級(jí),可以使用:configuration,interface,subinterface,line,rout
er,router-map等命令。
docker中的EXEC
我們?cè)趹?yīng)用容器的過(guò)程中,無(wú)論是在通過(guò)Dockerfile在調(diào)試構(gòu)建鏡像的過(guò)程,還是容器運(yùn)行一段時(shí)間想查看內(nèi)部結(jié)構(gòu),我們還是希望能像操作本地機(jī)器一樣,實(shí)時(shí)的查看容器內(nèi)部文件,代碼或者日志。或是修改文件,拷貝文件目錄等等。
- 訪問(wèn)容器內(nèi)部,目前有兩種方法
1. Docker自帶的exec命令
2. Nsenter工具
- 來(lái)說(shuō)說(shuō)Docker exec 命令方式訪問(wèn)
- 如圖所示,簡(jiǎn)單的ls命令。Linux系統(tǒng)自帶的命令都可以通過(guò)這種方式運(yùn)行。文件放錯(cuò)位置了,mv一下,查看log,就cat log.log一下,等等。
Exec加點(diǎn)料
- 簡(jiǎn)單的操作不能滿足我們對(duì)他的好奇...
- 我們運(yùn)行一下docker exec -ti 61f ps -ef
- 發(fā)現(xiàn)只有3個(gè)進(jìn)程,進(jìn)程1是CMD命令啟動(dòng)的腳本;進(jìn)程2是腳本啟動(dòng)的程序;進(jìn)程3是我們運(yùn)行ps -ef的進(jìn)程。
- 出于好奇,我又docker exec 9fe0 kill 15
- 容器從此就停止了...
- 原因是殺掉進(jìn)程后,Dockerfile指定的CMD["/run.sh"]腳本運(yùn)行結(jié)束,CMD入口已經(jīng)退出,導(dǎo)致容器退出。
問(wèn)題還沒(méi)結(jié)束
- Kubernetes是一個(gè)基于docker的容器集群管理系統(tǒng),它的一大特點(diǎn)是擁有Replication Controllers,他的作用主要是保持所起動(dòng)的Pod數(shù)量不變(pod里面裝的是container)。我猜想如果類似的kill掉容器內(nèi)部的進(jìn)程,那么kubernetes應(yīng)該會(huì)讓這個(gè)container重新啟動(dòng)。于是就來(lái)動(dòng)手試試。
- 時(shí)速云他們應(yīng)用了kubernetes,并且提供了客戶端tce可以使用exec功能。運(yùn)行 tce exec bbb-145fv-zkdqz ps -ef
- tce exec bbb-145fv-zkdqz kill 15
沒(méi)有重啟??
- 再次運(yùn)行 tce exec bbb-145fv-zkdqz ps -ef
- 又出現(xiàn)了。
- 由此可見(jiàn)Kubernentes的Replication Controllers還是很強(qiáng)大的。保證了集群中有指定數(shù)量的pod副本在運(yùn)行。