linux.7 进程间通信

1.进程通信的介绍

为什么要有进程通信?
数据传输、资源共享、通知事件、进程控制

进程间通信是如何做到的?
进程运行时是具有独立性的(数据层面),所以进程间通信一般要借助第三方资源(OS),通信就是“数据的拷贝”。进程A将数据拷贝给OS,再由OS拷贝给进程B。所以OSY一定要提供一段内存区域,能够被双方进程看到。

进程间通信的本质就是让不同的进程先看到同一份资源(内存,文件内核缓冲等)

2.管道

管道只能进行单向通信。
怎样让两个进程看到同一块资源呢?父进程task_struct中指向的 struct files_struct 中的 fd_array 指向了一个文件的 struct file ,对应着内存中的文件缓冲区,当创建子进程,子进程会拷贝父进程的数据结构,这时父子进程就指向了同一块 struct file,也就是看到了同一块资源。所以这样可以使用文件的方式,来进行数据共享与管道通信。

2.1 匿名管道

匿名管道就是没有文件名的管道。

在这里插入图片描述

站在文件描述符的角度深度理解管道:
在这里插入图片描述

通过pipe实现父子通信:
在这里插入图片描述

pipe() creates a pipe, a unidirectional data channel that can be used for interprocess communication. The array pipefd is used to return two file descriptors.
Referring to the ends of the pipe. pipefd[0] refers to the read end of the pipe. pipefd[1] refers to the write end of the pipe. Data written to the write
End of the pipe is buffered by the kernel until it is read from the read end of the pipe.
在这里插入图片描述

父子进程通信可不可以创建全局缓冲区来完成通信呢??不可以,父进程与子进程运行具有独立性,子进程写入时发生写时拷贝父进程是看不到的。进程间不可以使用全局变量通信
为什么子进程不发生写时拷贝呢??因为管道通信用的并不是进程自己的空间,而是系统维护的一个已经打开的管道文件,只是通过系统调用接口把一个进程的内容拷贝到管道文件,然后另外一个进程通过系统调用接口读取到管道文件的内容罢了,因此就不需要写时拷贝

站在内核角度管道的本质:所以,看待管道,就如同看待文件一样!管道的使用和文件一致,迎合了“Linux一切皆文件思想”。
在这里插入图片描述
如果现在再多执行流下(父子),看到的同一份资源(临界资源),如果子进程在写入的时候,父进程就来读取了就出问题了,所以要对临界资源保护起来。这种保护机制叫做
同步与互斥
互斥:任何时候只能有一个进程正在使用某种资源。 同步:一个进程等待另一个进程完成后在执行。(这里的父进程等待子进程写入父进程才会读取,如果父进程识别到管道内为空就进入阻塞状态)
如果写端关闭,读端就会read返回0,代表文件结束;如果打开文件的进程退出了,文件也就会被释放掉。
匿名管道适合具有血缘关系的进程进行通信,常用于父子。

4种情况:
不write,一直read,read阻塞(pipe为空等子进程写)
不read,一直write,write阻塞(pipe写满了等父进程读)
write写完,关闭,read返回值为0
read关闭,一直写,写方(child)被操作系统杀掉,写入无意义(操作系统收到 SIGPIPE 信号,子进程异常退出)
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

管道是有大小的,是多大呢?
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.2 命名管道

管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。命名管道是一种特殊类型的文件。
在这里插入图片描述

创建命名管道的两种方式:
1.命令行mkfifo
这里的管道文件的大小一直是0也就证实了管道文件的内容并没有写入到磁盘内,一直存在于缓冲区里,是内存文件。
在这里插入图片描述

2.系统调用函数mkfifo
在这里插入图片描述

mkfifo() makes a FIFO special file with name pathname. Mode specifies the FIFO’s permissions. It is modified by the process’s umask in the usual way: the permissions of the created file are (mode & ~umask).

A FIFO special file is similar to a pipe, except that it is created in a different way. Instead of being an anonymous communications channel, a FIFO special file is entered into the file system by calling mkfifo().

Once you have created a FIFO special file in this way, any process can open it for reading or writing, in the same way as an ordinary file. However, it has to be open at both ends simultaneously before you can proceed to do any input or output operations on it. Opening a FIFO for reading normally blocks until some other process opens the same FIFO for writing, and vice versa. See fifo(7) for nonblocking handling of FIFO special files.

On success mkfifo() returns 0. In the case of an error, -1 is returned (in which case, errno is set appropriately).

在这里插入图片描述在这里插入图片描述

可以发现,client 和 server 是两个毫不相关的进程。这样的话就可以利用进程间的通信,在一个进程中给另一个进程派发任务。例如在一个进程中派发指令让另一个进程去执行:
在这里插入图片描述

