管道之popen,pclose函数

时间:2022-11-25 05:55:03

 方法6popen,pclose函数:

            参考:   https://www.cnblogs.com/nufangrensheng/p/3561190.html

 

#include <stdio.h>

FILE *popen(const char *cmdstring,const char *type);

int pclose(FILE *fp);

返回值:cmdstring(shell)的终止状态,若出错则返回-1

 

  popen函数: 

pipe---->fork---->exec :创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令.

管道之popen,pclose函数

执行fp = popen(cmdstring, “r”)函数的结果

type为’r’连接到cmdstring标准输出;此时fp相当于管道的fd[0], stdout相当于管道的fd[1]. 

返回一个指向fd[1] 端的FILE文件指针,然后从fd读取该数据

管道之popen,pclose函数

type为’w’连接到cmdstring的标准输入,此时fp相当于管道的fd[1], stdin相当于管道的fd[0].

         返回fp,fp写,也就写到了子进程的fd[0],把写入的数据(命令)/bin/sh执行.

 pclose函数:  关闭标准i/o流,然后等待命令终止

                   popen向分页程序传送文件

            

#include"apue.h"

#include <sys/wait.h>

#define PAGER    "${PAGER:-more}"    /*PAGER若定义使用其值 , or default使用more */

int main(int argc,char *argv[])

{

    char    line[MAXLINE];

    FILE    *fpin, *fpout;

 

    if(argc != 2)    err_quit("usage: a.out <pathname>");

    if((fpin =fopen(argv[1], "r")) == NULL)  err_sys("can't open %s", argv[1]);

 

    if((fpout =popen(PAGER, "w")) == NULL)  err_sys("popen error");

 

    /* copy argv[1] to pager */

    while(fgets(line, MAXLINE, fpin) != NULL)

    {

        if(fputs(line,fpout) == EOF)   err_sys("fputs error to pipe");

    }

    if(ferror(fpin))  err_sys("fgets error");

    if(pclose(fpout) == -1)   err_sys("pclose error");

 

    exit(0);

}


执行:

 管道之popen,pclose函数


 popen,pclose函数的实现

                            参考:  http://blog.csdn.net/litingli/article/details/5891726 

1.                           #include <errno.h>

1. #include <fcntl.h>

2. #include <stdio.h>

3. #include <unistd.h>

 

4. static pid_t *childpid = NULL;

5. static int maxfd;

6. 

7. FILE * popen(const char *cmdstring, const char *type)

8. {

9.     int i;

10.     int pfd[2];

11.     pid_t pid;

12.     FILE *fp;

13. 

14.     /* only allow "r" or "w" */

15.     if ((type[0] != 'r' && type[0] != 'w') || type[1] != 0) 

16.      {

17.         errno = EINVAL; /* required by POSIX */

18.         return(NULL);

19.     }

20. 

21.     if (childpid == NULL)/* first time through */

22.     {     /* allocate zeroed out array for child pids */

23.         maxfd = open_max();//返回最大文件描述符个数,其里面实现用sysconf(_SC_OPEN_MAX)

24.         if ((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)

25.             return(NULL);

26.     }

27. 

28.     if (pipe(pfd) < 0)   return(NULL); /* errno set by pipe() */

29.     if (pfd[0] >= maxfd || pfd[1] >= maxfd  )

30.     { //maxfd <管道文件描述符,则认为很多文件描述符是打开的.

31.          close(pfd[0]);   close(pfd[1]);  errnp = EMFILE;  return (NULL);

32.     }

33.     if ((pid = fork()) < 0)

34.     {

35.         return(NULL); /* errno set by fork() */

36.     } 

37.     else if (pid == 0) /* child */

38.     {     

39.         if (*type == 'r') 

40.          {

41.             close(pfd[0]);

42.             if (pfd[1] != STDOUT_FILENO) 

43.              {

44.                 dup2(pfd[1], STDOUT_FILENO);

45.                 close(pfd[1]);

46.             }

47.         } 

48.          else 

49.          {

50.             close(pfd[1]);

51.             if (pfd[0] != STDIN_FILENO) 

52.              {

53.                 dup2(pfd[0], STDIN_FILENO);

54.                 close(pfd[0]);

55.             }

56.         }

57.     

58.         for (= 0; i < maxfd; i++)    /* close all descriptors in childpid[] */

59.             if (childpid[i] > 0)   close(i); //关闭调用open依旧打开着的的文件描述符.

60. 

61.         execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);

62.         _exit(127);

63.     }

64. 

65.     if (*type == 'r')   /* parent continues... */

66.     {

67.         close(pfd[1]);

        if ((fp = fdopen(pfd[0], type)) == NULL)  return(NULL);//将文件描述符转化为FILE*指针.

68.     } 

69.      else 

70.      {

71.         close(pfd[0]);

72.         if ((fp = fdopen(pfd[1], type)) == NULL)  return(NULL);

73.     }

74. 

75.     childpid[fileno(fp)] = pid; /* remember child pid for this fd */

76.     return(fp);

77. }

