第10章 I/O重定向和管道

unix进程使用文件描述符0、1和2作为标准输入、输出和错误的通道。

当进程请求一个新的文件描述符的时候,系统内核将最低可用的文件描述符赋给它

1.将stdin定向到文件

(1)close-then-open

/* stdinredir1.c
 *	purpose: show how to redirect standard input by replacing file 
 *		 descriptor 0 with a connection to a file.
 *	 action: reads three lines from standard input, then
 *		 closes fd 0, opens a disk file, then reads in
 *		 three more lines from standard input
 */
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>

void main()
{
	int	fd ;
	char	line[100];

	/* read and print three lines */

	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );

	/* redirect input */

	close(0);
	fd = open("/etc/passwd", O_RDONLY);
	if ( fd != 0 ){
		fprintf(stderr,"Could not open data as fd 0\n");
		exit(1);
	}

	/* read and print three lines */

	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
}

(2)open-close-dup-close

(3)open-dup2-close

/* stdinredir2.c
 * 	shows two more methods for redirecting standard input
 *	use #define to set one or the other
 */
#include<stdio.h>
#include<fcntl.h>
#include<stdlib.h>
#define	CLOSE_DUP		/* open, close, dup, close */
/* #define	USE_DUP2	/* open, dup2, close */

void main()
{
	int	fd ;
	int	newfd;
	char	line[100];

	/* read and print three lines */

	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );

	/* redirect input */
	fd = open("/etc/passwd", O_RDONLY);	/* open the disk file	*/
#ifdef CLOSE_DUP
	close(0);
	newfd = dup(fd);		/* copy open fd to 0	*/
#else
	newfd = dup2(fd,0);		/* close 0, dup fd to 0 */
#endif
	if ( newfd != 0 ){
		fprintf(stderr,"Could not duplicate fd to 0\n");
		exit(1);
	}
	close(fd);			/* close original fd	*/

	/* read and print three lines */

	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
	fgets( line, 100, stdin ); printf("%s", line );
}

dup和dup2函数

2.为其他程序重定向I/O:who > userlist

/* whotofile.c
 *	purpose: show how to redirect output for another program
 *	   idea: fork, then in the child, redirect output, then exec
 */
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
void main()
{
	int	pid ;
	int	fd;

	printf("About to run who into a file\n");

	/* create a new process or quit */
	if( (pid = fork() ) == -1 ){
		perror("fork");
                exit(1);
	}
	/* child does the work */
	if ( pid == 0 ){
		close(1);				/* close, */
		fd = creat( "userlist", 0644 );		/* then open */
		if(execlp( "who", "who", NULL ) == -1)	/* and run	*/
			perror("execlp");
		exit(1);
	}
	/* parent waits then reports */
	if ( pid != 0 ){
		wait(NULL);
		printf("Done running who.  results in userlist\n");
	}
}

文件描述符集合通过exec调用传递且不会被改变,shell使用进程通过fork产生子进程与子进程调用exec之间的时间间隔来重定向标准输入、输出到文件。

3.管道编程

(1)管道自己给自己发送数据

/*  pipedemo.c	* Demonstrates: how to create and use a pipe
 *		* Effect: creates a pipe, writes into writing
 *		  end, then runs around and reads from reading
 *		  end.  A little weird, but demonstrates the idea.
 */
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

void main()
{
	int	len, i, apipe[2];	/* two file descriptors */
	char	buf[BUFSIZ];		/* for reading end	*/

	/* get a pipe */
	if ( pipe ( apipe ) == -1 ){
		perror("could not make pipe");
		exit(1);
	}
	printf("Got a pipe! It is file descriptors: { %d %d }\n", 
							apipe[0], apipe[1]);

	/* read from stdin, write into pipe, read from pipe, print */

	while ( fgets(buf, BUFSIZ, stdin) ){
		len = strlen( buf );
		if (  write( apipe[1], buf, len) != len ){	/* send	*/
			perror("writing to pipe");		/* down */
			break;					/* pipe */
		}
		for ( i = 0 ; i<len ; i++ )                     /* wipe	*/
			buf[i] = 'X' ;
		len = read( apipe[0], buf, BUFSIZ ) ;		/* read */
		if ( len == -1 ){				/* from */
			perror("reading from pipe");		/* pipe */
			break;
		}
		if ( write( 1 , buf, len ) != len ){		/* send  */
			perror("writing to stdout");		/* to    */
			break;					/* stdout */
		}
	}
}

