管道使用举例

//----------------------------------------------------

//AUTHOR: lanyang123456

//DATE: 2014-10-26

//----------------------------------------------------





管道是一种两个进程间进行单向通信的机制。因为管道传递数据的单向性,管道又称为半双工管道。管道的这一特点决定了其使用的局限性。具有以下特点:

*** 数据只能由一个进程流向另一个进程(其中一个读管道,一个写管道);如果要进行双工通信,需要建 立两个管道。
*** 管道只能用于父子进程或者兄弟进程间通信。,也就是说管道只能用于具有亲缘关系的进程间通信。






#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

int
main(int argc, char *argv[])
{
	int pipefd[2];
	pid_t cpid;
	char buf;

	if (argc != 2) {
		fprintf(stderr, "Usage: %s <string>\n", argv[0]);
		exit(EXIT_FAILURE);
	}

	if (pipe(pipefd) == -1) {
               perror("pipe");
               exit(EXIT_FAILURE);
	}
	printf("before fork\n");

	cpid = fork();
	if (cpid == -1) {
               perror("fork");
               exit(EXIT_FAILURE);
	}
	
	if (cpid == 0) {    /* Child reads from pipe */
		close(pipefd[1]);          /* Close unused write end */
		printf("child get parent's:\n");
		while (read(pipefd[0], &buf, 1) > 0)
			write(STDOUT_FILENO, &buf, 1);

		write(STDOUT_FILENO, "\n", 1);
		close(pipefd[0]);
		printf("--------child exit-------\n");
		_exit(EXIT_SUCCESS);
		//exit(EXIT_SUCCESS);

	} else {            /* Parent writes argv[1] to pipe */
		close(pipefd[0]);          /* Close unused read end */
		printf("parent give child %s\n", argv[1]);
		write(pipefd[1], argv[1], strlen(argv[1]));
		close(pipefd[1]);          /* Reader will see EOF */
		wait(NULL);                /* Wait for child */
		printf("---------parent exit-------\n");
		exit(EXIT_SUCCESS);
	}
}




$ gcc -o test pipe1.c
$ ./test hello
before fork
parent give child hello
child get parent's:
hello
--------child exit-------
---------parent exit-------


有个地方值得关注一下,就是exit和_exit的区别:

exit() 做的工作包括:执行退出处理函数;关闭所有标准iO流(这会导致所有的缓冲数据都会被冲洗,即写到文件);然后调用_exit();

如果这样执行程序,可看到区别。

$ ./test hello > tmp
$ cat tmp
hello
before fork
parent give child hello
---------parent exit-------






下面的代码通过管道实现重定向, 将标准输出定向到管道的写入端,标准输入定向到管道的读取端。







#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


#define	MAXLEN	2048

int main()
{
	int pipefd[2];

	int ret = pipe(pipefd);
	if (ret < 0) {
		perror("pipe");
               	exit(EXIT_FAILURE);	
	}

	int pid = fork();
	if (pid < 0) {
		perror("fork");
               	exit(EXIT_FAILURE);
	} else if (pid == 0) {//child
		close(pipefd[0]);   //关闭不需要的端
	
		if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
			if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
				perror("dup2");
               			exit(EXIT_FAILURE);
			}
			close(pipefd[1]);
		}
		
		//write(STDOUT_FILENO, "one ", strlen("one "));
		execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
		_exit(127);
	} else if(pid > 0) {//parent
		close(pipefd[1]);
		char buffer[MAXLEN];
		char ret;
		int nread = 0;
		memset(buffer, 0, MAXLEN);
		if (pipefd[0] != STDIN_FILENO) { //重定向管道读端到标准输入
			if (dup2(pipefd[0], STDIN_FILENO) != STDIN_FILENO) {
				perror("dup2");
               			exit(EXIT_FAILURE);
			}
			close(pipefd[0]);
		}
		
		/*do {
			ret = read(STDIN_FILENO, buffer + nread, MAXLEN - 1);
			if (ret < 0 && errno == EINTR) {
				continue;			
			} else if (ret > 0) {
				nread += ret;
			}		
		}
		while ( ret > 0);
		
		printf("%s", buffer);
		fflush(stdout);*/
		execlp("wc", "wc", "-l", (char *)0);  //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
		//,所以读取的数据即为上面ls -l的执行结果
	} else {
		printf("fork error/n");
	}

	return 0;
}

 $./test2
17

17行


再举一例:





#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


#define	MAXLEN	2048

