无名管道pipe()

本文介绍了Linux进程间通信的六种方式,重点讲解了无名管道的概念、API使用及其实现进程间的单向和双向通信。无名管道通过pipe()函数创建,具有读写端口,并且在读端口关闭后,向写端口写入内容会导致SIGPIPE信号,使进程终止。

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

Linux 进程间通信方式主要有下面 6 种:

1. 管道 和命名管道:管道(无名管道) 有亲缘关系进程间的通信,命名管道还允许无亲缘关系进程间通信
2. 信号 signal:在软件层模拟中断机制,通知进程某事发生
3. 消息队列:消息的链表包括 posix 消息队列和 SystemV 消息队列
4. 共享内存:多个进程访问一块内存主要用于同步
5. 信号量:进程间同步
6. 套接字 socket:不同机器间进程通信

无名管道

  1. 只能在具有亲缘关系的进程间进行通信。
  2. 无名管道在内核当中, 是不可见的。
  3. 无名管道本质上是一段内存。
  4. 创建一个无名管道需要调用pipe 接口,创建一个无名管道会同时创建一个读端口,一个写端口。
    读端口具有读数据的 功能,写端口具有写数据的功能。
    读端口 通过 fd[ 0] 来表示
    写端口 通过 fd[ 1] 来表示
    管道的容量由 PIPE_BUF 的值决定
    可以把无名管道比喻成一个水管,一端进水一端出水,进水的这端就是写端口出水这端就是读端口 。

API:

pipe() 创建一个无名管道

#include <unistd.h>
原型:int pipe(int pipefd[2]);
功能: 创建一个无名管道
参数: fd[2] :
fd[0] : 读端 :读取数据
fd[1] : 写端 : 写数据
返回值: 成功: 0 失败:-1 以及设置相应的 errno 全局的变量
帮助记忆:创建一个数组, fd[0]读数据,fd[1] 写数据
端口是特殊的文件描述符

案例1: 验证创建的无名管道的成功,以及读端进行读写端进行成功,并没有进行进程间的通信。

#include <stdio.h>
#include <unistd.h>
#include <string.h>

