popen函数的实现

popen函数的实现包括一下几步:

 

1、使用pipe()建立管道

2、使用fork()创建子进程

3、在子进程中调用exec族函数执行命令,通过管道将结果传送至父进程

4、在主进程中等待子进程执行,子进程执行完成后将接收其结果,返回结果的文件指针

 

类似与system(fork与exec函数的组合),popen在启动另外一个线程时,该线程有可能启动失败或者popen执行shell时失败了,但这个时候popen本身不会报错,直接就造成调用popen的父进程卡住了。可以通过验证errno来避免。

 

下面是popen()在linux中的实现:

  1. /* 
  2.  *  popen.c     Written by W. Richard Stevens 
  3.  */  
  4.   
  5. #include    <sys/wait.h>   
  6. #include    <errno.h>   
  7. #include    <fcntl.h>   
  8. #include    "ourhdr.h"   
  9.   
  10. static pid_t    *childpid = NULL;  
  11.                         /* ptr to array allocated at run-time */  
  12. static int      maxfd;  /* from our open_max(), {Prog openmax} */  
  13.   
  14. #define SHELL   "/bin/sh"   
  15.   
  16. FILE *  
  17. popen(const char *cmdstring, const char *type)  
  18. {  
  19.     int     i, pfd[2];  
  20.     pid_t   pid;  
  21.     FILE    *fp;  
  22.   
  23.             /* only allow "r" or "w" */  
  24.     if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {  
  25.         errno = EINVAL;     /* required by POSIX.2 */  
  26.         return(NULL);  
  27.     }  
  28.   
  29.     if (childpid == NULL) {     /* first time through */  
  30.                 /* allocate zeroed out array for child pids */  
  31.         maxfd = open_max();  
  32.         if ( (childpid = calloc(maxfd, sizeof(pid_t))) == NULL)  
  33.             return(NULL);  
  34.     }  
  35.   
  36.     if (pipe(pfd) < 0)  
  37.         return(NULL);   /* errno set by pipe() */  
  38.   
  39.     if ( (pid = fork()) < 0)  
  40.         return(NULL);   /* errno set by fork() */  
  41.     else if (pid == 0) {                            /* child */  
  42.         if (*type == 'r') {  
  43.             close(pfd[0]);  
  44.             if (pfd[1] != STDOUT_FILENO) {  
  45.                 dup2(pfd[1], STDOUT_FILENO);  
  46.                 close(pfd[1]);  
  47.             }  
  48.         } else {  
  49.             close(pfd[1]);  
  50.             if (pfd[0] != STDIN_FILENO) {  
  51.                 dup2(pfd[0], STDIN_FILENO);  
  52.                 close(pfd[0]);  
  53.             }  
  54.         }  
  55.             /* close all descriptors in childpid[] */  
  56.         for (i = 0; i < maxfd; i++)  
  57.             if (childpid[ i ] > 0)  
  58.                 close(i);  
  59.   
  60.         execl(SHELL, "sh""-c", cmdstring, (char *) 0);  
  61.         _exit(127);  
  62.     }  
  63.                                 /* parent */  
  64.     if (*type == 'r') {  
  65.         close(pfd[1]);  
  66.         if ( (fp = fdopen(pfd[0], type)) == NULL)  
  67.             return(NULL);  
  68.     } else {  
  69.         close(pfd[0]);  
  70.         if ( (fp = fdopen(pfd[1], type)) == NULL)  
  71.             return(NULL);  
  72.     }  
  73.     childpid[fileno(fp)] = pid; /* remember child pid for this fd */  
  74.     return(fp);  
  75. }  
  76.   
  77. int  
  78. pclose(FILE *fp)  
  79. {  
  80.   
  81.     int     fd, stat;  
  82.     pid_t   pid;  
  83.   
  84.     if (childpid == NULL)  
  85.         return(-1);     /* popen() has never been called */  
  86.   
  87.     fd = fileno(fp);  
  88.     if ( (pid = childpid[fd]) == 0)  
  89.         return(-1);     /* fp wasn't opened by popen() */  
  90.   
  91.     childpid[fd] = 0;  
  92.     if (fclose(fp) == EOF)  
  93.         return(-1);  
  94.   
  95.     while (waitpid(pid, &stat, 0) < 0)  
  96.         if (errno != EINTR)  
  97.             return(-1); /* error other than EINTR from waitpid() */  
  98.   
  99.     return(stat);   /* return child's termination status */  
  100. }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值