Linux进程通信无名管道和有名管道

三、进程间通信

1、 无名管道

​ 无名管道:1、半双工 2、只有具有亲缘关系的两个进程才可使用

主要是用于父子进程间进行通信

1、pipe函数创建无名管道

#include <unistd.h>
	int pipe(int fd[2]);
//成功返回0 , 失败返回-1

经由参数返回2个文件描述符,fd[0] 为读而打开,fd[1]为写而打开 , fd[1]的输出是fd[0]的输入

通常,进程会先调用pipe,接着调用fork,从而创建从父进程到子进程的IPC通道。

在这里插入图片描述

fork之后做什么取决于我们想要的数据流方向。对于从父进程到子进程的管道,父进程关闭读端fd[0],子进程关闭写端fd[1], 那么就是下图所示的数据流向
在这里插入图片描述

当管道的一端被关闭后,下列两条规则起作用

(1) 当(read) 一个写端已被关闭管道时,在所有数据都被读取后,read返回0,表示文件结束

(2) 如果写(write) 一个读端已被关闭的管道时,则产生信号SIGPIPE。如果忽略该信号,或者捕捉该信号并从其处理程序返回,则write返回-1, errno设置为EPIPE。

在写管道时,常量PIPE_BUF规定了内核管道缓冲区大小(pathconffpathconf函数)可以确定PIPE_BUF的大小。如果对管道调用write,而且要求写的字节数大小小于等于PIPE_BUF,则此操作不会与其他进程对同一个管道的write操作交叉进行,否则会出现数据交叉

父子进程双向通信

父子进程双向通信采用的是 两个pipe 一个是 父进程---->子进程 ,另一个是 父进程<-------子进程

#include<stdio.h>
#include <unistd.h>
#include <string.h>
int main(int argc,char *argv[])
{
	int p_fd[2];		// p -> c
	int c_fd[2];		// c -> p
	int cli_fd;
	int ret = pipe(p_fd);
	if (ret == -1)
		perror("pipe");
	ret = pipe(c_fd);
	if (ret == -1)
		perror("pipe");
	cli_fd = fork();
	if (cli_fd == -1)
	{
		perror("fork");
		return -1;
	}
	if (cli_fd == 0) //子进程
	{
		char buf[128] = "hello parent";
		close(p_fd[1]); // 子进程读取关闭写端
		close(c_fd[0]); // 子进程发送,关闭读端
		write(c_fd[1], buf, strlen(buf));
		memset(buf, 0, 128);
		read(p_fd[0],buf, sizeof(buf));
		printf("child recv buf :%s\n", buf);
	}
	else //父进程 
	{
		char buf[128] = "hello child";
		close(p_fd[0]);
		close(c_fd[1]);
		write(p_fd[1], buf, strlen(buf));
		read(c_fd[0],buf, sizeof(buf));
		printf("parent recv buf :%s\n", buf);
	}
	return 0;
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wMjriPMO-1620633211071)(linux%E9%AB%98%E7%BA%A7%E7%BC%96%E7%A8%8B.assets/image-20210427174131999.png)]

2、有名管道FIFO

​ FIFO有时被称为命名管道。未命名的管道只能在两个有亲缘关系的进程之间使用。 但是通过FIFO无亲缘关系也可使用

1、创建FIFO
       #include <sys/types.h>
       #include <sys/stat.h>

       int mkfifo(const char *pathname, mode_t mode);

       #include <fcntl.h>           /* Definition of AT_* constants */
       #include <sys/stat.h>

       int mkfifoat(int dirfd, const char *pathname, mode_t mode);
		//两个函数的返回值, 成功 返回0 , 失败返回-1

mkfifo和mkfifoat相似。但是mkfifoat可以被用来在fd文件描述符表示的目录相关位置创建一个FIFO。

参数mode 权限 与 文件IO的方式相同

关于mkfifoat的 path 有三种情况

(1) 如果path参数指定的是绝对路径名,则fd参数会被忽略掉,并且mkfifoat函数的行为和mkfifo类似

(2) 如果path参数指定的是相对路径,则fd参数是一个打开目录的有效文件描述符路径名和目录有关