//创建管道
int main()
{
	
	char * p  = "hello";
	char buf[1024] = {'0'};
	ssize_t  retrd;
	int fd[2];
	int ret = pipe(fd);
	if(ret < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
	
	//wirte
	printf("----------------------------write-----------------------\r\n");
	retrd = write(fd[1],p,strlen(p));
	printf("----------------------------write end-----------------------\r\n");
	printf("----------------------------read-----------------------\r\n");
	
	read(fd[0],buf,retrd);
	
	printf("read buf:%s\r\n",buf);
	
	return 0;
}

运行:
[root@localhost ]# gcc pipe.c
[root@localhost]# ./a.out
----------------------------write-----------------------
----------------------------write end-----------------------
----------------------------read-----------------------
read buf:hello

案例:在父子进程间实现 通信比如 父进程 写hello 子进程读到一个hello 即可 -----单向通信

#include <stdio.h>
#include <unistd.h>
#include <string.h>

//创建管道
int main()
{
	char  p[10] = {'0'};
	
	char buf[1024] = {'0'};
	ssize_t  retrd;
	int fd[2];
	int ret = pipe(fd);//
	if(ret < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
 	pid_t pid = fork();
	if(pid < 0)
	{
		printf("fork is error\r\n");
		return -1;
	}else if (0 == pid)
	{
		sleep(5);
		printf(" i am child \r\n");
		
		printf("----------------------------read-----------------------\r\n");
		
		retrd = read(fd[0],buf,sizeof(p));
		if(retrd < 0)
		{
			printf("read is error\r\n");
			return -1;
		}
		
		printf("read buf:%s\r\n",buf);
			
	}else
	{
		printf(" i am parent \r\n");
		//wirte
		printf("----------------------------write-----------------------\r\n");
		gets(p);  //自己键盘输入
		
		//scanf("%s",p);
		retrd = write(fd[1],p,sizeof(p));  //往写管道端写数据
		
		printf("p:%s\r\n",p);
		wait(NULL);
		printf("----------------------------write end-----------------------\r\n");	
		
	}
	
	return 0;
}

[root@localhost ]# gcc pipe.c
[root@localhost ]# ./a.out
i am parent
----------------------------write-----------------------
qqqqqqqqqq
p:qqqqqqqqqq

i am child
----------------------------read-----------------------
read buf:qqqqqqqqqq
----------------------------write end-----------------------
[root@localhost]#

父子进程通过管道实现双向通信(两根管道)

#include <stdio.h>
#include <unistd.h>
#include <string.h>

//创建管道
int main()
{
	
	char  p[20] = {'0'};
	
	char buf[20] = {'0'};
	ssize_t  retrd;
	int fd[2];
	int fds[2];
	
	//创建管道
	int ret = pipe(fd);
	if(ret < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
	
	//创建管道2
	int ret1 = pipe(fds);
	if(ret1 < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
	
	//fork进程
 	pid_t pid = fork();
	if(pid < 0)
	{
		printf("fork is error\r\n");
		return -1;
	}else if (0 == pid)
	{
		while(1)
		{
			memset(buf,0,sizeof(buf));
			retrd = read(fd[0],buf,sizeof(p));
			if(retrd < 0)
			{
				printf("read is error\r\n");
				return -1;
			}
			printf("child recvbuf:%s\r\n",buf);
			//write
			printf("chlid:\r\n");
			memset(p,0,sizeof(p));
			gets(p);
			retrd = write(fds[1],p,sizeof(p));	
		}	
		
			
	}else
	{
		printf(" i am parent \r\n");
		while(1)
		{
				
			//wirte
			printf("parent:\r\n");
			memset(p,0,sizeof(p));
			gets(p);
			retrd = write(fd[1],p,sizeof(p));
			
			memset(buf,0,sizeof(buf));
			retrd = read(fds[0],buf,sizeof(p));
			if(retrd < 0)
			{
				printf("read is error\r\n");
				return -1;
			}
			printf("parent recvbuf:%s\r\n",buf);
			
		}
		
		
		//回收子进程的退出状态
		wait(NULL);
	}
	
	return 0;
}

运行结果:

[root@localhost ]# ./a.out 
 i am parent 
parent:
qqqqq
child recvbuf:qqqqq
chlid:
wwwwwwwwww
parent recvbuf:wwwwwwwwww
parent:
eeeeeeeeee
child recvbuf:eeeeeeeeee
chlid:
zzzzzzzzzz
parent recvbuf:zzzzzzzzzz
parent:

案例3: 能否利用一根管道实现 双向通信。
可以,利用挂起进程的时候,对端口进行读写(有局限性)

#include <stdio.h>
#include <unistd.h>
#include <string.h>

//创建管道
int main()
{
	
	char * p = "hello parent";
	
	char *buf ="hello child" ;
	char buf2[20] = {'0'};
	
	ssize_t  retrd;
	int fd[2];	
	//创建管道
	int ret = pipe(fd);
	if(ret < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
	
	//fork进程
 	pid_t pid = fork();
	if(pid < 0)
	{
		printf("fork is error\r\n");
		return -1;
	}else if (0 == pid)
	{
			sleep(1);
			retrd = read(fd[0],buf2,strlen(p));
			if(retrd < 0)
			{
				printf("read is error\r\n");
				return -1;
			}
			printf("child recvbuf:%s\r\n",buf2);
			//write
			printf("chlid:\r\n");

			retrd = write(fd[1],p,strlen(p));	
	}else
	{
		    printf(" i am parent \r\n");
			//wirte
			printf("parent:\r\n");
			//memset(p,0,sizeof(p));
		    //gets(p);
			retrd = write(fd[1],buf,strlen(buf));
			
			sleep(2);
			memset(buf2,0,sizeof(buf2));
			retrd = read(fd[0],buf2,strlen(p));
			if(retrd < 0)
			{
				printf("read is error\r\n");
				return -1;
			}
			printf("parent recvbuf:%s\r\n",buf2);
		//回收子进程的退出状态
		wait(NULL);
	}
	
	return 0;
}

运行结果:

[root@localhost 201811.19]# gcc pipe.c 
[root@localhost 201811.19]# ./a.out 
 i am parent 
parent:
child recvbuf:hello child
chlid:
parent recvbuf:hello parent
[root@localhost 201811.19]#

读写端口都是特殊的文件描述符 。close 进行关闭 。

思考:当管道的读端口 关闭之后,向管道的写端口 写内容 有没有意义?

答:

当管道的读端口 关闭之后,写端口向读管道写内容没有意义 ,并且 如果写进程一直向 管道写内容 会使 进程收到来自内核发送的信号 ,SIGPIPE ,收到这个信号会使进程终止 。

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

//信号处理函数
void fun(int arg)
{
	printf("recv from  SIGPIPE signal\r\n");
}


//创建管道
int main()
{
	
	char * p = "hello parent";
	
	char *buf ="hello child" ;
	char buf2[20] = {'0'};
	
	ssize_t  retrd;
	ssize_t retrd1;
	
	int fd[2];
	int fds[2];
	//信号的捕捉  当捕捉到SIGPIPE 信号的时候  会启动信号处理函数	
	signal(SIGPIPE,fun);
	
	//创建管道
	int ret = pipe(fd);
	if(ret < 0)
	{
		printf("pipe is error\r\n");
		return -1;
	}
	//fork进程
 	pid_t pid = fork();
	if(pid < 0)
	{
		printf("fork is error\r\n");
		return -1;
	}else if (0 == pid)
	{
	     
			retrd = read(fd[0],buf2,strlen(p));
			if(retrd < 0)
			{
				printf("read is error\r\n");
				return -1;
			}
			printf("child recvbuf:%s\r\n",buf2);
			close(fd[0]);	//关闭读端口
	}else
	{
		printf(" i am parent \r\n");
		close(fd[0]);	//关闭读端口
		retrd = write(fd[1],buf,strlen(buf));
		//回收子进程的退出状态
		wait(NULL);	
		printf("kkkkkkkkkk\r\n");
		retrd1 = write(fd[1],buf,strlen(buf));	
		if(retrd1  < 0)	
		{
			printf("write+++\r\n");
		}
		  printf("write end..........\r\n");
	}
	return 0;
}


运行结果:

[root@localhost ]# gcc pipe.c 
[root@localhost ]# ./a.out 
 i am parent 
child recvbuf:hello child
kkkkkkkkkk
recv from  SIGPIPE signal
write+++
write end..........
[root@localhost ]#
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值