自己实现popen函数

自己实现了popen函数mypopen,后来查看了popen函数的源码发现自己实现的与其相差无几,本函数与linux中的实现最大的不同是不需要用专门的pclose()函数来关闭文件指针,用普通的fclose()即可,linux实现的代码也会给出在下文,可以对比一下其中差异。

主要通过pipe管道实现,具体思路如下:

        1、使用pipe()建立管道

        2、使用fork()创建子进程

        3、在子进程中调用exec族函数执行命令,通过管道将结果传送至父进程

        4、在主进程中等待子进程执行,子进程执行完成后将接收其结果,返回结果的文件指针

下面是mypopen源代码,重点部位已通过注释进行标注:

FILE *mypopen(char *cmd,char type)
{
	int pipefd[2];           //管道描述符
	int pid_t;               //进程描述符

	if(type !='r' && type != 'w')
	{
		printf("myopen() flag error/n");
		return NULL;
	}

	if(pipe(pipefd)<0)        //建立管道
	{
		printf("myopen() pipe create error/n");
		return NULL;
	}
	
	pid_t=fork();             //建立子进程

	if(pid_t < 0) 
 		return NULL;

	if(0 == pid_t)            //子进程中......
	{
		if(type == 'r')
		{
			close(pipefd[0]);               //此函数返回文件指针是用于读,因此,在子进程中应该将结果写入管道,这样父进程返回的文件指针才能读,所以这里将读端关闭
			dup2(pipefd[1],STDOUT_FILENO);  //exec函数的执行结果将会通过标准输出写到控制台上,但这里我们不需要在控制台输出,而是需要将结果返回,因此将标准输出重定向到管道写端
			close(pipefd[1]);		

		}
		else{
			close(pipefd[1]);
			dup2(pipefd[0],STDIN_FILENO);
			close(pipefd[0]);
		}
		char *argv[] = {cmd,NULL};	
		if(execvp(cmd,argv)<0)          //用exec族函数执行命令
		    return NULL;	
	}
	
	wait(0);                                //等待子进程返回

	if(type=='r'){
		close(pipefd[1]);
		return fdopen(pipefd[0],"r");	//由于程序需要返回的参数是文件指针,因此需要用fdopen函数将描述符打开,其返回值为相应的文件指针	
	}else{
		close(pipefd[0]);
		return fdopen(pipefd[1],"w");
	}
}


下面是popen()在linux中的实现:

/*
 *	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 */
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值