popen 和 pclose 函数

在管道操作中,常见的操作是创建一个连接到另一个进程的管道,然后读其输出或向其发送输入,所以标准I/O库为实现这些操作提供了两个函数POPEN和PCLOSE,这两个函数实现的操作是::

1创建一个管道

2FORK 一个子进程

3关闭管道的不是用端

4EXEC一个SHELL以执行命令

5等待命令终止

#include <sys/wait.h>
  2 #include "../../ourhdr.h"
  3
  4 #define PAGER "${PAGER:-more}"
  5 /*environment variable , or default*/
  6
  7 int main(int argc, char *argv[])
  8 {
  9         char  line[MAXLINE];
 10         FILE  *fpin, *fpout;
 11
 12         if (argc != 2)
 13                 err_sys("usage : a,out< pathname>    ");
 14
 15         if ( (fpin = fopen (argv[1], "r")) == NUL    L)
 16                 err_sys("can't open %s", argv[1])    ;
 17         if ( (fpout = popen (PAGER, "w")) == NULL    )
 18                 err_sys ("popen error");
 19
 20         /*copy argc[1] to Pager*/
 21
 22         while (fgets (line , MAXLINE, fpin) != NU    LL)
 23         {
 24                 if (fputs (line, fpout) == EOF)
 25                         err_sys("fputs error to p    ipe");
 26         }
 27
 28         if (ferror (fpin))
 29                 err_sys("fget error");
 30         if (pclose(fpout) == -1)
 31                 err_sys("pclose error");
 32         exit (0);
 33 }
这个程序是利用POPEN函数对管道的操作,方便吧(比起仅用write/read函数来进程间通信)

 

深入研究POPEN函数(下面是APUE中POPEN函数的实现)

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


106         return(stat);   /* return child's termination status */
107 }
现在读程序:

程序25行好理解,程序上有解释。

程序32行惊现怪物;

靠open_max函数是在第二章出现的:                                                                                                                                                  53,1-8        96%
1 #include <errno.h>
  2 #include <limits.h>
  3 #include "../../ourhdr.h"
  4
  5 #ifdef   OPEN_MAX;
  6 static int openmax = OPEN_MAX;
  7 #else
  8 static int openmax = 0;
  9 #endif
 10
 11 #define OPEN_MAX_GUESS 256
 12
 13
 14 int open_max (void)
 15 {
 16         if (openmax == 0)
 17         {
 18                 errno = 0;
 19
 20                 if ( (openmax = sysconf (_SC_OPEN    _MAP)) < 0)
 21                 {
 22                         if (errno == 0)
 23                                 openmax = OPEN_MA    X_GUESS;
 24                         else
 25                                 err_sys("sysconf     error for _SC_OPEN_MAX");
 26                 }
 27         }
 28
 29         return (openmax);
 30 }
 这是open_max函数源码:

读popen源码进程中断,开始open_max函数:

*******************************************************************************************************************************************************

程序open_max作用:确定打开的最大文件数.

我们用的是POSIX。1的OPEN——MAX确定可以打开的文件数上限。

这样能够提高程序的可移植性,但是这个值仍然是不缺定的则有问题

如果使用下面代码:

#include <unistd.h>

for (i  = 0; i < sysconf(_SC_OPEN_MAX); i++)

       close (i);

而且如果OPEN——AMX是不确定的那么sysconf将仍然返回-1,

于是,for循环根本就不会执行。在这种情况下最好的选择就是关闭所有

描述符直至某个任意的限制值(例如256)

这并不能保证在所有情况下都能正确工作,但这确实我们最好的选择

程序19行判断在正在运行的系统中OPEN——MAX这个值是不是确定的

是就返回OPEN——AMX

不是就指定为256

但是在这个程序中又有怪物出现   “sysconf"

下面开始sysconf函数“

¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥

#include <unistd.h>

       long sysconf(int name);

DESCRIPTION
       POSIX allows an application to test at compile or run time whether certain options are supported, or what the value is of certain configurable constants or
       limits.

       At compile time this is done by including <unistd.h> and/or <limits.h> and testing the value of certain macros.

       At run time, one can ask for numerical values using the present function sysconf().  On can ask for numerical values that may depend on the file  system  a
       file is in using the calls fpathconf(3) and pathconf(3).  One can ask for string values using confstr(3).

       The values obtained from these functions are system configuration constants.  They do not change during the lifetime of a process.

