Linux进程线程学习笔记:运行新程序
?? ? ? ? ? ? ? ? ? ? ? ? ? Linux進程線程學習筆記:運行新程序
?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?周銀輝
?
在上一篇中我們說到,當啟動一個新進程以后,新進程會復制父進程的大部份上下文并接著運行父進程中的代碼,如果我們使新進程不運行原父進程的代碼,轉而運行另外一個程序集中的代碼,這就相當于啟動了一個新程序。這里的代碼我們可以理解成一個可執行程序。
所以,要運行一個新程序,需要最基本的兩步:
1,創建一個可運行程序的環境,也就是進程。
2,將環境中的內容替換成你所希望的,也就是用你希望運行的可執行文件去覆蓋新進程中的原有映像,并從該可執行文件的起始處開始執行。?
要做到第一點,非常簡單,fork函數就可以(參考上一篇) ,要做到第二點,則可以利用exec函數族。
?
exec是一族函數的簡稱,包含在<unistd.h>中它們作用都一樣,用一個可執行文件覆蓋進程的現有映像,并轉到該可執行文件的起始處開始執行。
原型如下:
int execl(const char *path, const char *arg0, ... /*, (char *)0 */);?
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);?
int execle(const char *path, const char *arg0, ... /*,?(char *)0, 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[]);我們先以最簡單的execl函數為例,其他的大同小異,其第一個參數path是可執行文件的路徑,是絕對路徑;從arg0參數開始及后面所有的是你要傳遞給可執行文件的命令行參數,值得注意的是,arg0是可執行文件本身(還記得C語言中老師講main函數參數列表時所說的么),當然,不傳程序本身或傳一些亂七八糟的值并不代表不能通過編譯或不能運行,只不過,如果可執行文件要用到arg0時會產生一些迷惑;最后有一個注釋/*, (char*)0 */是提醒我們最后一個參數應該傳空字符串。如何函數運行成功,則不會有任何返回值,否則返回-1,而具體的錯誤號會被設置在errno,errno是一個全局變量,用于程序設置錯誤號,跟win32的getLastError函數類似。?
看下面的例子:
#include?<stdio.h>#include?<unistd.h>
intmain?()
{
????printf("app?start...\n");
????execl("/bin/ls",?"/bin/ls",?"-l",NULL);
????printf("app?end\n");
????
????return0;
}
我們運行了bin目錄下的ls程序,參數arg0時ls程序本身路徑,arg1為-l,使得其以列表的形式列舉當前目錄,在我的計算機上程序輸出如下:
total?12
-rw-r--r--?1zhouyh?zhouyh??2732010-09-0611:09temp.c
-rwxr-xr-x?1zhouyh?zhouyh?71752010-09-0611:09temp.exe ls程序運行成功了。但注意到了嗎?沒有輸出“app end”這個字符串,原因很簡單,我們沒有新起進程,而是直接用ls程序覆蓋了main函數所在的進程。
那我們接下來,試著用fork吧,以免影響原進程。
?
#include?<stdio.h>#include?<unistd.h>
intmain?()
{
????printf("app?start...\n");
????
????if(fork()?==0)
????{
????????execl("/bin/ls",?"/bin/ls",?"-l",?NULL);
????}
????????
????printf("app?end\n");
????
????return0;
}
我們用fork創建了一個新進程,當其成功創建后(返回值為0),我們用execl來加載ls程序并運行之。
程序的輸出如下:
app?start...app?end
zhouyh@ubuntu:~/Documents$?total?12
-rw-r--r--?1zhouyh?zhouyh??2292010-09-0615:59temp.c
-rwxr-xr-x?1zhouyh?zhouyh?72112010-09-0616:00temp.exe
?
程序的所有輸出都OK了,但有一點可能和我們想象的不一樣,那就是“app end”這個字符串很早就輸出了而不是在最后,其實這并沒有錯,“app end” 是main函數所在的程序(temp.exe)即將結束時輸出的,而列舉文件目錄的ls卻完全在另外一個進程中,兩個異步執行的進程,他們沒有誰先誰后結束可言。
?
如果我們希望所有工作完成之后,即ls也執行玩以后,才輸出“app end”,那么可以使用wait 以及waitpid函數,這里簡單說一下wait,具體的會在“Linux進程線程學習筆記:進程控制”中講。
pid_t wait (int * status); //包含在 <sys/wait.h> 中
wait函數講當前進程休眠,直到該進程的某個子進程結束或者有特定的信號來喚醒。如果子進程正常結束,則講子進程的進程id(pid)作為返回值,發生錯誤則返回-1,而status參數講傳出子進程的結束狀態值。
針對剛才的例子,可以參考下面的代碼:
#include?<unistd.h>//for?fork()
#include?<sys/wait.h>//for?wait(int*?status)
intmain?()
{
????printf("app?start...\n");
????
????if(fork()?==0)
????{
????????execl("/bin/ls",?"/bin/ls",?"-l",?NULL);
????}
????
????intstatus;
????wait(&status);
????????
????printf("app?end\n");
????
????return0;
}
?
程序輸出如下:
app?start...
total?12
-rw-r--r--?1zhouyh?zhouyh??3372010-09-0616:22temp.c
-rwxr-xr-x?1zhouyh?zhouyh?72472010-09-0616:22temp.exe
app?end
?
好了,現在回過頭來看除execl外的其他幾個函數?:
?
int execlp(const char *file, const char *arg0, ... /*, (char *)0 */);
execlp和execl差不多,但區別在于前者會去系統環境變量查找file所指的程序的位置,所以如果通過環境變量能找到可執行文件,則file可以不是絕對路徑了,比如 execlp("ls", "ls", "-l", NULL);
?
int execle(const char *path, const char *arg0, ... /*,?(char *)0, char *const?envp[]*/);?
與execlp不同的是,其最后一個參數作為你自定義的環境變量參數傳進去,而不是查找系統環境變量
char *env[] = { "HOME=/usr/home", "LOGNAME=home",(char *)0 };
execle("/bin/ls", "ls", "-l", NULL, env);
?
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[]);
這三個函數和前面的三個類似,函數名由后綴l變成了v,其表達的含義是參數不再用參數列表傳遞而是用一個參數數組argv[],當然,數組最后一個元素也必須是char* 0?
?
名字這么相近的函數,感覺好容易混淆,那么就從l,v,p,e 這樣的后綴來區分吧:
l:參數為一個逗號分隔的參數列表,并以char* 0作為列表結尾
v: 參數為字符串數組,數組的最后一個元素為char* 0
p: 可以通過系統環境變量查找文件位置
e:調用者顯示傳入環境變量?
?
轉載于:https://www.cnblogs.com/zhouyinhui/archive/2010/09/07/1819338.html
總結
以上是生活随笔為你收集整理的Linux进程线程学习笔记:运行新程序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: cf378D(stl模拟)
- 下一篇: linux下挂接fat32分区