int main()
{
	int pipefd[2];

	int ret = pipe(pipefd);
	if (ret < 0) {
		perror("pipe");
               	exit(EXIT_FAILURE);	
	}

	int pid = fork();
	if (pid < 0) {
		perror("fork");
               	exit(EXIT_FAILURE);
	} else if (pid == 0) {//child
		close(pipefd[0]);   //关闭不需要的端
	
		if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
			if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
				perror("dup2");
               			exit(EXIT_FAILURE);
			}
			close(pipefd[1]);
		}
		
		printf("child write to pipe.\n");//现在向标准输出写入,即是向管道写入
		printf("child write to pipe.\n");
		fflush(stdout);//必须的,若无,上面的写到管道的字符串不会立即发送到管道
		write(STDOUT_FILENO, "one ", strlen("one "));
		execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
		_exit(127);
	
	} else if(pid > 0) {//parent
		close(pipefd[1]);
		char buffer[MAXLEN];
		int ret;
		int nread = 0;	
		
		memset(buffer, 0, MAXLEN);
		do {
			ret = read(pipefd[0], buffer + nread, MAXLEN - 1);
			if (ret < 0 && errno == EINTR) {
				continue;			
			} else if (ret > 0) {
				nread += ret;
			}		
		}
		while ( ret > 0);

		printf("parent output: %s", buffer);
		printf("\n");

		//execlp("wc", "wc", "-l", (char *)0);  //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
		//,所以读取的数据即为上面ls -l的执行结果
	} else {
		printf("fork error/n");
	}

	return 0;
}

$ gcc -o test2.1 pipe2.1.c
$ ./test2.1
parent output: child write to pipe.
child write to pipe.
one total 88
-rwxrwxr-x 1 lanyang lanyang 7671 10月 26 21:01 fifo
-rw-rw-r-- 1 lanyang lanyang 1341 10月 26 21:02 fifo.c
-rw-rw-r-- 1 lanyang lanyang 1209 10月 26 21:01 fifo.c~
-rw-rw-r-- 1 lanyang lanyang 1498 10月 25 16:46 notes
-rw-rw-r-- 1 lanyang lanyang 1503 10月 25 10:06 notes~
-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:33 pipe1.1.c
-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:32 pipe1.1.c~
-rw-rw-r-- 1 lanyang lanyang 1410 10月 26 15:26 pipe1.c
-rw-rw-r-- 1 lanyang lanyang 1426 10月 26 15:22 pipe1.c~
-rw-rw-r-- 1 lanyang lanyang 1751 10月 26 16:49 pipe2.1.c
-rw-rw-r-- 1 lanyang lanyang 1758 10月 26 21:25 pipe2.c
-rw-rw-r-- 1 lanyang lanyang 1738 10月 26 17:23 pipe2.c~
-rwxrwxr-x 1 lanyang lanyang 7770 10月 25 15:30 test
-rwxrwxr-x 1 lanyang lanyang 7623 10月 26 16:33 test1.1
-rwxrwxr-x 1 lanyang lanyang 7674 10月 26 17:23 test2
-rwxrwxr-x 1 lanyang lanyang 7944 10月 26 21:29 test2.1

-rw-rw-r-- 1 lanyang lanyang   70 10月 26 21:21 tmp


若程序改为:

#include <sys/wait.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>


#define	MAXLEN	2048

int main()
{
	int pipefd[2];

	int ret = pipe(pipefd);
	if (ret < 0) {
		perror("pipe");
               	exit(EXIT_FAILURE);	
	}

	int pid = fork();
	if (pid < 0) {
		perror("fork");
               	exit(EXIT_FAILURE);
	} else if (pid == 0) {//child
		close(pipefd[0]);   //关闭不需要的端
	
		if (pipefd[1] != STDOUT_FILENO) { //重定向管道输出端到标准输出
			if (dup2(pipefd[1], STDOUT_FILENO) != STDOUT_FILENO) {
				perror("dup2");
               			exit(EXIT_FAILURE);
			}
			close(pipefd[1]);
		}
		
		printf("child write to pipe.\n");//现在向标准输出写入,即是向管道写入
		printf("child write to pipe.\n");
		//fflush(stdout);//必须的,若无,上面的写到管道的字符串不会立即发送到管道
		//write(STDOUT_FILENO, "one ", strlen("one "));
		execlp("ls", "ls", "-l", (char *)0); //由于将stdout重定向为管道的输入端,所以这行的执行结果被存放在管道输入端
		_exit(127);
	
	} else if(pid > 0) {//parent
		close(pipefd[1]);
		char buffer[MAXLEN];
		int ret;
		int nread = 0;	
		
		memset(buffer, 0, MAXLEN);
		do {
			ret = read(pipefd[0], buffer + nread, MAXLEN - 1);
			if (ret < 0 && errno == EINTR) {
				continue;			
			} else if (ret > 0) {
				nread += ret;
			}		
		}
		while ( ret > 0);

		printf("parent output: %s", buffer);
		printf("\n");

		//execlp("wc", "wc", "-l", (char *)0);  //这里wc后面没有给出文件名,所以从标准输入读取,而标准输入端被定向为管道输出端
		//,所以读取的数据即为上面ls -l的执行结果
	} else {
		printf("fork error/n");
	}

	return 0;
}


