一 分析
shell啟動一個程序,包括以下幾步:
1)從用戶讀入指令字符串
2)shell建立一個新進程
3)在新進程中運行指令並等待進程結束
用戶如何讀入指令我們就不在此探討了,這里主要探討如何在一個程序里啟動另一個程序。
二 一個程序如何運行另一個程序
1 使用execvp函數來啟動另一個程序
execvp()函數
找到指定路徑的文件並執行該文件
頭文件:#include<unistd.h>
函數原型:int execvp(const char *file ,char * const argv []);
參數: file 可執行文件的路徑+文件名
argv 參數組
返回值: 若函數執行成功則不會返回,若執行失敗就直接返回-1
代碼示意:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* arglist[3]; arglist[0] = "ls"; arglist[1] = "-l"; arglist[2] = 0; printf("ready for cmd ls\n"); execvp("ls",arglist); printf("cmd done\n"); }
輸出:
ready for cmd ls total 44 -rwxrwxr-x 1 lqx lqx 7196 2013-06-17 09:17 a.out -rw-rw-r-- 1 lqx lqx 497 2013-04-25 15:12 execute.c -rw-rw-r-- 1 lqx lqx 482 2013-04-23 15:54 psh2.c -rw-rw-r-- 1 lqx lqx 584 2013-04-25 15:10 smsh1.c -rw-rw-r-- 1 lqx lqx 202 2013-04-25 15:14 smsh.h -rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c -rw-rw-r-- 1 lqx lqx 241 2013-06-17 09:16 test.c -rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline -rw-rw-r-- 1 lqx lqx 436 2013-05-10 14:46 testline.c
在上面的代碼中,執行了“ls -l”,需要注意的是,arglist[2]=0表示指令結束。最后有個奇怪的地方,“cmd done”似乎沒有被打印出來?這是因為調用execvp后,新的進程覆蓋了原有的進程,新進程結束后並不會返回老進程而是直接結束。解決辦法是在老進程里新建一個進程,在新的進程里調用execvp,這樣就能防止老進程后續的指令無法執行。
2 使用fork函數來復制進程
fork()函數
當一個進程調用fork后,就有兩個代碼相同的進程,而且它們都運行到相同的位置。
頭文件:#include<unistd.h> #include<sys/types.h>
函數原型:pid_t fork(void);
參數:無
返回值:若成功調用,子進程返回0,父進程返回子進程ID;否則出錯返回-1
代碼示意:
int main() { printf("ready to fork\n"); fork(); sleep(1); printf("finished!\n"); }
輸出:
lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out ready to fork finished! lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ finished!
從上面可以看出,進程在執行fork后輸出了兩次“finished”,原因就是fork創建的進程與原進程是一樣的且同樣執行到fork函數返回的地方,於是“finished”在兩個進程中分別被輸出。
3 使用新進程來啟動另一個程序
在上面,我們使用fork新創建了一個跟原進程一樣的新進程,我們可以在新的進程里來啟動需要的程序,但我們也知道,新進程與舊的進程是一樣的,那么進程如何知道自己是新的還是老的呢?我們可以利用fork的返回值來判斷,子進程的fork返回0,父進程的fork返回子進程ID。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h> int main() { char* arglist[3]; int i; arglist[0] = "ls"; arglist[1] = "-l"; arglist[2] = 0; i = fork(); if(i==0) { printf("I'm the new process.ready for cmd ls\n"); execvp("ls",arglist); } else { printf("I'm the old process\n"); } }
輸出:
lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out I'm the old process lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ I'm the new process.ready for cmd ls total 44 -rwxrwxr-x 1 lqx lqx 7232 2013-06-19 09:51 a.out -rw-rw-r-- 1 lqx lqx 497 2013-04-25 15:12 execute.c -rw-rw-r-- 1 lqx lqx 482 2013-04-23 15:54 psh2.c -rw-rw-r-- 1 lqx lqx 584 2013-04-25 15:10 smsh1.c -rw-rw-r-- 1 lqx lqx 202 2013-04-25 15:14 smsh.h -rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c -rw-rw-r-- 1 lqx lqx 379 2013-06-19 09:51 test.c -rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline -rw-rw-r-- 1 lqx lqx 436 2013-05-10 14:46 testline.c
輸出里可以看到,新老進程都執行完畢。到這里還並不完美,因為在很多應用場景中,舊的進程需要等待新進程執行完畢了再進行下一步的操作,
4 父進程使用wait函數來等待子進程返回
wait()函數
進程調用wait后立即阻塞自己,直到其子進程返回
頭文件:無
函數原型:pid_t wait (int* status)
參數:status 保存子進程退出時的狀態信息
返回值:成功則返回子進程ID,若該進程沒有子進程則返回-1
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <sys/types.h>
int main() { char* arglist[3]; int i; arglist[0] = "ls"; arglist[1] = "-l"; arglist[2] = 0; i = fork(); if(i==0) { printf("I'm the new process.ready for cmd ls\n"); execvp("ls",arglist); } else { wait(NULL); printf("I'm the old process\n"); } }
輸出:
lqx@lqx-virtual-machine:~/bin/UnixPrograme/8$ ./a.out I'm the new process.ready for cmd ls total 44 -rwxrwxr-x 1 lqx lqx 7268 2013-06-19 10:26 a.out -rw-rw-r-- 1 lqx lqx 497 2013-04-25 15:12 execute.c -rw-rw-r-- 1 lqx lqx 482 2013-04-23 15:54 psh2.c -rw-rw-r-- 1 lqx lqx 584 2013-04-25 15:10 smsh1.c -rw-rw-r-- 1 lqx lqx 202 2013-04-25 15:14 smsh.h -rw-rw-r-- 1 lqx lqx 1715 2013-04-25 15:13 splitline.c -rw-rw-r-- 1 lqx lqx 389 2013-06-19 10:26 test.c -rwxrwxr-x 1 lqx lqx 7310 2013-05-09 14:46 testline -rw-rw-r-- 1 lqx lqx 436 2013-05-10 14:46 testline.c I'm the old process
這里,子進程沒有用到exit()來返回一個退出的狀態。