【Linux】进程间通讯之管道

进程间通信的机制包括:管道、信号量、共享内存、消息队列。
这篇博客主要介绍的是进程间通讯之管道的应用
一、管道的分类
管道都属于半双工通讯机制
管道分为有名管道和无名管道
1、有名管道
在磁盘上有一个管道文件标识,但是这个管道文件只会占据一个inode点,但是这个管道文件任何时候都不会占据block块。数据在传递过程中会缓存到内存上。这就是有名管道。
管道文件仅仅是为了使得不同的进程(有权限操作)能够共享
2、无名管道
没有管道文件,借助父子进程共享fork之前打开的文件描述符,来实现进程间通信。
二、创建有名管道文件
1、创建有名管道文件的方法
(1)命令:

mkfifo filename

(2)库函数:

int mkfifo()

创建有名管道用到的库函数的方法:open、read、write、close
2、通过有名管道实现进程间通讯
(1)我们在系统上创建两个文件,一个是mainA.c一个是mainB.c。mainA.c用来写文件,mainB.c用来读文件。

//mainA.c
#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <unistd.h>

#include <string.h>

#include <fcntl.h>

int main()

{

	int fd = open("./FIFO",O_WRONLY);

	assert(fd != -1);

	printf("Write open fifo success\n");

	while(1)

	{

		char buff[128] = {0};

		printf("input: ");

		fgets(buff,127,stdin);

		if(strncmp(buff,"end", 3)==0)

		{

			break;

		}

		write(fd,buff, strlen(buff) -1);

	}

	close(fd);

}
//mainB.c 

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

#include <unistd.h>

#include <fcntl.h>

#include <string.h>

int main()

{

	int fd = open("./FIFO",O_RDONLY);

	assert(fd != -1);

	printf("Read open fifo success\n");

	while(1)

	{

		char buff[128] = {0};

		int n = read(fd,buff,127);

		if(n <= 0)

		{

			break;

		}

		printf("Read: %s\n",buff);

	}

	close(fd);

}

(2)如果我们只在系统上执行mainA.c我们发现什么也没有显示:
在这里插入图片描述
这块发生了阻塞,通过分析代码:

Write open fifo success

我们发现执行后这句没有执行,证明是在前面的open处阻塞了,这是因为,open操作的管道文件是以只写的方法打开的,没有任何读的方式去读它,它就会发生阻塞
(3)同样如果我们只在系统上执行mainB.c的话,也是同样的道理,发生阻塞
在这里插入图片描述

(4)当我们同时进行mainA.c和mainB.c的操作时,会发现,当我们在mainA.c中写一个文件mainB.c中就会读入一个。
在这里插入图片描述

(5)原理
在这里插入图片描述
原理就是通过两个进程在磁盘空间是共享的去访问磁盘上的FIFO结点,通过FIFO结点最终会指向内存上的一块空间,然后去写入数据和读取数据。

(6)注意事项

  • open以一种方式打开管道文件会阻塞,直到有进程以另一种方式打开次管道文件
  • 如果管道对应的内存空间中没有数据,则read会阻塞直到内存中有数据或者所有写端关闭才会退出。
  • 如果管道对应的内存空间已满,则write就会阻塞,直到内存中有空间或者所有读端关闭才会返回。

三、创建无名管道文件
1、无名管道的创建与打开:

int pipe(int fds[2]);

创建无名管道用到的系统调用的方法:read、write、close
这块没有open是因为fds[2]中就已经包含了open的参数
2、通过无名管道实现进程间通讯
(1)我们只需创建一个文件pipe.c来实现

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <assert.h>

#include <string.h>

int main()

{

	int fds[2];

	int res = pipe(fds); //fds[0]:管道读端 fds[1]:管道写端

	assert(res != -1);

	pid_t pid = fork();

	assert(pid != -1);

	if(pid == 0)

	{

		close(fds[1]); //子进程直接关闭管道的写端

		while(1)

		{

			char buff[128] = {0};

			int n = read(fds[0],buff,127);

			if(n <= 0)

			{

				break;

			}

			printf("Child: %s\n",buff);

		}

		close(fds[0]);

	}

	else

	{

		close(fds[0]); //父进程直接关闭管道的读端

		while(1)

		{

			char buff[128] = {0};

			printf("input: ");

			fgets(buff, 127, stdin);

			if(strncmp(buff,"end",3)==0)

			{

				break;

			}

			write(fds[1],buff,strlen(buff) -1);

		}

	}

		close(fds[1]);

}

在这里插入图片描述

(2)我们发现子进程打印的东西在input之后。
这是因为:父进程将数据写入之后直接输出printf("input: ");这个操作,不会等待子进程,只要write不阻塞,就会只执行自己的内容,子进程也是如此,父子进程执行相互之间没有关系。
(3)原理
在这里插入图片描述

四、管道的特点

  • 无论有名还是无名, 写入管道的数据都在内存中
  • 管道是一种半双工通信方式(通信方式有单工、半双工。全双工)
  • 有名和无名管道的区别:有名可以在任意进程间使用,而无名主要在父子进程间。
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值