$ gcc -o test2.1 pipe2.1.c
lanyang@lanyang-ThinkPad-Edge-E431:~/exercise/pipe$ ./test2.1
parent output: total 92
-rwxrwxr-x 1 lanyang lanyang 7671 10月 26 21:01 fifo
-rw-rw-r-- 1 lanyang lanyangn 1341 10月 26 21:02 fifo.c
-rw-rw-r-- 1 lanyang lanyang 1209 10月 26 21:01 fifo.c~
-rw-rw-r-- 1 lanyang lanyang 1498 10月 25 16:46 notes
-rw-rw-r-- 1 lanyang lanyang 1503 10月 25 10:06 notes~
-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:33 pipe1.1.c
-rw-rw-r-- 1 lanyang lanyang 1194 10月 26 16:32 pipe1.1.c~
-rw-rw-r-- 1 lanyang lanyang 1410 10月 26 15:26 pipe1.c
-rw-rw-r-- 1 lanyang lanyang 1426 10月 26 15:22 pipe1.c~
-rw-rw-r-- 1 lanyang lanyang 2838 10月 26 21:31 pipe2.1.c
-rw-rw-r-- 1 lanyang lanyang 2836 10月 26 21:31 pipe2.1.c~
-rw-rw-r-- 1 lanyang lanyang 1758 10月 26 21:25 pipe2.c
-rw-rw-r-- 1 lanyang lanyang 1738 10月 26 17:23 pipe2.c~
-rwxrwxr-x 1 lanyang lanyang 7770 10月 25 15:30 test
-rwxrwxr-x 1 lanyang lanyang 7623 10月 26 16:33 test1.1
-rwxrwxr-x 1 lanyang lanyang 7674 10月 26 17:23 test2
-rwxrwxr-x 1 lanyang lanyang 7835 10月 26 21:32 test2.1
-rw-rw-r-- 1 lanyang lanyang   70 10月 26 21:21 tmp


会发现使用printf写到管道中的数据丢失了。这是因为printf带有缓冲区,标准输入重定向为管道写断后,变为全缓冲。printf的数据不会立刻放到管道中。

后面再执行exec,输出的数据会将前面的数据覆盖,于是管道中就不会有printf的数据了。这个地方如果使用write则会正常,因为write不带缓冲,直接写到管道中。如果带缓冲区的printf,则fflush一下。



命名管道:



#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

#define FNAME "/tmp/TestFIFO"

int main()
{
   char buffer[32] = {0};

   //注意下面的0666
   //4 2 1分别表示读、写和执行权限
   //第一个6表示文件所有者具有读写权限
   //第二个6表示同组用户权限
   //地三个6表示其他用户读写权限

   int mkRet = mkfifo(FNAME, 0666); //mkfifo的第一个参数必须是不存在的文件,执行第二次就会出错

   if (mkRet == -1)
   {
       perror("mkfifo failed\n");
       exit(1);
   }
   int pid = fork();
   if (pid < 0) {
	perror("fork error");
	exit(1);
   }
   else if (pid == 0) //子进程
   {
      int fd = open(FNAME, O_RDONLY);
      read(fd, buffer, sizeof(buffer));  //read会一直等待可读数据
      printf("r content = %s\n", buffer);
      close(fd);
   }
   else if (pid > 0)
   {
     // wait(NULL);
      char s[] = "what a nice day!";
      int fd = open(FNAME, O_WRONLY);
      size_t len = sizeof(s);
      printf("len = %d\n", len);
      write(fd, s, sizeof(s));
      printf("write over!\n");
      close(fd);
   }

   return 0;
}



$ gcc -o fifo fifo.c
$ ./fifo
len = 17
write over!
r content = what a nice day!

第二次执行,则会出错。
$ ./fifo
mkfifo failed
: File exists



参考

http://blog.csdn.net/wutaozhao/article/details/6010665

http://blog.csdn.net/ljianhui/article/details/10202699



  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值