分享

自己实现popen函数

 xsx2008 2014-01-23

自己实现popen函数

2545人阅读 评论(3) 收藏 举报

   闲来无事,自己实现了popen函数mypopen,后来查看了popen函数的源码发现自己实现的与其相差无几,本函数与linux中的实 现最大的不同是不需要用专门的pclose()函数来关闭文件指针,用普通的fclose()即可,linux实现的代码也会给出在下文,可以对比一下其 中差异。

 

   主要通过pipe管道实现,具体思路如下:

        1、使用pipe()建立管道

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

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

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

 

  下面是mypopen源代码,重点部位已通过注释进行标注:

    

  1. FILE *mypopen(char *cmd,char type)  
  2. {  
  3.     int pipefd[2];           //管道描述符  
  4.     int pid_t;               //进程描述符  
  5.   
  6.     if(type !='r' && type != 'w')  
  7.     {  
  8.         printf("myopen() flag error/n");  
  9.         return NULL;  
  10.     }  
  11.   
  12.     if(pipe(pipefd)<0)        //建立管道  
  13.     {  
  14.         printf("myopen() pipe create error/n");  
  15.         return NULL;  
  16.     }  
  17.       
  18.     pid_t=fork();             //建立子进程  
  19.   
  20.     if(pid_t < 0)   
  21.         return NULL;  
  22.   
  23.     if(0 == pid_t)            //子进程中......  
  24.     {  
  25.         if(type == 'r')  
  26.         {  
  27.             close(pipefd[0]);               //此函数返回文件指针是用于读,因此,在子进程中应该将结果写入管道,这样父进程返回的文件指针才能读,所以这里将读端关闭  
  28.             dup2(pipefd[1],STDOUT_FILENO);  //exec函数的执行结果将会通过标准输出写到控制台上,但这里我们不需要在控制台输出,而是需要将结果返回,因此将标准输出重定向到管道写端  
  29.             close(pipefd[1]);         
  30.   
  31.         }  
  32.         else{  
  33.             close(pipefd[1]);  
  34.             dup2(pipefd[0],STDIN_FILENO);  
  35.             close(pipefd[0]);  
  36.         }  
  37.         char *argv[] = {cmd,NULL};    
  38.         if(execvp(cmd,argv)<0)          //用exec族函数执行命令  
  39.             return NULL;      
  40.     }  
  41.       
  42.     wait(0);                                //等待子进程返回  
  43.   
  44.     if(type=='r'){  
  45.         close(pipefd[1]);  
  46.         return fdopen(pipefd[0],"r");   //由于程序需要返回的参数是文件指针,因此需要用fdopen函数将描述符打开,其返回值为相应的文件指针   
  47.     }else{  
  48.         close(pipefd[0]);  
  49.         return fdopen(pipefd[1],"w");  
  50.     }  
  51. }  
 

 

 

   下面是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. }  

 

    参考资料,真诚致谢:

   http://www./forum/showflat.php?Cat=&Board=program&Number=266385 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多