(2)使用fork来共享信道

/* pipedemo2.c	* Demonstrates how pipe is duplicated in fork()
 *		* Parent continues to write and read pipe,
 *		  but child also writes to the pipe
 */
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>

#define	CHILD_MESS	"I want a cookie\n"
#define	PAR_MESS	"testing..\n"
#define	oops(m,x)	{ perror(m); exit(x); }

void main()
{
	int	pipefd[2];		/* the pipe	*/
	int	len;			/* for write	*/
	char	buf[BUFSIZ];		/* for read	*/
	int	read_len;

	if ( pipe( pipefd ) == -1 )
		oops("cannot get a pipe", 1);

	switch( fork() ){
		case -1:
			oops("cannot fork", 2);
	
		/* child writes to pipe every 5 seconds */
		case 0:			
			len = strlen(CHILD_MESS);
			while ( 1 ){
				if (write( pipefd[1], CHILD_MESS, len) != len )
					oops("write", 3);
				sleep(5);
			}

		/* parent reads from pipe and also writes to pipe */
		default:		
			len = strlen( PAR_MESS );
			while ( 1 ){
				if ( write( pipefd[1], PAR_MESS, len)!=len )
					oops("write", 4);
				sleep(1);
				read_len = read( pipefd[0], buf, BUFSIZ );
				if ( read_len <= 0 )
					break;
				write( 1 , buf, read_len );
			}
	}
}

(3)使用pipe、fork和exec

/* pipe.c		
 *	* Demonstrates how to create a pipeline from one process to another
 *	* Takes two args, each a command, and connects
 *	  av[1]'s output to input of av[2]
 *	* usage: pipe command1 command2
 *	  effect: command1 | command2
 *	* Limitations: commands do not take arguments
 *	* uses execlp() since known number of args
 *	* Note: exchange child and parent and watch fun
 */
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

#define	oops(m,x)	{ perror(m); exit(x); }

void main(int ac, char **av)
{
	int	thepipe[2],			/* two file descriptors	*/
		newfd,				/* useful for pipes	*/
		pid;				/* and the pid		*/

	if ( ac != 3 ){
		fprintf(stderr, "usage: pipe cmd1 cmd2\n");
		exit(1);
	}
	if ( pipe( thepipe ) == -1 )		/* get a pipe		*/
		oops("Cannot get a pipe", 1);

	/* ------------------------------------------------------------ */
	/*	now we have a pipe, now let's get two processes		*/

	if ( (pid = fork()) == -1 )			/* get a proc	*/
		oops("Cannot fork", 2);

	/* ------------------------------------------------------------ */
	/* 	Right Here, there are two processes			*/
	/*             parent will read from pipe			*/

	if ( pid > 0 ){			/* parent will exec av[2]	*/
		close(thepipe[1]);	/* parent doesn't write to pipe	*/

		if ( dup2(thepipe[0], 0) == -1 )
			oops("could not redirect stdin",3);

		close(thepipe[0]);	/* stdin is duped, close pipe	*/
		execlp( av[2], av[2], NULL);
		oops(av[2], 4);
	}

	/*	 child execs av[1] and writes into pipe			*/

	close(thepipe[0]);		/* child doesn't read from pipe	*/

	if ( dup2(thepipe[1], 1) == -1 )
		oops("could not redirect stdout", 4);

	close(thepipe[1]);		/* stdout is duped, close pipe	*/
	execlp( av[1], av[1], NULL);
	oops(av[1], 5);
}

4.管道并非文件

管道在unix进程只能单向数据流动,只有共同父进程的进程之间才可以使用管道连接。

(1)从管道中读数据

当进程试图从管道中读数据时,进程被挂起直到数据被写进管道;

当所有的写者关闭了管道,试图从管道中读取数据的调用返回0,这意味着文件的结束;

管道是一个队列,当进程从管道中读取完数据之后数据已经不存在了。

(2)向管道中写数据

写入数据阻塞直到管道有空间去容纳新的数据;

写入必须保证一个最小的块大小;

若无读者在读数据,则写操作失败。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值