文章目录
- 序:在上一章对用来进行进程间通信的匿名管道进行了详细的说明,知道了进程间通信的本质就是不同进程间看到同一份资源,还知道了管道的系统调用和管道的使用,通过总结四种情况和五中特征的方式了解深入了解了管道的使用情况!!!而本章将先对上一章的管道出发,来引入一个新的概念—命名管道,我们会知道命名管道区别于匿名管道有什么特殊,又是怎么使用的,其次本章还会从共享内存的角度来讲述进程间通信,我们会知道什么是共享内存,共享内存的原理是是什么?他的系统调用接口又是什么,以及怎么访问和使用共享内存!!!
1.命名管道
1.1 命名管道是什么
在上一章中,我们对匿名管道进行了深入的学习,知道了匿名管道是基于有“血缘关系”的进程间进行进程间通信的一种方式!!!而本章的命名管道则不同,我们要探讨的是,如何让两个不相干的进程进行进程间通信!!!所以命名管道就是一种能让两个毫不相干的进程进行进程间通信的一个管道文件!!!
1.2 如何创建命名管道
mkfifo +[文件名]
如图:
该文件的权限前一位是p,所以这是一个管道文件!!!
1.3 如何通过命名管道实现进程间通信
我在云服务器中开了两个毫不相干的进程,我们将左边的进程命名为进程1,右边的进程命名为进程2,如图:
当进程1向该管道文件输入“hello linux”的时候,进程1出现了阻塞现象,此时我们在进程2中查看该管道文件的大小时发现竟然是0,接着,我们让进程1继续阻塞,进程2将该管道文件中的数据打印出来!!!
如图:
我们发现,进程2将“hello linux”打印出来了,此时,进程1也退出了阻塞状态!!!
从上面两张图分析,我们可以知道,该管道文件的大小不会变化,即该文件缓冲区中的内容不会刷新到磁盘当中,所以,管道文件是一个内存级文件和匿名文件一样。
问题一:如果让两个不同的进程,打开同一个文件的时候,在内核中,OS会打开几个文件?(进程通信的前提是:先让不同的进程看到同一份资源)
1个,只不过是两个不同的struct file结构体指向同一个文件文件缓冲区
问题二:这两个进程打开的是同一个文件吗?为什么?
因为同路径下同一个文件名 = 路径+文件名(这个文件是唯一的,因为路径天然具有唯一性,同一路径下的文件不能同名!!!)
问题三:为什么要用一个命名管道文件,而不是一个普通文件来实现进程间通信?
我们不想让该文件内的内容刷新到磁盘内保存,只需要一个不会将内容刷新到磁盘中的一个内存级文件就行!!!(p管道文件)
怎么在代码中实现呢?
如图:
链接如下:
使用命名管道实现两个毫不相干的进程的进程间通信
2. 共享内存
System V IPC{
System V —消息队列
System V —共享内存
System V —信号量
}
本章将围绕着system V IPC中的共享内存来讲述
2.1 共享内存的原理
进程间通信的本质:让不同的进程,看到同一份资源!!!
首先,我们知道,什么是共享内存,共享内存就是一块在物理内存上的空间,只要,我们能够让两个进程都能访问到该共享内存的区域,就能实现进程间通信!!!因为进程间通信的本质就是让不同进程看到同一份资源,这个共享内存就是这样一块共享资源!!!
我们之前学过的动静态库中,我们加载动态库的方式是将动态库加载到进程地址空间中的共享区中!!!这样就能让多个进程访问同一个动态库了!!!所以动态库也叫做共享库,那么同理,共享内存既然有共享两个字,那么他应该也是这样,所以,在物理内存上的共享内存区域通过进程的页表映射到进程地址空间的共享区内,然后将该映射的首地址返回给上层,让进程能够访问到该共享内存!!!
所以,我们要通过共享内存进行进程间通信总共有两步:
第一步:申请共享内存
第二步:挂接进程的进程地址空间如果要释放共享内存:就应该先去关联,再释放共享内存。
问题一:上面的操作都是进程直接做的吗?
不是,是直接由操作系统来做的!!!(需求方->系统调用->执行方)
问题二:如何管理好这么多个共享内存?
先描述,在组织!!!通过内核结构体描述共享内存
2.2 共享内存的系统接口与接口的调用
第二个参数是创建的共享内存的大小(问题一),单位是字节。
第三个参数,类似于与open中的位图参数,表示,如何创建,如何获取概念
例子:
IPC_CREAT(单独使用):如果你申请的共享内存不存在,就创建,反之,则调用已经创建
好的共享内存,并返回。
IPC_CREAT | IPC_EXCL(混起来用):如果你申请的共享内存不存在,就创建,如果存在了就返回错误!!!确保,一但我们成功申请了一个共享内存,那么这个共享内存一定是新的!!!(其中IPC_EXCL不单独使用,用了也没用。)
返回值是一个共享内存标识符(该返回值和文件描述符并不能类比)
问题一:我是如何判断申请的共享内存真的存在呢?怎么保证让不同的进程看到同一个共享内存呢?
这就不得不谈到第一个参数key了!
第一个参数key:
1. key是一个数字,这个数字是几,不重要,关键在于它必须在内核中具有唯一性,能够让不同的进程进行唯一性标识
2. 第一个进程可以通过key创建共享内存,第二个之后的进程,只要拿着同一个key就可以和第一个进程看到同一个共享内存了!!!
3. 对于一个已经创建好的共享内存,key在哪?key一定在共享内存的描述对象中
4. 第一次创建的时候,必须有一个key,怎么有的?
5. key—类似—路径—和路径有类似的共性(唯一性!!!)
这个函数是一套算法,通过pathname和proj_id进行了数值计算,形成一个冲突概率很小的key值!!!(其中pathname和proj_id由用户自由指定!!!有冲突的可能性,一但冲突,就要重新改变参数,形成一个新的key。)
这个key值是由用户来约定的,只有先生成一个key,才能去申请一个与该key值相对于的共享内存,这个key值只能由用户来申请,只有用户才知道,谁和谁通过这个key值通信!!!所以key值不能由操作系统来生成!!!因为操作系统根本不知道,哪个进程要和哪个进程进行通信。
查看共享内存:
ipcs -m
删除共享内存:
ipcrm -m shmid的值
查看共享内存的属性:
1.key:创建共享内存时传入的key值
2.shmid:该共享内存的唯一编号,用来找到该共享内存的,但又不同于文件描述符
3.owner:哪个用户创建的这块共享内存
4.bytes:该共享内存的大小
5.perms:权限
6.nattch:代表该共享内存有几个进程关联
需要注意的是:共享内存的大小一般建议是4096的整数倍,如果你要申请4097字节,实际上,操作系统给你的是4096 * 2字节的大小。但你自己只能用4097字节,多出来的不能访问,越界就会报错,所以建议申请4096字节的整数倍。且共享内存的生命周期是随内核的!!!用户不主动关闭,共享内存会一直存在,除非内核重启或者用户主动关闭
key vs shmid(shmget()函数的返回值)
==key :==操作系统内标定的唯一性
==shmid :==只在你的进程内,用来表示资源的唯一性
2.3 共享内存的挂接
问题一:以上只是创建共享内存,我们该如何挂接共享内存呢?
1.shmid:要挂接的共享内存的编号
2.shaddr:要挂接的共享内存的哪一块地址,一般传nullptr,让系统自己决定
3.shmflg:权限
返回值:返回的是挂接的共享内存的虚拟起始地址!!!
一但进程退出,这种关联也会消失。
也不必须等进程退出,才能去关联,用下面的函数shmdt也行。其中传的值就是shmat函数的返回值
要想修改共享内存的属性,可以通过下面一个函数来修改
该函数能通过shmid_ds来修改共享内存的属性,其中shmid_ds就是类似于管理共享内存的数据结构。(是内核管理共享内存的数据结构的子集)其中cmd就是代表我们要做出什么样的操作。
共享内存的管理结构体:
2.4 共享内存的特性
a. 共享内存没有同步互斥之类的保护机制
b. 共享内存是所有的进程间通信中,速度最快的!(为什么他的速度最快,因为他的拷贝次数少)
c. 共享内存内部的数据,由用户自己维护!
总结:
而本章在上一章的基础上,引出了命名管道的概念,我们知道了命名管道是什么,又是怎么使用的,其次本章还从共享内存的角度来讲述进程间通信,我们知道了什么是共享内存,共享内存的原理,共享的系统调用接口,怎么访问和使用共享内存!!!以及共享内存的特性。