popen函数的实现

【注】本文转自:http://www.cnblogs.com/li-hao/archive/2011/10/20/2219418.html,本人有修改对open_max()的调用

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

 

1、使用pipe()建立管道

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

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

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

 

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

 

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

/* 
 *  popen.c     Written by W. Richard Stevens 
 */  
  
#include    <sys/wait.h>   
#include    <errno.h>   
#include    <fcntl.h>   
//#include    "ourhdr.h"   
#include    <sys/resource.h>
  
static pid_t    *childpid = NULL;  
                        /* ptr to array allocated at run-time */  
static int      maxfd;  /* from our open_max(), {Prog openmax} */  
  
#define SHELL   "/bin/sh"   
  
FILE *  
popen(const char *cmdstring, const char *type)  
{  
    int     i, pfd[2];  
    pid_t   pid;  
    FILE    *fp;  
  
            /* only allow "r" or "w" */  
    if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) {  
        errno = EINVAL;     /* required by POSIX.2 */  
        return(NULL);  
    }  
  
    if (childpid == NULL) {     /* first time through */  
                /* allocate zeroed out array for child pids */  
#if 0
        maxfd = open_max();  
#else
        /* OPEN_MAX is canceled after linux-2.6.24, and become a part of RLIMIT_NOFILE. 
        Execute shell 'ulimit -a' to get more information */
        struct rlimit limit;
        if(getrlimit(RLIMIT_NOFILE, &limit) == -1)
        {
            perror("getrlimit fail");
            return(NULL);
        }
        maxfd = limit.rlim_cur;
        //print("rlimit = %d.\n", maxfd);
#endif
        if ( (childpid = calloc(maxfd, sizeof(pid_t))) == NULL)  
            return(NULL);  
    }  
  
    if (pipe(pfd) < 0)  
        return(NULL);   /* errno set by pipe() */  
  
    if ( (pid = fork()) < 0)  
        return(NULL);   /* errno set by fork() */  
    else if (pid == 0) {                            /* child */  
        if (*type == 'r') {  
            close(pfd[0]);  
            if (pfd[1] != STDOUT_FILENO) {  
                dup2(pfd[1], STDOUT_FILENO);  
                close(pfd[1]);  
            }  
        } else {  
            close(pfd[1]);  
            if (pfd[0] != STDIN_FILENO) {  
                dup2(pfd[0], STDIN_FILENO);  
                close(pfd[0]);  
            }  
        }  
            /* close all descriptors in childpid[] */  
        for (i = 0; i < maxfd; i++)  
            if (childpid[ i ] > 0)  
                close(i);  
  
        execl(SHELL, "sh", "-c", cmdstring, (char *) 0);  
        _exit(127);  
    }  
                                /* parent */  
    if (*type == 'r') {  
        close(pfd[1]);  
        if ( (fp = fdopen(pfd[0], type)) == NULL)  
            return(NULL);  
    } else {  
        close(pfd[0]);  
        if ( (fp = fdopen(pfd[1], type)) == NULL)  
            return(NULL);  
    }  
    childpid[fileno(fp)] = pid; /* remember child pid for this fd */  
    return(fp);  
}  
  
int  
pclose(FILE *fp)  
{  
  
    int     fd, stat;  
    pid_t   pid;  
  
    if (childpid == NULL)  
        return(-1);     /* popen() has never been called */  
  
    fd = fileno(fp);  
    if ( (pid = childpid[fd]) == 0)  
        return(-1);     /* fp wasn't opened by popen() */  
  
    childpid[fd] = 0;  
    if (fclose(fp) == EOF)  
        return(-1);  
  
    while (waitpid(pid, &stat, 0) < 0)  
        if (errno != EINTR)  
            return(-1); /* error other than EINTR from waitpid() */  
  
    return(stat);   /* return child's termination status */  
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值