linux 标准流管道 popen 源码理解

来源:互联网 发布:石家庄企业办公软件 编辑:程序博客网 时间:2024/05/10 13:57

标准流管道popen、pclose函数说明:

#include <stdio.h>FILE *popen(const char *command, const char *type)

返回值:若成功,返回文件流指针;若出错,返回-1
参数说明:

  • Command:指向的是一个以 null 结束符结尾的字符串,这个字符串包含一个 shell 命令,并被送到/bin/sh 以-c 参数执行,即由 shell 来执行
  • type:
    • ”r”: 文件指针连接到 command 的标准输出
    • “w” :文件指针连接到 command 的标准输入
#include <stdio.h>int pclose(FILE *stream)

返回值:若成功,返回 popen 中执行命令的终止状态;若出错,返回-1
参数说明:

  • stream:要关闭的文件流

popen函数其实是对管道操作的一些包装,所完成的工作有以下几步:

  • 创建一个管道。
  • fork 一个子进程。
  • 在父子进程中关闭不需要的文件描述符。
  • 执行 exec 函数族调用。
  • 执行函数中所指定的命令。

可以对照着Richard Stevens 实现的源码加深理解。
linux popen源码:

/*  *  popen.c     Written by W. Richard Stevens  */  #include    <sys/wait.h>  #include    <errno.h>  #include    <fcntl.h>  #include    "ourhdr.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 */          maxfd = open_max();          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 */  }  

接下来看一个简单的示例程序:

/* popen.c*/#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <fcntl.h>#define BUFSIZE 1000int main(){    FILE *fp;    char *cmd = "ls -al";    char buf[BUFSIZE];    if((fp=popen(cmd,"r"))==NULL)   //调用 popen 函数执行命令ls -al        perror("popen");    fread( buf, sizeof(char), sizeof(buf),fp);  //将刚刚fp指向的文件流读取到buf中    printf("%s",buf);    pclose(fp);    exit(0);}

实验结果:

ubuntu:~/test/process_test$ gcc popen.c -o popenubuntu:~/test/process_test$ ./popentotal 92drwxr-xr-x  2  4096 Aug 15 16:56 .drwxr-xr-x 19  4096 Aug 15 10:12 ..-rwxr-xr-x  1    12 Aug 15 11:16 file_fork-rwxr-xr-x  1  8784 Aug 15 10:46 fork-rwxr-xr-x  1  8785 Aug 15 10:45 fork2-rwxr--r--  1   571 Aug 15 11:25 fork2.c-rwxr--r--  1   469 Aug 15 10:42 fork.c-rwxr-xr-x  1  8890 Aug 15 11:16 open_fork-rwxr--r--  1  1114 Aug 15 11:25 open_fork.c-rwxr-xr-x  1  8884 Aug 15 16:16 pipe-rwxr--r--  1  1677 Aug 15 16:16 pipe.c-rwxr-xr-x  1  8683 Aug 15 16:56 popen-rwxr--r--  1   409 Aug 15 16:56 popen.c

在终端执行shell命令”ls -al”

ubuntu:~/test/process_test$ ls -altotal 92       drwxr-xr-x  2  4096 Aug 15 16:56 .drwxr-xr-x 19  4096 Aug 15 10:12 ..-rwxr-xr-x  1    12 Aug 15 11:16 file_fork-rwxr-xr-x  1  8784 Aug 15 10:46 fork-rwxr-xr-x  1  8785 Aug 15 10:45 fork2-rwxr--r--  1   571 Aug 15 11:25 fork2.c-rwxr--r--  1   469 Aug 15 10:42 fork.c-rwxr-xr-x  1  8890 Aug 15 11:16 open_fork-rwxr--r--  1  1114 Aug 15 11:25 open_fork.c-rwxr-xr-x  1  8884 Aug 15 16:16 pipe-rwxr--r--  1  1677 Aug 15 16:16 pipe.c-rwxr-xr-x  1  8683 Aug 15 16:56 popen-rwxr--r--  1   409 Aug 15 16:56 popen.c

实验结果跟在终端执行shell命令是一样的。理解源码之后,对popen理解也更加深刻,在不同场景中运用也更加自如。

原创粉丝点击