Linux系统进程间通信:标准流管道popen和pclose函数

popen简介:

Linux系统提供了两个标准流管道的操作函数popen和pclose。这两个函数实现的操作是:
创建一个管道,fork一个子进程,关闭管道的不使用端,通过exec函数执行一个shell命令,等待命令终止。

popen函数声明:

#include <stdio.h>

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

功能:popen打开的数据管道流,pclose关闭数据管道流。popen必须由pclose来关闭。

参数说明:

command:shell 命令字符串的指针。

type:用来表示读("r")或者写("w")的,如果type是"r",则返回的文件指针是可读的,如果type是"w",则是可写的。不能同时为读和写。

返回值:返回:若成功则为文件指针,若出错则为NULL。

popen函数使用举例:

模拟一个shell命令的实现,将命令结果保存到已有文件中。

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

/*将命令行执行的结果保存到一个已经存在的文件中 */ 
int main(int argc, char* argv[])
{
    
    FILE *pipe_fp;
    FILE *infile;

    //pid_t pid;
    char buffer[80];

    if (argc != 3) {
        fprintf(stderr, "usage: popen3[commond][filename]\n");
        exit(1);
    }
    if ((infile = fopen(argv[2],"wt")) == NULL) {
        perror("fopen");
        exit(1);
    }

    if ((pipe_fp = popen(argv[1],"r")) == NULL) {
        perror("popen");
        exit(1);
    }

    while (fgets(buffer, sizeof(buffer), pipe_fp) != NULL) {
        // if (tmp[strlen(tmp) - 1] == '\n') {
        //     tmp[strlen(tmp) - 1] = '\0'; //去除换行符
        // }
        fputs(buffer,infile);
    }

    // do {
    //     fgets(buffer,80,pipe_fp);
    //     // if (feof(infile)) {
    //     //     break;
    //     // }
    //     fputs(buffer,infile);
    // } while(!feof(infile));
    fclose(infile);
    pclose(pipe_fp);

    return(0);
}

程序使用方法:

输入三个字符串,第一个是编译好的可执行程序;第二个是shell可以识别的命令;第三个是已经存在的文件。

运行结果:

 myls ls in.txt

其中,myls就是上面的代码编译出来的可执行文件。这样,就把shell命令ls的结果保存在in.txt中了。(注意,必须先创建一个in.txt文件,这是为了简化程序)。

popen函数的源代码(来自于Android):

在Android系统中,我们可以查找到这样一个文件popen.c,文件位置和代码:

bionic/libc/unistd/popen.c:

/*	$OpenBSD: popen.c,v 1.17 2005/08/08 08:05:34 espie Exp $ */
/*
 * Copyright (c) 1988, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software written by Ken Arnold and
 * published in UNIX Review, Vol. 6, No. 8.
 *
 *......//省略若干
 */

#include <sys/param.h>
#include <sys/wait.h>

#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <paths.h>

static struct pid {
	struct pid *next;
	FILE *fp;
	pid_t pid;
} *pidlist;

extern char **environ;

FILE *
popen(const char *program, const char *type)
{
	struct pid * volatile cur;
	FILE *iop;
	int pdes[2];
	pid_t pid;
	char *argp[] = {"sh", "-c", NULL, NULL};

	if ((*type != 'r' && *type != 'w') || type[1] != '\0') {
		errno = EINVAL;
		return (NULL);
	}

	if ((cur = malloc(sizeof(struct pid))) == NULL)
		return (NULL);

	if (pipe(pdes) < 0) {
		free(cur);
		return (NULL);
	}

	switch (pid = fork()) {
	case -1:			/* Error. */
		(void)close(pdes[0]);
		(void)close(pdes[1]);
		free(cur);
		return (NULL);
		/* NOTREACHED */
	case 0:				/* Child. */
	    {
		struct pid *pcur;
		/*
		 * We fork()'d, we got our own copy of the list, no
		 * contention.
		 */
		for (pcur = pidlist; pcur; pcur = pcur->next)
			close(fileno(pcur->fp));

		if (*type == 'r') {
			(void) close(pdes[0]);
			if (pdes[1] != STDOUT_FILENO) {
				(void)dup2(pdes[1], STDOUT_FILENO);
				(void)close(pdes[1]);
			}
		} else {
			(void)close(pdes[1]);
			if (pdes[0] != STDIN_FILENO) {
				(void)dup2(pdes[0], STDIN_FILENO);
				(void)close(pdes[0]);
			}
		}
		argp[2] = (char *)program;
		execve(_PATH_BSHELL, argp, environ);
		_exit(127);
		/* NOTREACHED */
	    }
	}

	/* Parent; assume fdopen can't fail. */
	if (*type == 'r') {
		iop = fdopen(pdes[0], type);
		(void)close(pdes[1]);
	} else {
		iop = fdopen(pdes[1], type);
		(void)close(pdes[0]);
	}

	/* Link into list of file descriptors. */
	cur->fp = iop;
	cur->pid =  pid;
	cur->next = pidlist;
	pidlist = cur;

	return (iop);
}

/*
 * pclose --
 *	Pclose returns -1 if stream is not associated with a `popened' command,
 *	if already `pclosed', or waitpid returns an error.
 */
int
pclose(FILE *iop)
{
	struct pid *cur, *last;
	int pstat;
	pid_t pid;

	/* Find the appropriate file pointer. */
	for (last = NULL, cur = pidlist; cur; last = cur, cur = cur->next)
		if (cur->fp == iop)
			break;

	if (cur == NULL)
		return (-1);

	(void)fclose(iop);

	do {
		pid = waitpid(cur->pid, &pstat, 0);
	} while (pid == -1 && errno == EINTR);

	/* Remove the entry from the linked list. */
	if (last == NULL)
		pidlist = cur->next;
	else
		last->next = cur->next;
	free(cur);

	return (pid == -1 ? -1 : pstat);
}

 从代码路径可以看到,popen是在libc库中实现的,而且是通过调用pipe()函数创建的管道。

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liranke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值