这是man出的东东,能读懂大概就行,呵呵

参数:-SC—OPEN-MAX在/usr/include/bits/confname.h中定义

但是这个值在头文件中没有解释

在函数sysconf中有详细解释

下面是网上找的sysconf实验:

#include  < stdio.h >
#include 
< unistd.h >

#define  ONE_MB (1024 * 1024)

int  main ( void )
{
    
long  num_procs;
    
long  page_size;
    
long  num_pages;
    
long  free_pages;
    
long   long   mem;
    
long   long   free_mem;

    num_procs 
=  sysconf (_SC_NPROCESSORS_CONF);
    printf (
" CPU 个数为: %ld 个/n " , num_procs);

    page_size 
=  sysconf (_SC_PAGESIZE);
    printf (
" 系统页面的大小为: %ld K/n " , page_size  /   1024  );

    num_pages 
=  sysconf (_SC_PHYS_PAGES);
    printf (
" 系统中物理页数个数: %ld 个/n " , num_pages);

    free_pages 
=  sysconf (_SC_AVPHYS_PAGES);
    printf (
" 系统中可用的页面个数为: %ld 个/n " , free_pages);

    mem 
=  ( long   long ) (( long   long )num_pages  *  ( long   long )page_size);
    mem 
/=  ONE_MB;

    free_mem 
=  ( long   long )free_pages  *  ( long   long )page_size;
    free_mem 
/=  ONE_MB;

    printf (
" 总共有 %lld MB 的物理内存, 空闲的物理内存有: %lld MB/n " , mem, free_mem);
    
return  ( 0 );
}

 

 

看到啦吧

很强呀,呵呵

$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$

中断返回:继续看函数open_max函数

***************************************************************************************************************************************************

分配记录子进程的空间

原因:

首先每次调用popen时,应当记
住所创建的子进程的进程ID,以及其文件描述符或FILE指针。我们选择在数组chi
ldpid中保存子进程ID,并用文件描述符作为其下标。于是,当以FILE指针作为参
数调用pclose时,我们调用标准I/O函数fileno以得到文件描述符,然后取得子进
程ID,并用于调用waitpid。因为一个进程可能调用popen多次,所以我们在动态分
配childpid数组时(第一次调用popen时),其长度可以容纳与文件描述符数相同
的进程数。

我的理解就是程序33行是我们后面调用pclose函数用的

记录每个popen产出的子进程

if ((pid = fork()) < 0) {                 /*popen的第二步,fork出一个子进程*/
 41                 return(NULL);   /* errno set by fork() */
 42         } else if (pid == 0) {                                                  /* child */
 43                 if (*type == 'r') {
 44                         close(pfd[0]);
 45                         if (pfd[1] != STDOUT_FILENO) {
 46                                 dup2(pfd[1], STDOUT_FILENO);
 47                                 close(pfd[1]);
 48                         }
 49                 } else {
 50                         close(pfd[1]);
 51                         if (pfd[0] != STDIN_FILENO) {
 52                                 dup2(pfd[0], STDIN_FILENO);
 53                                 close(pfd[0]);
 54                         }
 55                 }
 56
这里作用子进程

因为对子进程的操作只有"r"或"w"这里上面已经确定

关掉作为"r"或"w‘是不是用的管道端

然后吧标准输入或输出搞到管道段

然后这个管道端的文件标识符就可以关掉啦

这是管道链接的就是所要求的标准输出或输出

/* close all descriptors in child    pid[] */
 58                 for (i = 0; i < maxfd; i++)
 59                         if (childpid[i] > 0)
 60                                 close(i);
POSIX.2要求子进程关闭在以前调用popen时形成,当前仍旧打开的所有I/O流。为
此,我们在子进程中从头逐个检查childpid数组的各元素,关闭仍旧打开的任一描
述符。

 /* parent continues... */
 67         if (*type == 'r') {
 68                 close(pfd[1]);
 69                 if ((fp = fdopen(pfd[0], type)) == NULL)
 70                         return(NULL);
 71         } else {
 72                 close(pfd[0]);
 73                 if ((fp = fdopen(pfd[1], type)) == NULL)
 74                         return(NULL);
 75         }
 76
 77         childpid[fileno(fp)] = pid;     /* remember child pid for this fd */
 78         return(fp);
 79 }
对父进程的操作

同子进程一样

不多说

最后是父进程要记住子进程的PID

调用关闭是用

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值