二十、Linux系统编程-管道(一)管道、匿名管道、管道读写规则

本文详细介绍了Unix管道的概念、限制、匿名管道的创建、读写规则,并通过实例展示了父子进程间的通信以及数据重定向的应用。同时阐述了管道读写操作的规则,包括数据读取和写入时的阻塞情况、管道满时的行为以及关闭文件描述符后的响应。

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

一、管道

概念:管道是Unix中最古老的进程间通信的形式。我们把从一个进程连接到另一个进程的一个数据流称为一个“管道”, 管道本质上是内核中一块固定大小的缓冲区
管道限制:
  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道
  • 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父、子进程之间就可应用该管道。

二、匿名管道pipe
函数声明:
#include <unistd.h>
int pipe(int pipefd[2]);
函数参数:pipefd[0]是管道读端,pipefd[1]管道写端
返回值:成功返回0,失败返回-1
创建管道示意图:

管道示例1-父子进程通信:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	}while(0)

int main()
{
	signal(SIGCHLD,SIG_IGN);
	int pfd[2];
	int fd;
	char buff[1024];
	if (pipe(pfd) == -1)
		ERR_EXIT("pipe error");
	if ((fd = fork()) < 0)
		ERR_EXIT("fork error");
	if (fd == 0)
	{
		close(pfd[0]);
		write(pfd[1],"hello",5);
		close(pfd[1]);
		exit(EXIT_SUCCESS);
	}
	close(pfd[1]);
	read(pfd[0],buff,5);
	printf("%s\n",buff);
	return 0;
}
管道示例2-数据重定向:
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

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

#define ERR_EXIT(m) \
	do \
	{ \
		perror(m); \
		exit(EXIT_FAILURE); \
	}while(0)

int main()
{
	signal(SIGCHLD,SIG_IGN);
	int pfd[2];
	int fd;
	char buff[1024];
	if (pipe(pfd) == -1)
		ERR_EXIT("pipe error");
	if ((fd = fork()) < 0)
		ERR_EXIT("fork error");
	if (fd == 0)
	{
		dup2(pfd[1],STDOUT_FILENO);
		close(pfd[0]);
		close(pfd[1]);
		execlp("ls","ls",NULL);
		exit(EXIT_FAILURE);
	}
	dup2(pfd[0],STDIN_FILENO);
	close(pfd[0]);
	close(pfd[1]);
	execlp("wc","wc","-w",NULL);
	printf("%s\n",buff);
	return 0;
}

三、管道读写规则
如果往读端写数据或者从写端读数据都会失败。
(1)、当没有数据可读时
  • O_NONBLOCK disable:read调用阻塞,即进程暂停执行,一直等到有数据来到为止。
  • O_NONBLOCK enable:read调用返回-1,errno值为EAGAIN。
(2)、管道满的时候
  • O_NONBLOCK disable:write调用阻塞,直到有进程读走数据
  • O_NONBLOCK enable:调用返回-1,errno值为EAGAIN
(3)、如果所有管道写端对应的文件描述符被关闭,则read返回0
(4)、如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE
(5)、当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。
(6)、当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。

示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <fcntl.h>


#define ERR_EXIT(m) \
        do \
        { \
                perror(m); \
                exit(EXIT_FAILURE); \
        } while(0)

#define TEST_SIZE 68*1024

int main(void)
{
	char a[TEST_SIZE];
	char b[TEST_SIZE];

	memset(a, 'A', sizeof(a));
	memset(b, 'B', sizeof(b));

	int pipefd[2];

	int ret = pipe(pipefd);
	if (ret == -1)
		ERR_EXIT("pipe error");

	pid_t pid;
	pid = fork();
	if (pid == 0)
	{
		close(pipefd[0]);
		ret = write(pipefd[1], a, sizeof(a));
		printf("apid=%d write %d bytes to pipe\n", getpid(), ret);
		exit(0);
	}

	pid = fork();

	if (pid == 0)
	{
		close(pipefd[0]);
		ret = write(pipefd[1], b, sizeof(b));
		printf("bpid=%d write %d bytes to pipe\n", getpid(), ret);
		exit(0);
	}


	close(pipefd[1]);
	
	sleep(1);
	int fd = open("test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
	char buf[1024*4] = {0};
	int n = 1;
	while (1)
	{
		ret = read(pipefd[0], buf, sizeof(buf));
		if (ret == 0)
			break;
		printf("n=%02d pid=%d read %d bytes from pipe buf[4095]=%c\n", n++, getpid(), ret, buf[4095]);
		write(fd, buf, ret);

	}
	return 0;	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值