今天我们来讲进程间的通信!
首先我们来看进程间通信的目的!

总的来说就是为了实现进程间的协同!
然后首先我们来了解一个前提!
![]()
只要能看到同一份资源,两个进程就完成了进程间的通信!并且该资源不能由进程两方任意一方拥有!必须由稳定的第三方提供(一般就是操作系统),然后两方申请这个资源然后进行通信!
由此就衍生出了管道,system v,posix通信
其中system v一般用于本地两个进程间的通信。
posix进程间通信则是用于网络通信的(网络部分我们再讲)

所以我们首先来讲管道

1:匿名管道
我们先讲匿名管道。

首先我们看上图,我们就可以知道在创建子进程的时候,会浅拷贝fd表,这样两个进程就会指向同样的资源!
这样我们就可以创建一个管道文件帮助完成父子进程之间的通信!
并且这个通信不用借助磁盘是一个内存级的通信,所以这个管道就被称为匿名管道!
然后为了我们实现的方便,我们的管道只能进行单向通信!
匿名管道是Linux中一种非常古老的进程间通信方式,实际上是由内核管理内核中的一块缓冲区,本质就是一个内存级的文件,但它没有名字,所以称之为“匿名管道”。这种通信方式主要用于父子进程间的通信,父进程和子进程共同使用这个管道文件来传输数据。
如果想要父子进程互相通信,我们就必须建立两个管道文件然后再进行互相通信!
接下来我们就来看如何创建和使用匿名管道!

首先我们来看一下创建管道的原理



然后我们就开始实操,创建管道和子进程

然后再让父子关闭相应的进程。

然后我们再完善子进程写入的代码以使实验效果更明显。
其中snprintf是一个在C语言中使用的函数,用于格式化字符串并将结果写入指定的缓冲区中。它类似于printf函数,但可以将格式化后的字符串输出到一个指定大小的字符数组中,从而避免缓冲区溢出的问题。snprintf函数的主要作用是将格式化的数据(如数字、字符串等)转换为一个字符串,并将该字符串写入到一个指定的缓冲区中,以便后续处理。因此,snprintf是一种常用的格式化输出函数,可以将指定格式的内容写入指定长度的缓冲区中。

然后完善父进程读取的代码

然后我们编译运行就可以看到效果了

接下来我们来总结一下管道的4种情况和5种特性

其中面向字节流的意思就是写和读没有明确的规定,可以写很多次,很多东西然后一次读出来!
全双工和半双工的意思:
全双工是一种允许数据在两个方向上同时传输的通讯模式,相当于两个单工通信方式的结合。在全双工模式下,通信系统的每一端都设置了发送器和接收器,因此,能控制数据同时在两个方向上传送。这就像我们平时打电话一样,说话的同时也能够听到对方的声音。全双工的好处在于迟延小,速度快,它要求发送线和接受线各自独立,因此可以在微处理器与外围设备之间实现数据的瞬时同步传输。
而半双工则是指在通信过程中的任何时候,信息可以从A传输到B,从B传输到A,但只能有一个方向的传输,不能同时传输。也就是说,一个时间段内只能有一个动作发生。例如,一条窄窄的马路,同时只能有一辆车通过。当两辆车对开时,只能一辆先过,等到头儿后另一辆再开。早期的对讲机、集线器等设备都是基于半双工的产品。
然后我们大多数的这个缓冲区大小是64kb!

当我们了解了匿名管道的原理以后,我们来看一个匿名管道的实例

我们以前的|就是一个管道!(之所以可以使用就是因为这些进程的父进程都是bash,所以多个进程之前就可以通过bash进行间接通信了)
更具体来说就是先和bash建立管道然后再把不同进程之间的管道连接(使用dup2进行fd的替换)在一起就可以了!
接下来我们来讲命名管道!
通过我们对匿名管道的学习,我们就会发现匿名管道只能在有血缘关系之间的进程进行通信,如果没有血缘关系就不行了。
而命名管道就是来解决这个问题的。
接下来我们就开始学习命名管道!
要想使用,我们必须先创建一个管道文件!
mkfifo指令就可以创建一个管道文件!

![]()

然后通过实现就发现可以通信了!!!

但是我们发现这个管道大小一直是0.

所以我们的管道文件只需要是一个磁盘级的符号,不用向磁盘刷新,也没有必要刷新!

