UNIX管道--简介与示例

本文深入讲解UNIX系统中的管道通信机制,包括管道的局限性、术语定义、代码示例及重点总结。介绍了管道如何实现进程间通信,数据流向的单向性,以及仅限于有共同祖先的进程使用的特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

管道是UNIX系统最古老的IPC形式。它有以下两点局限性:

  • 数据只能单向流动
  • 只能在具有公共祖先的两个进程间使用。

通常是父进程通过系统调用pipe()创建管道,然后fork()出子进程,这两个进程就可以通过管道进行通信了。

术语定义

管道fd:一条管道有两个文件描述符,这两个文件描述符是在调用pipe(fd)创建管道时获取获取的。

  • fd[0]:通过该文件描述符可以从管道中读取数据
  • fd[1]:通过该文件描述符可以向管道写入数据

写端进程:只向管道写数据的进程。在使用管道时,先close(fd[0])、再通过write(fd[1])向管道写数据。
读端进程:只从管道读数据的进程。在使用管道时,先close(fd[1])、在通过read(fd[0])从管道读取数据。

代码示例

下列代码在linux环境下,粘贴到本地后可以直接编译运行。

#include <unistd.h>	//declare int pipe(int pipefd[2]);
#include <limits.h> //PIPE_BUF
#include <stdio.h>
#include <sys/wait.h>
#include <string.h>

#define MSG_FROM_PARENT "Hello, world!"

int fd[2] = {0};	//存储管道的两个文件描述符,fd[0] for read, fd[1] for write

int main(int argc, char **argv)
{
	int pid = 0;

	/* 打印一下PIPE_BUF的值,这个值定义在linux/limits.h中 */
	// #define PIPE_BUF        4096	/* # bytes in atomic write to a pipe */
	printf("PIPE_BUF:[%d]\n", PIPE_BUF);

	if (0 != pipe(fd))
	{
		perror("pipe");
		return -1;
	}

	pid = fork();
	if (pid < 0)	// fork error
	{
		perror("fork");
		return -1;
	}
	else if (pid > 0)	// branch of parent
	{
		close(fd[0]);  // parent close read

		/* write to pipe */
		if (write(fd[1], MSG_FROM_PARENT, sizeof(MSG_FROM_PARENT)) < 0)
		{
			perror("write");
			close(fd[1]);
			return -1;
		}

		/* !!非常重要,writer写完后必须关闭write end
		 * 否则reader在读完管道中的数据后,再次读管道时会因为没有数据而一直阻塞 */
		close(fd[1]);	//建议注释掉这行代码,运行查看下效果。有惊喜哟。你会发现printf()中\n有将缓冲区刷新的效果

		/* 父进程阻塞等待子进程退出 */
		if (waitpid(pid, NULL, 0) < 0)
		{
			perror("waitpid");
			return -1;
		}

		printf("parent exit\n");
	}
	else	// branch of son
	{
		close(fd[1]);

		char read_buf[5] = {0}; // 故意把buf设的小一点,这样可以多次读取管道,观察更多细节
		int readlen = 0;
		do{
			memset(read_buf, 0, sizeof(read_buf));
			perror("before read");
			readlen = read(fd[0], read_buf, sizeof(read_buf) - 1);
			if (readlen < 0)
			{
				perror("read");
				close(fd[0]);
				return -1;
			}
			perror("after read");
			printf("%s", read_buf); // printf()的fmt如果加上'\n',其效果可不只是换行哟。
									//'\n'其实还有fflush(stdout)的作用:将printf缓冲区的数据刷新到stdout(即屏幕)上。
		}while (readlen > 0);

		close(fd[0]);
		printf("\nson exit\n");
	}

	return 0;
}

重点总结

如果pipe的一端被关闭 ,以下两条规则将生效:

  1. 如果写端关闭,那么读端将管道中的数据全部读完后,如果再次调用read,将会返回0。
    换言之,如果写端进程写完数据后没有关闭pipe的写端文件描述符,那么读端进程在读取一个不再有数据可读的管道时会阻塞。
  2. 如果读端关闭,那么写端write时,将会产生信号SIGPIPE,如果该信号未被处理,write将返回-1,且errno被置为EPIPE。
    关于linux信号处理,有时间单写一篇博客总结一下。这里只需要注意,向一个读端关闭的管道写数据,write会返回-1;如果读端没有关闭,但是管道的数据一直没有被读端进程读走,那么当管道中缓存数据达到PIPE_BUF规定的上限时(本示例中打印出的上限是4096 bytes),write会阻塞!

以上两条规则告诉我们,不管是读端进程还是写端进程,用完了管道都要记得关闭啊!否则对端进程再次读写管道时会阻塞的!好严重的!

=============
先写到这里,其实还有一些需要总结,有时间再填坑吧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值