(3) 如果path参数指定的是相对路径,并且fd参数有一个特殊值AT_FDCWD,则路径名以当前目录开始,mkfifoat 和 mkfifo类似。

当我们使用mkfifo或者mkfifoat创建FIFO时,要用open来打开它

一般情况下,(没有指定O_NONBLOCK) ,只读open要阻塞到某个其他进程为写而打开这个FIFO为止,类似的,只写open要阻塞到某个其他进程为读而打开为止。

2、实例

创建管道

#include<stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc,char *argv[])
{
	if (mkfifo("FIFO", 777) == -1)
	{
		perror("FIFO");
		return -1;
	}
	return 0;
}

读管道进程

	/*************************************************************************
    > File Name: fifo_read.c
    > 作者:YJK
    > Mail: 745506980@qq.com
    > Created Time: 2021年04月28日 星期三 09时11分59秒
 ************************************************************************/

#include<stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#define FIFO_NAME "FIFO"
#define BUF_SIZE 128
// 读端
int main(int argc,char *argv[])
{
	struct stat st;
	int fd;
	char buf[BUF_SIZE] = {0};
	// 首先判断是否为FIFO文件,如果不是则退出
	if (lstat(FIFO_NAME, &st) == -1)
	{
		perror("lstat");
		return -1;
	}
	fd = open(FIFO_NAME, O_RDONLY);	// 以只读方式打开
	if (fd == -1)
	{
		perror("open");
		return -1;
	}
	int ret = 1;
	while (ret)
	{
		ret = read(fd, buf,BUF_SIZE);
		if (ret < 0)
		{
			perror("read");
			break;
		}
		printf("recv buf %s\n", buf);
		memset(buf, 0, BUF_SIZE);
	}
	close(fd);
	return 0;
}

写管道进程

#include<stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <string.h>
#define FIFO_NAME "FIFO"
#define BUF_SIZE 128
// 写端
int main(int argc,char *argv[])
{
	struct stat st;
	int fd;
	char *buf="hello world test1\n";
	// 首先判断是否为FIFO文件,如果不是则退出
	if (lstat(FIFO_NAME, &st) == -1)
	{
		perror("lstat");
		return -1;
	}
	fd = open(FIFO_NAME, O_WRONLY);	//只写打开
	if (fd == -1)
	{
		perror("open");
		return -1;
	}
	int ret = 10;
	while (ret)
	{
		write(fd, buf ,strlen(buf));
		sleep(1);
		ret --;
	}
	close(fd);
	return 0;
}

在这里插入图片描述

刚开始两个写进程 以只写方式打开FIFO因为没有读FIFO所以阻塞

两个写进程向FIFO写数据,一个读进程读取数据

3、总结

FIFO与无名管道相比, FIFO可以实现无亲缘关系的进之间进行通信。

​ 当使用阻塞方式打开FIFO时, 只读open阻塞到某个其他进程为写而打开这个FIFO为止

只写open要阻塞到某个进程为读而打开这个FIFO为止

无名管道与有名管道总结

实际上就是一个内核中的队列

无名管道

​ 1、半双工 (要实现全双工的话,需要两个无名管道)

​ 2、只能用于具有亲缘关系的进程

​ 3、无名管道存在于内核空间, 不属于任何文件系统,只存在于内存中

有名管道

​ FIFO,在文件系统目录中存在一个管道文件FIFO

​ 管道文件仅仅是文件系统中的标识,并不在磁盘上占据空间。在使用时,在内存上开辟空间,作为两个进程数据交互的通道

在这里插入图片描述

​ FIFO可以用于无亲缘关系的进程之间进行通信

1、相同点
open打开管道文件以后,在内存中开辟了一块空间,管道的内容在内存中存放,有两个指针—-头指针(指向写的位置)和尾指针(指向读的位置)指向它。读写数据都是在给内存的操作,并且都是半双工通讯。
2、区别
有名在任意进程之间使用,无名在有亲缘关系进程之间使用。

拓展:
全双工、半双工、单工通讯的区别:
单工:方向是固定的,只有一个方向可以写,例如广播。
半双工:方向不固定,但在某一刻只能有一个方向进行写,例如对讲机。
全双工:两个方向都可以同时写,例如打电话。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值