UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell->在shell中启动另一个程序

时间:2021-08-17 11:14:34

一 分析

  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

UNIX-LINUX编程实践教程->第八章->实例代码注解->写一个简单的shell->在shell中启动另一个程序

   代码示意:

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