那么我们平时用的 | 是匿名管道还是命名管道呢?可见是匿名管道。
在这里插入图片描述

3.SystemV共享内存

3.1 共享内存基本原理

将物理内存映射到进程的地址空间中,本质就是修改页表,在虚拟地址空间中开辟空间,在这个过程中用的是系统调用接口(完成所谓的开辟空间,建立映射,开辟虚拟地址,返回给用户)。
在这里插入图片描述在这里插入图片描述

3.2 共享内存的建立

共享内存建立的过程:
1.申请共享内存(物理内存已经开辟好了)
2.共享内存挂接到地址空间(建立映射关系)
3.共享内存去关联(修改页表,取消映射关系)
4.释放共享内存(内存归还给系统)

既然两个进程间的通信需要共享内存,而系统中存在大量的进程,所以系统中也存在着大量的共享内存,所以这时候操作系统就要对共享内存进行管理,先描述再组织,为共享内存维护相关的内核数据结构。
既然要让两个进程看到同一份资源,那么就要确保这个共享内存唯一性,如何保证这个共享内存的唯一性呢?在shmget函数中的key参数就可以体现。

3.2.1 共享内存创建相关接口:shmget 和 ftok

1.shmget
在这里插入图片描述

参数key:IPC资源 (share memory) 的唯一性
参数shmflg选项:
IPC_CREAT:如果共享内存存在,直接返回该共享内存,如果不存在再创建(一定会获得一个共享内存,调用成功的情况下无法确认是否是新的共享内存)。
IPC_EXCL:单独使用无意义。IPC_CREAT | IPC_EXCL 如果内存不存在,则创建,如果存在,出错返回(调用成功,一定会获得一个全新的共享内存)。
参数size:向页大小对齐,为一页大小4096bytes的整数倍,如果传的不是4096的整数倍,如4097,会申请两个页的大小,但是能用的只有4097个字节。
返回值叫做共享内存句柄,用于在用户层标识共享内存,也可以使用接口访问该共享内存。key是在系统层面的标识符。

在这里插入图片描述

2.ftok
在这里插入图片描述在这里插入图片描述

虽然进程已经退出,但是曾经创建的共享内存还存在,shm的生命周期是随内核的,进程不主动删除或者用命令删除,共享内存一直存在,直到关机重启。IPC资源一定是系统提供并维护的。

3.2.2 共享内存销毁相关命令与接口:shmctl( , IPC_RMID, ) 与 ipcrm -m

1.shmctl(, IPC_RMID, )
在这里插入图片描述

在这里插入图片描述在这里插入图片描述

2.ipcrm -m
在这里插入图片描述

3.2.3 共享内存关联与去关联:shmat 和 shmdt

在这里插入图片描述在这里插入图片描述

在这里插入图片描述在这里插入图片描述

测试效果:
在这里插入图片描述

3.2.4 建立通信

开始建立server 与 client 的通信:
在这里可以看出,为什么要在 ftok 里要有两个参数了,当这两个参数在头文件中被包含了,两个进程都可以通过这两个参数获得同一个 key,从而获得同一个 shmid ,这时两个进程就看到了同一份资源,这是根本。
在这里插入图片描述

开始通信:
在这里插入图片描述
在这里插入图片描述

3.3 共享内存的特点与数据结构

共享内存的特点:
1.在读写共享内存的时候,没有使用系统调用接口。是速度最快的通信方式,因为拷贝次数减少了。
2.不提供任何的保护机制(互斥与同步)。

共享内存数据结构:
key : 在内核层面上,区分共享内存的唯一方式
shmid : 在用户层面上,在进程内部,区分某一个IPC资源
在这里插入图片描述

3.4 消息队列

3.5 信号量

进程间通信的本质就是共享资源,这样解决了通信的问题但是也引入了新的问题。被多个进程共享的资源叫做临界资源,如果没有保护机制,就会造成临界资源数据不一致的问题,所以要有保护机制:加锁(同步与互斥)。但是加锁也会带来新的问题,就是效率的降低,所以得引入其他的机制。

多个进程有多条代码,其中访问临界资源的代码叫做临界区,临界区才是要保护的区域,将代码保护起来,这里就引入了信号量。信号量分为二元信号量多元信号量。信号量本质是一个计数器,用来描述临界资源中资源的数目,在二元信号量中,将临界资源看作一个,达到了互斥的效果。
要申请一个资源,必须要进程直接占有这个资源吗?只要申请信号量成功了,就一定有你的资源。整体来看,一个进程访问共享内存,要么这个共享内存没有被访问,要么这块内容已经被访问完毕了(没有中间状态,只有两态),这就是原子性。
信号量本身也是临界资源,PV操作也要有原子性。
在这里插入图片描述

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RanieMiss

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值