一 分析
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()来返回一个退出的状态。