进程间的通信——管道、内存映射


title: 进程间的通信—管道、内存映射
date: 2019-08-07 08:41:50
tags: [Linux,进程]
categories: Linux

1、进程间的通信方式

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

  1. 管道:(无名管道) 管道有亲缘关系进程间的通信,有名管道还允许无亲缘关系进程间通信。

  2. 信号 signal:在软件层模拟中断机制,通知进程某事发生

  3. 消息队列:消息的链表包括 posix 消息队列和 SystemV 消息队列

  4. 共享内存:多个进程访问一块内存主要用于同步

  5. 信号量:进程间同步

  6. 套接字 socket:不同机器间进程通信

管道通信: 有名管道通信&无名管道通信。

2、无名管道

在这里插入图片描述

        无名管道创建必须在fork创建子进程之前创建,不然子进程没法复制端口号,无法通信

①、无名管道的特点

  • 无名管道 应用在具有亲缘关系的进程间通信 。
  • 无名管道的创建在内核当中 是不可见的
  • 无名管道的具体的操作 是可以通过 read write 来操作
  • 管道通信是单向通信,同一时刻只能向一个方向读写数据。

        把无名管道 比作一根水管 ,水管一端进水 、一端出水,进水这端可以称作写端口,写端口的作用是向管道写数据;出水这端 就是读端口,读端口的作用就是将管道中的数据读 出来 。

        读写端口是一组特殊的文件描述符 。其中 fd[0] ------是读端 fd[1] : ------是写端

       

②、无名管道的创建

函数原型#include <unistd.h>
int pipe(int pipefd[2]);
功能创建一个无名管道
参数fd[0]:读端口
fd[1]:写端口
传出参数
成功:0
失败:-1,并设置错误码

注意 : 如果读管道,读不到数据的时候 程序会阻塞,直到有数据接收到 。

案例:无名管道只适合应用在具有亲缘关系的进程间通信,下面利用两根管道实现双向通信

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
int main()
{
	int fd[2];
	int fd2[2];
	char buf[50] = {'0'};
	int ret=pipe(fd);  //创建无名管道
	if(ret < 0)
	{
		perror("pipe\r\n");
		exit(1);
	}
	int ret2=pipe(fd2);
	if(ret2 < 0)
	{
		perror("pipe2\r\n");
		exit(1);
	}
	
	pid_t pid = fork(); //创建子进程
	if(pid < 0)
	{
		printf("fork is error\r\n");
		exit(1);
	}else if(pid == 0)
	{
		while(1)//子进程
		{
			read(fd[0],buf,sizeof(buf));
			printf("父进程回应:%s\r\n",buf);
			printf("子进程发送:\r\n");
			memset(buf,0,sizeof(buf));
			gets(buf);
			write(fd2[1],buf,sizeof(buf));
		}
		
	}else
	{
		while(1)     //父进程
		{
			printf("父进程发送\r\n");
			memset(buf,0,sizeof(buf));
			gets(buf);
			write(fd[1],buf,sizeof(buf));  
			read(fd2[0],buf,sizeof(buf));
			printf("子进程回应:%s\r\n",buf);
		}
		
		wait(NULL);	
	}
	close(fd[0]);
	close(fd[1]);
	
	return 0;
}

在这里插入图片描述

思考: 如果当管道的读端口关闭 ,写进程向管道的写端写数据 ,有没有意义 ?

        没有意义 ,当管道的读端关闭 ,写进程继续向管道的写端 写数据 ,内核会向 写进程发送一个SIGPIEP 信号,来终止当前的进程 。如果希望 执行完毕 可以 利用 signal 来对信号 进行安装 处理,编写处理函数,即对该信号不执行默认操作。

3、有名管道

在这里插入图片描述

管道没有读到数据的时候 一直 会阻塞 直到读到数据之后阻塞结束 。

管道的读端与管道的写端 :

        创建一个有名管道之后,先要打开管道文件,对管道进行读写操作

        如果以读的方式来打开管道文件,则会打开文件的读端口 ;以写的方式打开文件,则打开文件的写端口 。

①、无名管道的特点

  • 解决陌生进程间通信 关系
  • 有名管道本质上是一个节点
  • 有名管道在用户层 是可见的
  • 既然在用户层是可见 ,就是文件 ,要操作文件先要打开文件,再对文件进行读写操作 。

②、有名管道的创建

1、通过命令mkfifo创建

管道的创建 : mkfifo

在这里插入图片描述

管道的删除:unlink
在这里插入图片描述

管道文件的验证

在这里插入图片描述

2、通过函数创建

