欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久

打開(kāi)APP
userphoto
未登錄

開(kāi)通VIP,暢享免費電子書(shū)等14項超值服

開(kāi)通VIP
Linux下的進(jìn)程的
本文介紹了Linux下的進(jìn)程的一些概念,并著(zhù)重講解了與Linux進(jìn)程管理相關(guān)的重要系統調用wait,waitpid和exec函數族,輔助一些例程說(shuō)明了它們的特點(diǎn)和使用方法。
在前面的文章中,我們已經(jīng)了解了父進(jìn)程和子進(jìn)程的概念,并已經(jīng)掌握了系統調用exit的用法,但可能很少有人意識到,在一個(gè)進(jìn)程調用了exit之后,該進(jìn)程并非馬上就消失掉,而是留下一個(gè)稱(chēng)為僵尸進(jìn)程(Zombie)的數據結構。在Linux進(jìn)程的5種狀態(tài)中,僵尸進(jìn)程是非常特殊的一種,它已經(jīng)放棄了幾乎所有內存空間,沒(méi)有任何可執行代碼,也不能被調度,僅僅在進(jìn)程列表中保留一個(gè)位置,記載該進(jìn)程的退出狀態(tài)等信息供其他進(jìn)程收集,除此之外,僵尸進(jìn)程不再占有任何內存空間。從這點(diǎn)來(lái)看,僵尸進(jìn)程雖然有一個(gè)很酷的名字,但它的影響力遠遠抵不上那些真正的僵尸兄弟,真正的僵尸總能令人感到恐怖,而僵尸進(jìn)程卻除了留下一些供人憑吊的信息,對系統毫無(wú)作用。
也許讀者們還對這個(gè)新概念比較好奇,那就讓我們來(lái)看一眼Linux里的僵尸進(jìn)程究竟長(cháng)什么樣子。
當一個(gè)進(jìn)程已退出,但其父進(jìn)程還沒(méi)有調用系統調用wait(稍后介紹)對其進(jìn)行收集之前的這段時(shí)間里,它會(huì )一直保持僵尸狀態(tài),利用這個(gè)特點(diǎn),我們來(lái)寫(xiě)一個(gè)簡(jiǎn)單的小程序:
/* zombie.c */ #include <sys/types.h> #include <unistd.h> main() { pid_t pid; pid=fork(); if(pid<0) /* 如果出錯 */ printf("error occurred!\n"); else if(pid==0) /* 如果是子進(jìn)程 */ exit(0); else /* 如果是父進(jìn)程 */ sleep(60); /* 休眠60秒,這段時(shí)間里,父進(jìn)程什么也干不了 */ wait(NULL); /* 收集僵尸進(jìn)程 */ }
sleep的作用是讓進(jìn)程休眠指定的秒數,在這60秒內,子進(jìn)程已經(jīng)退出,而父進(jìn)程正忙著(zhù)睡覺(jué),不可能對它進(jìn)行收集,這樣,我們就能保持子進(jìn)程60秒的僵尸狀態(tài)。
編譯這個(gè)程序:
$ cc zombie.c -o zombie
后臺運行程序,以使我們能夠執行下一條命令
$ ./zombie & [1] 1577
列一下系統內的進(jìn)程
$ ps -ax ... ... 1177 pts/0 S 0:00 -bash 1577 pts/0 S 0:00 ./zombie 1578 pts/0 Z 0:00 [zombie <defunct>] 1579 pts/0 R 0:00 ps -ax
看到中間的"Z"了嗎?那就是僵尸進(jìn)程的標志,它表示1578號進(jìn)程現在就是一個(gè)僵尸進(jìn)程。
我們已經(jīng)學(xué)習了系統調用exit,它的作用是使進(jìn)程退出,但也僅僅限于將一個(gè)正常的進(jìn)程變成一個(gè)僵尸進(jìn)程,并不能將其完全銷(xiāo)毀。僵尸進(jìn)程雖然對其他進(jìn)程幾乎沒(méi)有什么影響,不占用CPU時(shí)間,消耗的內存也幾乎可以忽略不計,但有它在那里呆著(zhù),還是讓人覺(jué)得心里很不舒服。而且Linux系統中進(jìn)程數目是有限制的,在一些特殊的情況下,如果存在太多的僵尸進(jìn)程,也會(huì )影響到新進(jìn)程的產(chǎn)生。那么,我們該如何來(lái)消滅這些僵尸進(jìn)程呢?
先來(lái)了解一下僵尸進(jìn)程的來(lái)由,我們知道,Linux和UNIX總有著(zhù)剪不斷理還亂的親緣關(guān)系,僵尸進(jìn)程的概念也是從UNIX上繼承來(lái)的,而UNIX的先驅們設計這個(gè)東西并非是因為閑來(lái)無(wú)聊想煩煩其他的程序員。僵尸進(jìn)程中保存著(zhù)很多對程序員和系統管理員非常重要的信息,首先,這個(gè)進(jìn)程是怎么死亡的?是正常退出呢,還是出現了錯誤,還是被其它進(jìn)程強迫退出的?其次,這個(gè)進(jìn)程占用的總系統CPU時(shí)間和總用戶(hù)CPU時(shí)間分別是多少?發(fā)生頁(yè)錯誤的數目和收到信號的數目。這些信息都被存儲在僵尸進(jìn)程中,試想如果沒(méi)有僵尸進(jìn)程,進(jìn)程一退出,所有與之相關(guān)的信息都立刻歸于無(wú)形,而此時(shí)程序員或系統管理員需要用到,就只好干瞪眼了。
那么,我們如何收集這些信息,并終結這些僵尸進(jìn)程呢?就要靠我們下面要講到的waitpid調用和wait調用。這兩者的作用都是收集僵尸進(jìn)程留下的信息,同時(shí)使這個(gè)進(jìn)程徹底消失。下面就對這兩個(gè)調用分別作詳細介紹。
回頁(yè)首
wait的函數原型是:
#include <sys/types.h> /* 提供類(lèi)型pid_t的定義 */ #include <sys/wait.h> pid_t wait(int *status)
進(jìn)程一旦調用了wait,就立即阻塞自己,由wait自動(dòng)分析是否當前進(jìn)程的某個(gè)子進(jìn)程已經(jīng)退出,如果讓它找到了這樣一個(gè)已經(jīng)變成僵尸的子進(jìn)程,wait就會(huì )收集這個(gè)子進(jìn)程的信息,并把它徹底銷(xiāo)毀后返回;如果沒(méi)有找到這樣一個(gè)子進(jìn)程,wait就會(huì )一直阻塞在這里,直到有一個(gè)出現為止。
參數status用來(lái)保存被收集進(jìn)程退出時(shí)的一些狀態(tài),它是一個(gè)指向int類(lèi)型的指針。但如果我們對這個(gè)子進(jìn)程是如何死掉的毫不在意,只想把這個(gè)僵尸進(jìn)程消滅掉,(事實(shí)上絕大多數情況下,我們都會(huì )這樣想),我們就可以設定這個(gè)參數為NULL,就象下面這樣:
pid = wait(NULL);
如果成功,wait會(huì )返回被收集的子進(jìn)程的進(jìn)程ID,如果調用進(jìn)程沒(méi)有子進(jìn)程,調用就會(huì )失敗,此時(shí)wait返回-1,同時(shí)errno被置為ECHILD。
下面就讓我們用一個(gè)例子來(lái)實(shí)戰應用一下wait調用,程序中用到了系統調用fork,如果你對此不大熟悉或已經(jīng)忘記了,請參考上一篇文章《進(jìn)程管理相關(guān)的系統調用(一)》。
/* wait1.c */ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> #include <stdlib.h> main() { pid_t pc,pr; pc=fork(); if(pc<0) /* 如果出錯 */ printf("error ocurred!\n"); else if(pc==0){ /* 如果是子進(jìn)程 */ printf("This is child process with pid of %d\n",getpid()); sleep(10); /* 睡眠10秒鐘 */ } else{ /* 如果是父進(jìn)程 */ pr=wait(NULL); /* 在這里等待 */ printf("I catched a child process with pid of %d\n"),pr); } exit(0); }
編譯并運行:
$ cc wait1.c -o wait1 $ ./wait1 This is child process with pid of 1508 I catched a child process with pid of 1508
可以明顯注意到,在第2行結果打印出來(lái)前有10秒鐘的等待時(shí)間,這就是我們設定的讓子進(jìn)程睡眠的時(shí)間,只有子進(jìn)程從睡眠中蘇醒過(guò)來(lái),它才能正常退出,也就才能被父進(jìn)程捕捉到。其實(shí)這里我們不管設定子進(jìn)程睡眠的時(shí)間有多長(cháng),父進(jìn)程都會(huì )一直等待下去,讀者如果有興趣的話(huà),可以試著(zhù)自己修改一下這個(gè)數值,看看會(huì )出現怎樣的結果。
如果參數status的值不是NULL,wait就會(huì )把子進(jìn)程退出時(shí)的狀態(tài)取出并存入其中,這是一個(gè)整數值(int),指出了子進(jìn)程是正常退出還是被非正常結束的(一個(gè)進(jìn)程也可以被其他進(jìn)程用信號結束,我們將在以后的文章中介紹),以及正常結束時(shí)的返回值,或被哪一個(gè)信號結束的等信息。由于這些信息被存放在一個(gè)整數的不同二進(jìn)制位中,所以用常規的方法讀取會(huì )非常麻煩,人們就設計了一套專(zhuān)門(mén)的宏(macro)來(lái)完成這項工作,下面我們來(lái)學(xué)習一下其中最常用的兩個(gè):
1,WIFEXITED(status) 這個(gè)宏用來(lái)指出子進(jìn)程是否為正常退出的,如果是,它會(huì )返回一個(gè)非零值。
(請注意,雖然名字一樣,這里的參數status并不同于wait唯一的參數--指向整數的指針status,而是那個(gè)指針所指向的整數,切記不要搞混了。)
2,WEXITSTATUS(status) 當WIFEXITED返回非零值時(shí),我們可以用這個(gè)宏來(lái)提取子進(jìn)程的返回值,如果子進(jìn)程調用exit(5)退出,WEXITSTATUS(status)就會(huì )返回5;如果子進(jìn)程調用exit(7),WEXITSTATUS(status)就會(huì )返回7。請注意,如果進(jìn)程不是正常退出的,也就是說(shuō),WIFEXITED返回0,這個(gè)值就毫無(wú)意義。
下面通過(guò)例子來(lái)實(shí)戰一下我們剛剛學(xué)到的內容:
/* wait2.c */ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> main() { int status; pid_t pc,pr; pc=fork(); if(pc<0) /* 如果出錯 */ printf("error ocurred!\n"); else if(pc==0){ /* 子進(jìn)程 */ printf("This is child process with pid of %d.\n",getpid()); exit(3); /* 子進(jìn)程返回3 */ } else{ /* 父進(jìn)程 */ pr=wait(&status); if(WIFEXITED(status)){ /* 如果WIFEXITED返回非零值 */ printf("the child process %d exit normally.\n",pr); printf("the return code is %d.\n",WEXITSTATUS(status)); }else /* 如果WIFEXITED返回零 */ printf("the child process %d exit abnormally.\n",pr); } }
編譯并運行:
$ cc wait2.c -o wait2 $ ./wait2 This is child process with pid of 1538. the child process 1538 exit normally. the return code is 3.
父進(jìn)程準確捕捉到了子進(jìn)程的返回值3,并把它打印了出來(lái)。
當然,處理進(jìn)程退出狀態(tài)的宏并不止這兩個(gè),但它們當中的絕大部分在平時(shí)的編程中很少用到,就也不在這里浪費篇幅介紹了,有興趣的讀者可以自己參閱Linux man pages去了解它們的用法。
有時(shí)候,父進(jìn)程要求子進(jìn)程的運算結果進(jìn)行下一步的運算,或者子進(jìn)程的功能是為父進(jìn)程提供了下一步執行的先決條件(如:子進(jìn)程建立文件,而父進(jìn)程寫(xiě)入數據),此時(shí)父進(jìn)程就必須在某一個(gè)位置停下來(lái),等待子進(jìn)程運行結束,而如果父進(jìn)程不等待而直接執行下去的話(huà),可以想見(jiàn),會(huì )出現極大的混亂。這種情況稱(chēng)為進(jìn)程之間的同步,更準確地說(shuō),這是進(jìn)程同步的一種特例。進(jìn)程同步就是要協(xié)調好2個(gè)以上的進(jìn)程,使之以安排好地次序依次執行。解決進(jìn)程同步問(wèn)題有更通用的方法,我們將在以后介紹,但對于我們假設的這種情況,則完全可以用wait系統調用簡(jiǎn)單的予以解決。請看下面這段程序:
#include <sys/types.h> #include <sys/wait.h> main() { pid_t pc, pr; int status; pc=fork(); if(pc<0) printf("Error occured on forking.\n"); else if(pc==0){ /* 子進(jìn)程的工作 */ exit(0); }else{ /* 父進(jìn)程的工作 */ pr=wait(&status); /* 利用子進(jìn)程的結果 */ } }
這段程序只是個(gè)例子,不能真正拿來(lái)執行,但它卻說(shuō)明了一些問(wèn)題,首先,當fork調用成功后,父子進(jìn)程各做各的事情,但當父進(jìn)程的工作告一段落,需要用到子進(jìn)程的結果時(shí),它就停下來(lái)調用wait,一直等到子進(jìn)程運行結束,然后利用子進(jìn)程的結果繼續執行,這樣就圓滿(mǎn)地解決了我們提出的進(jìn)程同步問(wèn)題。
回頁(yè)首
waitpid系統調用在Linux函數庫中的原型是:
#include <sys/types.h> /* 提供類(lèi)型pid_t的定義 */ #include <sys/wait.h> pid_t waitpid(pid_t pid,int *status,int options)
從本質(zhì)上講,系統調用waitpid和wait的作用是完全相同的,但waitpid多出了兩個(gè)可由用戶(hù)控制的參數pid和options,從而為我們編程提供了另一種更靈活的方式。下面我們就來(lái)詳細介紹一下這兩個(gè)參數:
從參數的名字pid和類(lèi)型pid_t中就可以看出,這里需要的是一個(gè)進(jìn)程ID。但當pid取不同的值時(shí),在這里有不同的意義。
pid>0時(shí),只等待進(jìn)程ID等于pid的子進(jìn)程,不管其它已經(jīng)有多少子進(jìn)程運行結束退出了,只要指定的子進(jìn)程還沒(méi)有結束,waitpid就會(huì )一直等下去。 pid=-1時(shí),等待任何一個(gè)子進(jìn)程退出,沒(méi)有任何限制,此時(shí)waitpid和wait的作用一模一樣。 pid=0時(shí),等待同一個(gè)進(jìn)程組中的任何子進(jìn)程,如果子進(jìn)程已經(jīng)加入了別的進(jìn)程組,waitpid不會(huì )對它做任何理睬。 pid<-1時(shí),等待一個(gè)指定進(jìn)程組中的任何子進(jìn)程,這個(gè)進(jìn)程組的ID等于pid的絕對值。
options提供了一些額外的選項來(lái)控制waitpid,目前在Linux中只支持WNOHANG和WUNTRACED兩個(gè)選項,這是兩個(gè)常數,可以用"|"運算符把它們連接起來(lái)使用,比如:
ret=waitpid(-1,NULL,WNOHANG | WUNTRACED);
如果我們不想使用它們,也可以把options設為0,如:
ret=waitpid(-1,NULL,0);
如果使用了WNOHANG參數調用waitpid,即使沒(méi)有子進(jìn)程退出,它也會(huì )立即返回,不會(huì )像wait那樣永遠等下去。
而WUNTRACED參數,由于涉及到一些跟蹤調試方面的知識,加之極少用到,這里就不多費筆墨了,有興趣的讀者可以自行查閱相關(guān)材料。
看到這里,聰明的讀者可能已經(jīng)看出端倪了--wait不就是經(jīng)過(guò)包裝的waitpid嗎?沒(méi)錯,察看<內核源碼目錄>/include/unistd.h文件349-352行就會(huì )發(fā)現以下程序段:
static inline pid_t wait(int * wait_stat) { return waitpid(-1,wait_stat,0); }
waitpid的返回值比wait稍微復雜一些,一共有3種情況:
當正常返回的時(shí)候,waitpid返回收集到的子進(jìn)程的進(jìn)程ID; 如果設置了選項WNOHANG,而調用中waitpid發(fā)現沒(méi)有已退出的子進(jìn)程可收集,則返回0; 如果調用中出錯,則返回-1,這時(shí)errno會(huì )被設置成相應的值以指示錯誤所在;
當pid所指示的子進(jìn)程不存在,或此進(jìn)程存在,但不是調用進(jìn)程的子進(jìn)程,waitpid就會(huì )出錯返回,這時(shí)errno被設置為ECHILD;
/* waitpid.c */ #include <sys/types.h> #include <sys/wait.h> #include <unistd.h> main() { pid_t pc, pr; pc=fork(); if(pc<0) /* 如果fork出錯 */ printf("Error occured on forking.\n"); else if(pc==0){ /* 如果是子進(jìn)程 */ sleep(10); /* 睡眠10秒 */ exit(0); } /* 如果是父進(jìn)程 */ do{ pr=waitpid(pc, NULL, WNOHANG); /* 使用了WNOHANG參數,waitpid不會(huì )在這里等待 */ if(pr==0){ /* 如果沒(méi)有收集到子進(jìn)程 */ printf("No child exited\n"); sleep(1); } }while(pr==0); /* 沒(méi)有收集到子進(jìn)程,就回去繼續嘗試 */ if(pr==pc) printf("successfully get child %d\n", pr); else printf("some error occured\n"); }
編譯并運行:
$ cc waitpid.c -o waitpid $ ./waitpid No child exited No child exited No child exited No child exited No child exited No child exited No child exited No child exited No child exited No child exited successfully get child 1526
父進(jìn)程經(jīng)過(guò)10次失敗的嘗試之后,終于收集到了退出的子進(jìn)程。
因為這只是一個(gè)例子程序,不便寫(xiě)得太復雜,所以我們就讓父進(jìn)程和子進(jìn)程分別睡眠了10秒鐘和1秒鐘,代表它們分別作了10秒鐘和1秒鐘的工作。父子進(jìn)程都有工作要做,父進(jìn)程利用工作的簡(jiǎn)短間歇察看子進(jìn)程的是否退出,如退出就收集它。
回頁(yè)首
也許有不少讀者從本系列文章一推出就開(kāi)始讀,一直到這里還有一個(gè)很大的疑惑:既然所有新進(jìn)程都是由fork產(chǎn)生的,而且由fork產(chǎn)生的子進(jìn)程和父進(jìn)程幾乎完全一樣,那豈不是意味著(zhù)系統中所有的進(jìn)程都應該一模一樣了嗎?而且,就我們的常識來(lái)說(shuō),當我們執行一個(gè)程序的時(shí)候,新產(chǎn)生的進(jìn)程的內容應就是程序的內容才對。是我們理解錯了嗎?顯然不是,要解決這些疑惑,就必須提到我們下面要介紹的exec系統調用。
說(shuō)是exec系統調用,實(shí)際上在Linux中,并不存在一個(gè)exec()的函數形式,exec指的是一組函數,一共有6個(gè),分別是:
#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[]);
其中只有execve是真正意義上的系統調用,其它都是在此基礎上經(jīng)過(guò)包裝的庫函數。
exec函數族的作用是根據指定的文件名找到可執行文件,并用它來(lái)取代調用進(jìn)程的內容,換句話(huà)說(shuō),就是在調用進(jìn)程內部執行一個(gè)可執行文件。這里的可執行文件既可以是二進(jìn)制文件,也可以是任何Linux下可執行的腳本文件。
與一般情況不同,exec函數族的函數執行成功后不會(huì )返回,因為調用進(jìn)程的實(shí)體,包括代碼段,數據段和堆棧等都已經(jīng)被新的內容取代,只留下進(jìn)程ID等一些表面上的信息仍保持原樣,頗有些神似"三十六計"中的"金蟬脫殼"??瓷先ミ€是舊的軀殼,卻已經(jīng)注入了新的靈魂。只有調用失敗了,它們才會(huì )返回一個(gè)-1,從原程序的調用點(diǎn)接著(zhù)往下執行。
現在我們應該明白了,Linux下是如何執行新程序的,每當有進(jìn)程認為自己不能為系統和擁護做出任何貢獻了,他就可以發(fā)揮最后一點(diǎn)余熱,調用任何一個(gè)exec,讓自己以新的面貌重生;或者,更普遍的情況是,如果一個(gè)進(jìn)程想執行另一個(gè)程序,它就可以fork出一個(gè)新進(jìn)程,然后調用任何一個(gè)exec,這樣看起來(lái)就好像通過(guò)執行應用程序而產(chǎn)生了一個(gè)新進(jìn)程一樣。
事實(shí)上第二種情況被應用得如此普遍,以至于Linux專(zhuān)門(mén)為其作了優(yōu)化,我們已經(jīng)知道,fork會(huì )將調用進(jìn)程的所有內容原封不動(dòng)的拷貝到新產(chǎn)生的子進(jìn)程中去,這些拷貝的動(dòng)作很消耗時(shí)間,而如果fork完之后我們馬上就調用exec,這些辛辛苦苦拷貝來(lái)的東西又會(huì )被立刻抹掉,這看起來(lái)非常不劃算,于是人們設計了一種"寫(xiě)時(shí)拷貝(copy-on-write)"技術(shù),使得fork結束后并不立刻復制父進(jìn)程的內容,而是到了真正實(shí)用的時(shí)候才復制,這樣如果下一條語(yǔ)句是exec,它就不會(huì )白白作無(wú)用功了,也就提高了效率。
上面6條函數看起來(lái)似乎很復雜,但實(shí)際上無(wú)論是作用還是用法都非常相似,只有很微小的差別。在學(xué)習它們之前,先來(lái)了解一下我們習以為常的main函數。
下面這個(gè)main函數的形式可能有些出乎我們的意料:
int main(int argc, char *argv[], char *envp[])
它可能與絕大多數教科書(shū)上描述的都不一樣,但實(shí)際上,這才是main函數真正完整的形式。
參數argc指出了運行該程序時(shí)命令行參數的個(gè)數,數組argv存放了所有的命令行參數,數組envp存放了所有的環(huán)境變量。環(huán)境變量指的是一組值,從用戶(hù)登錄后就一直存在,很多應用程序需要依靠它來(lái)確定系統的一些細節,我們最常見(jiàn)的環(huán)境變量是PATH,它指出了應到哪里去搜索應用程序,如/bin;HOME也是比較常見(jiàn)的環(huán)境變量,它指出了我們在系統中的個(gè)人目錄。環(huán)境變量一般以字符串"XXX=xxx"的形式存在,XXX表示變量名,xxx表示變量的值。
值得一提的是,argv數組和envp數組存放的都是指向字符串的指針,這兩個(gè)數組都以一個(gè)NULL元素表示數組的結尾。
我們可以通過(guò)以下這個(gè)程序來(lái)觀(guān)看傳到argc、argv和envp里的都是什么東西:
/* main.c */ int main(int argc, char *argv[], char *envp[]) { printf("\n### ARGC ###\n%d\n", argc); printf("\n### ARGV ###\n"); while(*argv) printf("%s\n", *(argv++)); printf("\n### ENVP ###\n"); while(*envp) printf("%s\n", *(envp++)); return 0; }
編譯它:
$ cc main.c -o main
運行時(shí),我們故意加幾個(gè)沒(méi)有任何作用的命令行參數:
$ ./main -xx 000 ### ARGC ### 3 ### ARGV ### ./main -xx 000 ### ENVP ### PWD=/home/lei REMOTEHOST=dt.laser.com HOSTNAME=localhost.localdomain QTDIR=/usr/lib/qt-2.3.1 LESSOPEN=|/usr/bin/lesspipe.sh %s KDEDIR=/usr USER=lei LS_COLORS= MACHTYPE=i386-redhat-linux-gnu MAIL=/var/spool/mail/lei INPUTRC=/etc/inputrc LANG=en_US LOGNAME=lei SHLVL=1 SHELL=/bin/bash HOSTTYPE=i386 OSTYPE=linux-gnu HISTSIZE=1000 TERM=ansi HOME=/home/lei PATH=/usr/local/bin:/bin:/usr/bin:/usr/X11R6/bin:/home/lei/bin _=./main
我們看到,程序將"./main"作為第1個(gè)命令行參數,所以我們一共有3個(gè)命令行參數。這可能與大家平時(shí)習慣的說(shuō)法有些不同,小心不要搞錯了。
現在回過(guò)頭來(lái)看一下exec函數族,先把注意力集中在execve上:
int execve(const char *path, char *const argv[], char *const envp[]);
對比一下main函數的完整形式,看出問(wèn)題了嗎?是的,這兩個(gè)函數里的argv和envp是完全一一對應的關(guān)系。execve第1個(gè)參數path是被執行應用程序的完整路徑,第2個(gè)參數argv就是傳給被執行應用程序的命令行參數,第3個(gè)參數envp是傳給被執行應用程序的環(huán)境變量。
留心看一下這6個(gè)函數還可以發(fā)現,前3個(gè)函數都是以execl開(kāi)頭的,后3個(gè)都是以execv開(kāi)頭的,它們的區別在于,execv開(kāi)頭的函數是以"char *argv[]"這樣的形式傳遞命令行參數,而execl開(kāi)頭的函數采用了我們更容易習慣的方式,把參數一個(gè)一個(gè)列出來(lái),然后以一個(gè)NULL表示結束。這里的NULL的作用和argv數組里的NULL作用是一樣的。
在全部6個(gè)函數中,只有execle和execve使用了char *envp[]傳遞環(huán)境變量,其它的4個(gè)函數都沒(méi)有這個(gè)參數,這并不意味著(zhù)它們不傳遞環(huán)境變量,這4個(gè)函數將把默認的環(huán)境變量不做任何修改地傳給被執行的應用程序。而execle和execve會(huì )用指定的環(huán)境變量去替代默認的那些。
還有2個(gè)以p結尾的函數execlp和execvp,咋看起來(lái),它們和execl與execv的差別很小,事實(shí)也確是如此,除execlp和execvp之外的4個(gè)函數都要求,它們的第1個(gè)參數path必須是一個(gè)完整的路徑,如"/bin/ls";而execlp和execvp的第1個(gè)參數file可以簡(jiǎn)單到僅僅是一個(gè)文件名,如"ls",這兩個(gè)函數可以自動(dòng)到環(huán)境變量PATH制定的目錄里去尋找。
知識介紹得差不多了,接下來(lái)我們看看實(shí)際的應用:
/* exec.c */ #include <unistd.h> main() { 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"); }
程序里調用了2個(gè)Linux常用的系統命令,echo和env。echo會(huì )把后面跟的命令行參數原封不動(dòng)的打印出來(lái),env用來(lái)列出所有環(huán)境變量。
由于各個(gè)子進(jìn)程執行的順序無(wú)法控制,所以有可能出現一個(gè)比較混亂的輸出--各子進(jìn)程打印的結果交雜在一起,而不是嚴格按照程序中列出的次序。
編譯并運行:
$ cc exec.c -o exec $ ./exec executed by execl PATH=/tmp USER=lei STATUS=testing executed by execlp excuted by execv executed by execvp PATH=/tmp USER=lei STATUS=testing
果然不出所料,execle輸出的結果跑到了execlp前面。
大家在平時(shí)的編程中,如果用到了exec函數族,一定記得要加錯誤判斷語(yǔ)句。因為與其他系統調用比起來(lái),exec很容易受傷,被執行文件的位置,權限等很多因素都能導致該調用的失敗。最常見(jiàn)的錯誤是:
找不到文件或路徑,此時(shí)errno被設置為ENOENT; 數組argv和envp忘記用NULL結束,此時(shí)errno被設置為EFAULT; 沒(méi)有對要執行文件的運行權限,此時(shí)errno被設置為EACCES。
回頁(yè)首
下面就讓我用一些形象的比喻,來(lái)對進(jìn)程短暫的一生作一個(gè)小小的總結:
隨著(zhù)一句fork,一個(gè)新進(jìn)程呱呱落地,但它這時(shí)只是老進(jìn)程的一個(gè)克隆。
然后隨著(zhù)exec,新進(jìn)程脫胎換骨,離家獨立,開(kāi)始了為人民服務(wù)的職業(yè)生涯。
人有生老病死,進(jìn)程也一樣,它可以是自然死亡,即運行到main函數的最后一個(gè)"}",從容地離我們而去;也可以是自殺,自殺有2種方式,一種是調用exit函數,一種是在main函數內使用return,無(wú)論哪一種方式,它都可以留下遺書(shū),放在返回值里保留下來(lái);它還甚至能可被謀殺,被其它進(jìn)程通過(guò)另外一些方式結束他的生命。
進(jìn)程死掉以后,會(huì )留下一具僵尸,wait和waitpid充當了殮尸工,把僵尸推去火化,使其最終歸于無(wú)形。
這就是進(jìn)程完整的一生。
回頁(yè)首
本文重點(diǎn)介紹了系統調用wait、waitpid和exec函數族,對與進(jìn)程管理相關(guān)的系統調用的介紹就在這里告一段落,在下一篇文章,也是與進(jìn)程管理相關(guān)的系統調用的最后一篇文章中,我們會(huì )通過(guò)兩個(gè)很酷的實(shí)際例子,來(lái)重溫一下最近學(xué)過(guò)的知識。
Linux man pages
Advanced Programming in the UNIX Environment by W. Richard Stevens, 1993
Linux核心源代碼分析 彭曉明,王強,2000
本站僅提供存儲服務(wù),所有內容均由用戶(hù)發(fā)布,如發(fā)現有害或侵權內容,請點(diǎn)擊舉報。
打開(kāi)APP,閱讀全文并永久保存 查看更多類(lèi)似文章
猜你喜歡
類(lèi)似文章
waitpid函數
linux下通過(guò)腳本實(shí)現自動(dòng)重啟程序的方法
如何等待所有的子進(jìn)程結束?
UNIX環(huán)境編程學(xué)習筆記(18)
僵尸進(jìn)程
fork兩次如何避免僵尸進(jìn)程收藏
更多類(lèi)似文章 >>
生活服務(wù)
分享 收藏 導長(cháng)圖 關(guān)注 下載文章
綁定賬號成功
后續可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

欧美性猛交XXXX免费看蜜桃,成人网18免费韩国,亚洲国产成人精品区综合,欧美日韩一区二区三区高清不卡,亚洲综合一区二区精品久久