78. 

79. int pclose(FILE *fp)

80. {

81.     int fd, stat;

82.     pid_t pid;

83. 

84.     if (childpid == NULL) 

85.     {

86.         errno = EINVAL;

87.         return(-1); /* popen() has never been called */

88.     }

89. 

90.     fd = fileno(fp);

91.     if ((pid = childpid[fd]) == 0) //通过文件描述符得到进程id.

92.     {

93.         errno = EINVAL;

94.         return(-1); /* fp wasn't opened by popen() */

95.     }

96. 

97.     childpid[fd] = 0;

98.     if (fclose(fp) == EOF)   return(-1);

99.     

100.     while (waitpid(pid, &stat, 0) < 0) 

101.         if (errno != EINTR)   return(-1); /* error other than EINTR from waitpid() */

102. /*内核不SIGCHLD信息排队,如果有b个子进程同时结束,父进程会收到1个信号,信号处理函数中while就会循环n次,

103. 把所有结束的子进程处理掉,直到没有已经结束的进程。*/

104.     return(stat); /* return child's termination status */

105. }

                             

                               WaitpidSIGCHLD信号:

说明参考:

 http://blog.csdn.net/roland_sun/article/details/32084825

 http://blog.csdn.net/yinghuashihun/article/details/6387405

 http://blog.csdn.net/liuxingen/article/details/38350347

 https://www.cnblogs.com/wanpengcoder/p/5310983.html

返回EINTR:

    1pclose的调用者已近注册SIGCHLD信号处理函数,调用pclose时,其内部的waitpid返回EINTR.

返回ECHILD:

   1popen的调用进程调用watpid获取到popen子进程的退出状态,调用pclose时,其内部的waitpid返回ECHILD.

   2signal设置了忽略SIGCHLD信号的情况下,内核会处理子进程的退出状态并释放子进程资源将,等父进程调用waitpid时,发现对应的pid进

    程已不存在,因而返回-1错误,errno为10(No child processes).

Popen实现的过滤器:                           


参考:  https://www.cnblogs.com/nufangrensheng/p/3561190.html

注意,popen绝不应由设置用户ID或设置用户组ID程序调用。当它执行命令时,popen等同于:

execl("/bin/sh","sh","-c", command, NULL);

它在从调用者继承的环境中执行shell,并由shell解释执行command。一个心怀不轨的用户可以操纵这种环境,使得shell能以设置ID文件模式所授予的提升了的权限以及非预期的方式执行命令。

popen特别适用于构造简单的过滤器程序,它变换运行命令的输入或输出。当命令希望构造它自己的管道线时,就是这种情形。

实例

考虑一个应用程序,它向标准输出写一个提示,然后从标准输入读1行。使用popen,可以在应用程序和输入之间插入一个程序以便对输入进行变换处理。图15-7显示了为此做的进程安排。

管道之popen,pclose函数

  15-7popen对输入进行变换处理

对输入进行的变化可能是路径名扩充,或者是提供一种历史机制(记住以前输入的命令)。

程序清单15-6是一个简单的过滤程序,它只是将标准输入复制到标准输出,在复制时,将所有大写字符变换为小写字符。在写了一行以后,对标准输出进行了冲洗(用fflush),其理由可参考进程间通信之协同进程。

程序清单15-6将大写字符转换成小写字符的过滤程序

#include "apue.h"

#include <ctype.h>

int main(void)

{

    int c;   

    while((c = getchar()) != EOF)

    {

        if(isupper(c))   c = tolower(c);

        if(putchar(c) == EOF)     err_sys("output error");

        if(c =='\n')   fflush(stdout);

    }

    exit(0);

}

编译该过滤程序,也就是编译后的可执行文件名为myuclc,然后在程序清单15-7中用popen调用它们。

      程序清单15-7调用大写/小写过滤程序以读取命令

#include "apue.h"

#include <sys/wait.h>

int main(void)

{

    char    line[MAXLINE];

    FILE    *fpin;

    if((fpin =popen("/home/zhu/apue/myuclc","r")) == NULL)    err_sys("popen error");

    for(;;)

    {

        fputs("prompt> ", stdout);

        fflush(stdout);

        if(fgets(line, MAXLINE, fpin) == NULL)    /* read from pipe */    break;

        if(fputs(line, stdout) == EOF)  /*执行结果写到终端上去*/  err_sys("fputs error to pipe");

    }

    if(pclose(fpin) == -1)      err_sys("pclose error");

    putchar('\n');

    exit(0);

}


因为标准输出通常是行缓冲的,而提示符并不包括换行符,所以在写了提示之后,需要调用fflush