函数原型int mkfifo(const char*pathname, mode_t mode);
函数功能创建一个有名管道
参数pathname : 创建管道的路径
mode : 与open的mode 一致 ,参数mode为该文件的权限(mode%~umask)
返回值成功:0
失败:-1
函数原型int unlink(const char *pathname);
功能删除管道文件
参数pathname:要删除的管道文件
返回值成功:0
失败:-1,并设置错误码

案列:运用有名管道,实现两个陌生进程间的通信

在这里插入图片描述

实验现象:
在这里插入图片描述

4、标准流管道popen、pclose

        Linux 的文件操作中有基于标准 I/O 操作一样,管道操作也支持基于文件流的操作,这种基于文件流的管道。主要用来创建一个连接到另一个进程的管道,这里“另一个进程”是可以执行一定操作的可执行文件,例如用户执行“ls -l”或者****./pipe,由于这类操作很常见,所以将一系列创建过程合并到一个函数 popen()中完成。

函数原型FILE *popen(const char *command,const char *type);
功能在一个进程当中 实现另外一个进程
参数commad : 要 执行的指令 比如: ls cat
type :以什么样的方式: r -----以读的方式 w: 以写的方式
返回值成功:文件流
失败:NULL
函数原型int pclose(FILE *stream);
功能功能: 关闭标准流管道
参数上面函数的返回值

标准流管道的实现过程 (popen函数实现过程)

  • 创建一个 pipe 管道
  • fork 一个进程
  • 关闭无用的端口
  • 在子进程中 调用 exec 函数族 来 执行 要执行的指令 (可以将子进程中的内容读到父进程 ,也可以将父进程中的内容 写到 子进程中)
  • 调用wait,waitpid回收子进程

5、内存映射

        Linux 系统利用已有的存储管理机制可以很自然的实现进程间的共享存储.mmap是一种 内存映射文件的方法,即将一个文件或者其他对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写页面带对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。

在这里插入图片描述

相应的系统调用.

函数原型void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
功能将文件或其他对象映射到进程中
参数addr:要映射的内存地址,一般使用NULL 表示 系统自动分配 内存地址
length:映射区域的大小
prot:方式,有以下三种
PROT_EXEC: 可执行
PROT_READ : 可读
PROT_WRITE: 可写。使用可读写时可以将两个相或
flags:
MAP_SHARED: 共享的 ,经常使用在多进程的通信中,即修改进程映射区域的内容会写回到文件当中,操作进程中映射的区域会同步到文件当中
MAP_PRIVATE:写入时复制,当修改操作内存映射区域,不会将修改的内容 写回到源文件当中。
fd: 要映射的文件
offset : 偏移量一般是文件的起始位置,给0值开始偏移
返回值成功:映射建立成功,成功之后的映射区域的首地址
失败:(void *) -1)

注意: 一般将一个文件映射到进程中,这个文件不能为空文件。

函数原型int munmap(void *addr, size_t length);
功能取消mmap建立的映射
参数addr:mmap的返回值 映射后 的首地址,
length:映射区域的大小
返回值成功:0

案例: 利用映射。实现两个陌生进程通信

在这里插入图片描述

结果:

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Linux下,进程通信的一种方式是通过共享内存来实现的。共享内存允许两个或多个进程共享一定的存储区,这样它们就可以直接访问同一块内存区域,而不需要进行数据的复制。共享内存是一种高效的进程通信方式,因为数据直接写入内存,不需要多次数据拷贝,所以传输速度很快\[2\]。 在使用共享内存进行进程通信时,需要给共享内存创建一个唯一的身份ID,以便区分不同的共享内存。当进程需要访问共享内存时,需要在映射时带上这个ID,这样就可以确定访问的是哪一个共享内存\[3\]。 需要注意的是,共享内存并没有提供同步机制,也就是说,在一个进程结束对共享内存的写操作之前,并没有自动机制可以阻止另一个进程开始对它进行读取。为了实现多个进程对共享内存的同步访问,通常会使用信号量来实现对共享内存的同步访问控制\[2\]。 总结起来,Linux下的共享内存是一种高效的进程通信方式,允许多个进程共享一块存储区。通过给共享内存创建唯一的身份ID,可以区分不同的共享内存。然而,共享内存并没有提供同步机制,需要使用信号量来实现对共享内存的同步访问控制\[2\]\[3\]。 #### 引用[.reference_title] - *1* *3* [Linux多进程通信——共享内存实现](https://blog.csdn.net/zhm1949/article/details/124909541)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Linux下进程通信方式——共享内存](https://blog.csdn.net/xujianjun229/article/details/118584955)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值