进程间通信之popen和pclose函数
常见的操作是创建一个管道连接到另一个进程,然后读其输出或向其输入端发送数据,为此,标准I/O库提供了两个函数popen和pclose。这两个函数实现的操作是:创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,然后等待命令终止。
#include <stdio.h> FILE *popen(const char *cmdstring, const char *type);
返回值:若成功则返回文件指针,若出错则返回NULL int pclose(FILE *fp);
返回值:cmdstring的终止状态,若出错则返回-
函数popen先执行fork,然后调用exec以执行cmdstring,并且返回一个标准I/O文件指针。如果type是“r”,则文件指针连接到cmdstring的标准输出(见图15-5)。
fp相当于管道的fd[0], stdout相当于管道的fd[1].
图15-5 执行fp = popen(cmdstring, “r”)函数的结果
如果type是“w”,则文件指针连接到cmdstring的标准输入(见图15-6)。
fp相当于管道的fd[1], stdin相当于管道的fd[0].
图15-6 执行fp = popen(cmdstring, “w”)函数的结果
pclose函数关闭标准I/O流,等待命令执行结束,然后返回shell的终止状态。(我们曾在http://www.cnblogs.com/nufangrensheng/p/3510101.html对终止状态进行过说明,system函数(http://www.cnblogs.com/nufangrensheng/p/3512291.html)也返回终止状态。)如果shell不能被执行,则pclose返回的终止状态与shell已执行exit(127)一样。
cmdstring由Bourne shell以下列方式执行:
sh -c cmdstring
这表示shell将扩展cmdstring中的任何特殊字符。 例如,可以使用:
fp = popen("ls *.c", "r");
或者
fp = popen("cmd 2>&1", "r");
实例
程序清单15-4 用popen向分页程序传送文件
#include "apue.h"
#include <sys/wait.h> #define PAGER "${PAGER:-more}" /* environment variable, or default */ int
main(int argc, char *argv[])
{
char line[MAXLINE];
FILE *fpin, *fpout; if(argc != )
err_quit("usage: a.out <pathname>");
if((fpin = fopen(argv[], "r")) == NULL)
err_sys("can't open %s", argv[]); 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) == -)
err_sys("pclose error"); exit();
}
使用popen减少了需要编写的代码量。
shell命令${PAGER:-more}的意思是:如果shell变量PAGER已经定义,且其值非空,则使用其值,否则使用字符串more。
实例:popen和pclose函数
程序清单15-5是我们编写的popen和pclose版本。
程序清单15-5 popen和pclose函数
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h> /*
* Pointer to array allocated at run-time.
*/
static pid_t *childpid = NULL; /*
* From our open_max(), open_max()函数见http://www.cnblogs.com/nufangrensheng/p/3496323.html中的程序清单2-4。
*/
static int maxfd; FILE *
popen(const char *cmdstring, const char *type)
{
int i;
int pfd[];
pid_t pid;
FILE *fp; /* only allow "r" or "w" */
if((type[] != 'r' && type[] != 'w') || type[] != )
{
errno = EINVAL; /* required by POSIX */
return(NULL);
} if(childpid == NULL) /* first time through */
{
/* allocate zerod out array for child pids */
maxfd = open_max();
if((childpid = calloc(maxfd, sizeof(pid_t))) == NULL)
return(NULL);
} if(pipe(pfd) < )
return(NULL); /* errno set by pipe() */ if((pid = fork()) < )
{
return(NULL); /* error set by fork() */
}
else if(pid == )
{
if(*type == 'r')
{
close(pfd[]);
if(pfd[] != STDOUT_FILENO)
{
dup2(pfd[], STDOUT_FILENO);
close(pfd[]);
}
}
else
{
close(pfd[]);
if(pfd[] != STDIN_FILENO)
{
dup2(pfd[], STDIN_FILENO);
close(pfd[]);
}
} /* close all descriptors in childpid[] */
for(i=; i < maxfd; i++)
if(childpid[i] > )
close(i); execl("/bin/sh", "sh", "-c", cmdstring, (char *));
_exit();
} /* parent continues... */
if(*type == 'r')
{
close(pfd[]);
if((fp = fdopen(pfd[], type)) == NULL)
return(NULL);
}
else
{
close(pfd[]);
if((fp = fdopen(pfd[], type)) == NULL)
return(NULL);
} childpid[fileno(fp)] = pid; /* remeber child pid for this fd */
return(fp);
} int
pclose(FILE *fp)
{
int fd, stat;
pid_t pid; if(childpid == NULL)
{
errno = EINVAL;
return(-); /* popen() has never been called */
} fd = fileno(fp);
if((pid = childpid[fd]) = )
{
errno = EINVAL;
return(-); /* fp wasn't opened by popen() */
} childpid[fd] = ;
if(fclose(fp) == EOF)
return(-); while(waitpid(pid, &stat, ) < )
if(errno != EINTR)
return(-); /* error other than EINTR from waitpid() */ return(stat); /* return child's termination status */
}
这里有许多需要考虑的细节:首先,每次调用popen时,应当记住所创建的子进程的进程ID,以及其文件描述符或FILE指针。我们选择在数组childpid中保存子进程ID,并用文件描述符作为其下标。于是,当以FILE指针作为参数调用pclose时,我们调用标准I/O函数fileno得到文件描述符,然后取得子进程ID,并用其作为参数调用waitpid。因为一个进程可能调用popen多次,所以在动态分配childpid数组时(第一次调用popen时),其数组长度应当是最大文件描述符数,于是该数组中可以存放与最大文件描述符数相同的子进程。
POSIX.1要求子进程 关闭在之前调用popen时打开且当前仍旧打开的所有I/O流。为此,在子进程中从头逐个检查childpid数组的各元素,关闭仍旧打开的任何描述符。
若pclose的调用者已经为信号SIGCHLD设置了一个信号处理程序,则pclose中的waitpid调用将返回一个EINTR。因为允许调用者捕捉此信号(或者任何其他可能中断waitpid调用的信号),所以当waitpid被一个捕捉到的信号中断时,我们只是再次调用waitpid。
注意,如果应用程序调用waitpid,并且获得popen所创建的子进程的终止状态,则在应用程序调用pclose时,其中将调用waitpid,它发现子进程已不再存在,此时返回-1,errno被设置为ECHILD。
注意,popen绝不应由设置用户ID或设置用户组ID程序调用。当它执行命令时,popen等同于:
execl("/bin/sh", "sh", "-c", command, NULL);
它在从调用者继承的环境中执行shell,并由shell解释执行command。一个心怀不轨的用户可以操纵这种环境,使得shell能以设置ID文件模式所授予的提升了的权限以及非预期的方式执行命令。
popen特别适用于构造简单的过滤器程序,它变换运行命令的输入或输出。当命令希望构造它自己的管道线时,就是这种情形。
实例
考虑一个应用程序,它向标准输出写一个提示,然后从标准输入读1行。使用popen,可以在应用程序和输入之间插入一个程序以便对输入进行变换处理。图15-7显示了为此做的进程安排。
图15-7 用popen对输入进行变换处理
对输入进行的变化可能是路径名扩充,或者是提供一种历史机制(记住以前输入的命令)。
程序清单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();
}
对该过滤程序进行编译,其可执行目标代码放在文件myuclc中(也就是编译后的可执行文件名为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) == -)
err_sys("pclose error");
putchar('\n');
exit();
}
因为标准输出通常是行缓冲的,而提示符并不包括换行符,所以在写了提示之后,需要调用fflush。
本篇博文内容摘自《UNIX环境高级编程》(第二版),仅作个人学习记录所用。关于本书可参考:http://www.apuebook.com/。
进程间通信之popen和pclose函数的更多相关文章
- 【IPC通信】基于管道的popen和pclose函数
http://my.oschina.net/renhc/blog/35116 [IPC通信]基于管道的popen和pclose函数 恋恋美食 恋恋美食 发布时间: 2011/11/12 23:20 ...
- linux下代替system的基于管道的popen和pclose函数
linux下使用system需要谨慎,那么代替它的方法是什么呢? 标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行. 这里我们称调用popen的进程为父进程,由pop ...
- [转][IPC通信]基于管道的popen和pclose函数
标准I/O函数库提供了popen函数,它启动另外一个进程去执行一个shell命令行. 这里我们称调用popen的进程为父进程,由popen启动的进程称为子进程. popen函数还创建一个管道用于父子进 ...
- Linux进程间通信(三):匿名管道 popen()、pclose()、pipe()、close()、dup()、dup2()
在前面,介绍了一种进程间的通信方式:使用信号,我们创建通知事件,并通过它引起响应,但传递的信息只是一个信号值.这里将介绍另一种进程间通信的方式——匿名管道,通过它进程间可以交换更多有用的数据. 一.什 ...
- 管道通信(使用popen和pclose功能简单的控制管道)
函数原型: FILE *popen(const char * command ,const char *mode) int pclose(FILE * stream) 当心: 采用popen和pclo ...
- PHP pclose() 函数
定义和用法 pclose() 函数关闭由 popen() 打开的进程. 如果失败,该函数返回 FALSE. 语法 pclose(pipe) 参数 描述 pipe 必需.规定由 popen() 打开的进 ...
- 进程间通信系列 之 消息队列函数(msgget、msgctl、msgsnd、msgrcv)及其范例
进程间通信系列 之 概述与对比 http://blog.csdn.net/younger_china/article/details/15808685 进程间通信系列 之 共享内存及其实例 ...
- 详解linux进程间通信-管道 popen函数 dup2函数
前言:进程之间交换信息的唯一方法是经由f o r k或e x e c传送打开文件,或通过文件系统.本章将说明进程之间相互通信的其他技术-I P C(InterProcess Communication ...
- popen && pclose函数
1. 函数操作: 创建一个管道,调用fork产生一个子进程,关闭管道的不使用端,执行一个shell以运行命令,然后等待命令终止: 2. 函数原型: #include <stdio.h> F ...
随机推荐
- NSarray 赋值 拷贝 等问题记录
1. NSArray * a1 = @[@"1",@"2",@"3"]; NSArray * a2 = a1; a1跟a2所指向的地址是一样 ...
- UIButton 设置为圆形,并且使用图片(UIImage)当做背景
-(UIButton *)shareButtonWithIcon:(NSString *)iconName { UIButton *button = [UIButtonbuttonWithType:U ...
- 状压DP uvalive 6560
// 状压DP uvalive 6560 // 题意:相邻格子之间可以合并,合并后的格子的值是之前两个格子的乘积,没有合并的为0,求最大价值 // 思路: // dp[i][j]:第i行j状态下的值 ...
- Tkinter教程之Event篇(1)'
本文转载自:http://blog.csdn.net/jcodeer/article/details/1823544 ''Tkinter教程之Event篇(1)'''# 事件的使用方法'''1.测试鼠 ...
- poj1000 A+B Problem
Description Calculate a+b Input Two integer a,b (0<=a,b<=10) Output Output a+b Sample Input 1 ...
- erlang局域网内节点通信——艰难四步曲 (转)
http://blog.chinaunix.net/uid-22566367-id-382011.html 在Programming Erlang这本书中,在写到第十章中,主要实现的是不同节点之间的通 ...
- 第二百八十五天 how can I 坚持
今天好平凡啊. 晚上给徐斌打电话说忘带钥匙了,一块吃了个饭. 回到家,什么都不想做,好消沉. 玩了几局象棋,很多东西只是玩玩,但还是会认真,认真就会输,好惨. 最近在关注万科幸福里,可是.首付付不起啊 ...
- homework-05
经过这几天的深思熟虑我和小明同学将这次作业基本的完整了,可能界面略丑陋,但是基本功能均已实现.我们的服务器端采用python编写,因为服务器端是这次作业的难点,而python中有一个叫做web.py的 ...
- 宏_CRTIMP分析
CRTIMP是C run time implement的简写,C运行库的实现的意思. 作为用户代码,不应该使用这个东西. 该参数决定 运行时 到底用 动态链接库 还是静态链接 #ifndef _CRT ...
- bzoj 2190 仪仗队(欧拉函数)
2190: [SDOI2008]仪仗队 Time Limit: 10 Sec Memory Limit: 259 MBSubmit: 2245 Solved: 1413[Submit][Statu ...