命名管道也是通过文件缓冲区建立的通信的(管道文件与其他文件形式一样,只不过不向磁盘刷新数据而已),而匿名管道是通过血缘关系确定同一份资源,而命名管道是通过路径和文件名确定资源的!
接下来我们就开始学习命名管道的使用

首先创建管道文件,然后打开文件进行读写操作!


然后就可以正常使用了,与匿名管道最大的不同就是其可以实现毫无关系的进程间的通信!
接下来我们就开始讲system v版本下面的共享内存!

system v是进程间通信的标准!
首先我们来了解共享内存的原理!

本质就是将同一份内存映射到两个进程,两个进程都可以访问就完成实现了看到同一份资源!
由于可能会有多个共享内存,同理也会有相应的数据结构进行管理!
那么接下来的问题就是

那么答案就是

接下来我们先来了解一下接口,分别是获取共享内存,挂起共享内存,取消挂起共享内存,删除共享内存!

shmget返回就是地址空间,我们之后就要用这个地址空间!暂时不多讲,等下用了就懂了!
然后我们接下来就讲一下这里面的参数key!!!
这里的key就相当于密钥,具有独特性!这样两方使用同一个key就可以实现访问同一份内存了!
并且为了防止我们取的key与系统冲突,所以我们有专门生成key的函数与方式!

只要传进去一个字符串(一般就是文件路径)和id就可以生成一个独特的key了,这样只要双方用同一个地址和id就可以生成相同的key了,就可以访问同一份资源了!


移除的第二个参数就是执行的命令,第三个参数可以用来获取共享内存的信息!
![]()
然后我们要注意几个点
1:
哪怕进程退出了,也会存在!所以必须要用代码删除或者指令删除!!!
2.
3:
4:通过ipcs -m查看的信息中nattch就是有多少个进程挂接到地址空间!
5:![]()
然后我们总结一下共享内存的特点!

接下来我们来研究一下这个数据结构体!
然后我们发现ds里面还套了一个结构体perm
现在可以打印出来看看!基本可以知道表示的是什么。但是具体要讲什么,我们之后再揭晓!!

现在我们先讲消息队列!


![]()

任然是创建和销毁。不同的是,如果想要发送消息就不能和共享内存那么简单了。而要使用msgsnd去发送,依次为队列Id,数据的地址,数据大小,标志位!
这个数据必须使用msgbuf结构体的格式发送,通过特定格式进行发送!

然后就是收消息,则需要指定缓冲区和写明缓冲区的大小,也就差不多了!
而msgtyp则是用来区分发送双方的身份,避免自己读取了自己发送的信息!

然后我们再次查看消息队列的属性!

然后我们就发现也有ipc_perm这个结构体!!!
由此我们必须先浅谈一下另外一个内容——信号量!

首先我们要知道什么叫进程互斥

所以信号量其实就是一种保护资源的一种策略和手段!

同样我们开始了解信号量的一些接口!


下面这个是对信号量进行+和-的操作。

至于使用我们暂且按下不表,我们先来看为什么要产生信号量



所以信号量是用来解决互斥和同步的手段!
比如我们看电影的时候,买票的本质就是对资源的预定!而信号量就是用对票资源管理相似的手段去实现对公共资源的管理。

我们的信号量就可以记录公告资源的预定情况,就可以实现公告资源的合理分配!


每一次要访问临界代码就要先去访问信号量,然后再才能访问代码,就可以实现对临界资源的管控了。

所以,如果一个信号量只能由一个进程访问,那么就成为了互斥锁,就完成了互斥功能!


由于信号量也是公告资源,那么信号量也需要被保护,而保护方法就是原子性!


实现方法就是通过上面这个结构体实现的!
至此信号量我们暂时告一段落,等多线程的时候再去进一步深入!
然后我们再看一下信号量的管理结构。
我们现在主要看到的是

信号量的管理结构中也有ipc_perm!!!
由此我们真正想谈的是,也就是内核如何管理IPC资源的!



所以结构的开头都是Ipc_perm,这样我们就可以用一个ipc_perm指针数组保存管理任意类型的结构体(指的是共享内存,消息队列,信号量这些东西),然后要访问其中的数据时再强转成对应的类型就完成了我们的数据管理!!! (这样就实现了类似多态中基类指向不同的子类,只要管理好基类就可以管理好子类了)