目录
共享内存的原理
共享内存的创建
代码实现创建
共享内存的管理指令
我们今天来学习共享内存!!!
共享内存的原理
两个进程同时使用内存中开辟的共享空间进行通信就是建立并使用共享内存进行进程间的通信。System V 共享内存(Shared Memory)允许不同的进程共享一块内存区域。
那它和命名管道有什么区别呢?
所以共享内存不是文件,这个本质区别!!!
接下来我们讲一下共享内存通信的原理:
两个进程通过各自的mm_struct和页表的映射,映射到各自的物理内存上的一块空间形成物理地址(空间的头地址),那两个进程要进行通信就需要同一个共享内存,所以为了实现通信,内存创建一个共享内存(本质不是由操作系统创建,应该是其中一个进程要求然后自己先创建了),我们暂且任务需要OS创建,这个共同的内存通过页表映射到各自进程的虚表的共享区然后这个过程叫挂接到进程的地址空间中,解除共享状态就是去关联。管道如果只读打开的话会卡在创建管道的之前,而共享内存就没有这个问题。
共享内存的创建
使用shmget函数创建共享内存空间。
作为一个系统函数,三个参数,我们先看返回值以及参数说明吧。
size就是设置共享内存的字节大小,shmfig就是创建这个共享内存的创建标志,这个参数只能有以下三种传递方式:
前面这个key,类型就是这样的,这个是用来标志共享内存的唯一标识符,这个一般是用户自己传进去的,也就是进程创建时自己提供,为什么不能操作系统直接提供呢?
共享内存在任何时刻可以在OS内部存在多个,那么个的共享内存操作系统肯定是需要管理的,怎么管理呢先描述再组织,又是这句话,共享内存=共享内存的内核数据结构+内存块,操作系统将这个struct shm加载到操作系统中时,如果自行创建key那进程A进来创建共享内存得到操作系统的key,进程B要与之通信,可是如果此时进程B还没有创建怎么办,A和B毫不相干,这样进程B进入操作系统就不知道key值不知道要访问哪个共享内存了,如果这个是由jincA创建的key那到时候A只需要将其放在AB的共享区(都能看到的区域)这样B就可以看到了!!!这个A创建的过程本质还是让不同的进程看到同一份资源。
这个key值是随便创建的吗,A进程随便给key不会和其他进程冲突吗,会,所以操作系统认为你既然自己创建不好,我就自己提供一个函数创建一个key给你用。
ftok
是一个 System V IPC 中的函数,用于根据指定的路径名和一个项目ID(通常是一个字符)生成一个唯一的键值(key_t
)。这个键值通常用于共享内存、消息队列或信号量等 IPC 资源的标识符。
ftok根据你传入的公共路径(会被解析成数字)和公共项目的ID进行联合根据某种算法得出唯一key值
这种算法得出的key值原则上还是会有极小概率的冲突,可以忽略不计的,就是比自己创建准。
代码实现创建
依旧是创建两个进程和之前一样,然后都可访问公共头文件comm.hpp,makefile里面就是能同时编译两个.cc,和上一篇博客里面写的一样的。
我们得先创建一个key,所以先使用ftok进行创建,默认server.cc里面进行创建,然后知道了公共路径和id就知道了key,这个公共路径是随便起的自己系统里有这个路径就行,id也是随便起的,我们这里使用16进制数,所以将公共路径和id写入公共.hpp让两个进程都可以看到。
我们这个key值非常大,并且由于路径和id一致所以没次运行都是一样的。
那光有key不行呀,我们是不是还要判断创建key成功了没有,并且有了key之后就可以创建shm了对吧,创建共享内存的地点还是在server,不过创建多大字节的空间就需要公共出来。
由于每次创建的都是最新的,已经存在了共享内存就会报错!!!
这个我们的使用代码进行创建的流程就完了,至于实现通信就是下节课的事了,由上面可以看到第一次运行了之后由于server进程结束就退出了,但是第二次运行时那个共享空间还在内核里面,所以共享内存的生命周期是不随单个进程的,而是随着操作系统的生命周期,所以要销毁管道只能是将两个互通的进程都关掉然后操作系统认为这个内存没有意义了,就自己释放了,这个过程属于用户让操作系统释放,方法2是让OS自己重启,make clean是代码层面的释放,我们还可以使用指令创建,释放。
共享内存的管理指令
ipcs指令可以看到各自通信模式,有消息队列,共享内存和管道,由于我们之前没有释放共享内存所以就显示有一个,
nattch是该共享内存的附加进程数,就是有多少个进程在进行通信,当 nattch == 0
时,意味着当前没有任何进程使用该共享内存,但内存段仍然存在,直到 shmctl(IPC_RMID)
删除它。因为我们只是创建还没有使用所以是0。
ipcs -m选项直接可以看到共享进程。
ipcrm
是 Linux 上的 IPC(进程间通信)资源管理命令,用于删除共享内存、信号量或消息队列。其中,ipcrm -m
用于删除共享内存段。
可以看到要删除一个共享内存需要-m之后再指定上可以标志该内存段的ID,就是删除这个指定的内存段,为什么不使用key而使用shmid(shmget的返回值)。
还有一个原因是key也是有可能重复的对吧,即使使用了那种算法。
这个shmid有点像文件描述符对吧,shmid可以指明一个独一的共享内存。
如果我们一直创建共享内存然后删掉,再创建,发现这个shmid从0开始自增到n,n可以等于2.。。。,所以这个shmid就是数组的下标,我们可以认为shmid数组表的下标存放了shmid值,然后内容指向了共享内存,这个结构和文件描述符表是大致一样的,但是文件的是从3开始创建的,所以共享内存这部分和它又有所不同(后面会细讲)。
接着讲指令创建:
shmctl()
是 Linux System V 共享内存的管理函数,用于获取共享内存信息、修改权限、删除共享内存等。
这个函数的功能有点多,可以用于创建共享内存等等。
就是将指定唯一的shmid的共享内存创建/修改/删除,到底执行哪一种就取决于cmd的传入,这个cmd是像flag有特定传入限制的(有提供不需要自己写),然后如果是创建/修改就需要使用buf,删除填个nullptr就可以了,不关心buf指向的共享内存的信息。
buf指针指向struct shmid_ds这个装有共享内存信息的结构体,用于修改/创建。我就说共享内存空间有类似于PCB的内核数据结构吧。
我们进行函数级的删除,再使用ipcs -m进行追踪。
删除成功!!!
咦不对呀,这个shmctl不是指令呀,怎么写在这里,我后学的这个还不行吗???