当前位置: 首页 > news >正文
江阴公司企业网站建设,国外做饮用来源的网站,辽宁建设工程信息网企业人员调动,国内工业设计网站#x1f431;作者#xff1a;一只大喵咪1201 #x1f431;专栏#xff1a;《Linux学习》 #x1f525;格言#xff1a;你只管努力#xff0c;剩下的交给时间#xff01; 进程间通信——共享内存 | 消息队列 | 信号量#x1f3c0;共享内存⚽系统调用shmgetkey值⚽系统… 作者一只大喵咪1201 专栏《Linux学习》 格言你只管努力剩下的交给时间 进程间通信——共享内存 | 消息队列 | 信号量共享内存⚽系统调用shmgetkey值⚽系统调用shmctl⚽系统调用shmat和shmdt⚽共享内存的进程间通信特性⚽共享内存的内核数据结构消息队列(了解)⚽系统调用信号量(了解)总结system V是一种进程间通信策略它包括共享内存消息队列以及信号量。
共享内存
共享内存区是最快的IPC(进程间通信)形式。一旦这样的内存映射到共享它的进程的地址空间这些进程间数据传递不再涉及到内核换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。 共享内存也是由操作系统维护的共享资源。 两个进程的PCB各自维护着一个进程地址空间。当两个进程要进行通信时 操作系统在内存中开辟一个内存块。 通过两个进程的页表将内存中的内存块映射到两个进程的进程地址空间中。 此时两个进程便建立了连接。 进行通信时两个进程只需要访问自己的进程地址空间即可操作系统会通过页表访问内存中的内存块。
所以说共享内存就是让不同的进程看到同一块内存块。
在维持通信关系中还涉及到几个概念 挂接将内存中创建好的内存块映射到进程的地址空间中。去关联不想通信时取消进程和内存的映射关系。 注意 去关联后共享内存仍然存在只是和去关联的进程没有了映射关系。
⚽系统调用shmget 该系统调用接口就是用来在内存中创建共享内存的。它们的参数意义非凡下面本喵来详细解释一下。
key值
关于共享内存首先需要理解几件事情 共享内存是专门设计的ipc方式是用来进行进程间通信的。 所有想通信的进程都可以用共享内存的方式来通信。 所以操作系统中注定不止一块共享内存。
共享内存多了就需要有一个标识让要通信的进程找到正确的共享内存。
key值就是共享内存的标识让想要通信的进程双方看到同一块公共资源。
系统中既然存在很多个共享内存操作系统势必要将它们管理起来管理也是使用先描述再组织的方式。
管理共享内存并不是在管理内存块本身而是在管理共享内存对应的结构体
struct shm
{key_t key;size_t size;//….
}结构体类似与上面代码包含许多共享内存的属性最重要的是结构体中有key值。 每创建一个共享内存就会创建一个结构体对象并且赋一个不同的key值。所以这个key值就代表着一块唯一的共享内存。 问题又来了怎么保证这个key值是唯一的呢
从shmget函数的声明中可以看到这个key值是我们传给操作系统的也就是用我们传的key值来标定共享内存。
函数ftok()
这就需要用到另一个函数ftok()来生成一个独一无二的key值。 pathname文件路径名可以随意写一般我们都写成当前路径.。proj_id项目ID同样可以自定义但是不能为0。返回值独一无二的key值。 该函数会根据我们传的路径名和项目id值生成一个key值具体实现是通过一些算法实现的我们不需要在意只需要得到key值就行。
所以在开辟共享内存之前必须先使用ftok函数来生成一个独一无二的key值这样才能保证我们内存块的标识是唯一的。
当两个进程通过key值和共享内存挂接起来后就可以进行通信了。
结论key值就是用来标识共享内存的唯一性的。
size
size是用来指定开辟的共享内存是多大的以字节为单位。 一般指定的大小是4KB的整数倍。 也可以是任意值。
操作系统在开辟共享内存的时候是以4KB为单位的。每次开辟的共享内存最小也是4KB的。 假设我们指定4097字节大小的共享内存但是在内存中实际开辟的共享内存是2*4KB的。但是在使用的时候只能使用4097字节的空间剩下的空间用户无法使用操作系统也不会用就浪费掉了。 所以即使用不了那么大的空间我们也要指定4KB的整数倍。
shmflg
这是一个标志位和之前使用的open用法相似也是一个int类型的数据根据比特位不同用法也不同。
常用的两个选项 IPC_CREAT创建共享内存如果不存在创建新的如果存在获取相关信息。IPC_EXCL无法单独使用必须与其他标志组合使用。 IPC_CREAT | IPC_EXCL创建共享内存如果不存在则创建如果存在错误返回。 IPC_CREAT | IPC_EXCL是专门给用户使用的就是为了保证创建的共享内存是一块新的内存块。
返回值 创建成功返回共享内存的标识符注意不是key值。创建失败返回-1。 来看看返回的值是什么样子 shmid也是连续的小整数它和文件描述符fd一样也是让用户使用的。但是它和文件描述符代表的意义又不一样。 又存在一个问题为什么返回的不是key值而是shmid呢key值也是唯一的啊。
在设计上是可以的都是唯一的数字用户在语言层和在系统使用的标识相同是没有问题的。 如果写好了一份代码代码中使用的是key值。 当系统的底层发生了变化key值也会变化。 但是代码中的key值没有变化此时继续使用原理的代码就会出错。
所以说用户层使用shmid而不是key值是为了让用户层和系统层解耦。
结论shmid是供用户使用的共享内存标识符。
shmget系统调用就是用来让通信双方获取同一块共享内存的。
⚽系统调用shmctl
刚刚我们使用shmget时创建了五个共享内存 此时创建的5个共享内存就显示出来了。
此时进程早已经结束了但是共享内存还是存在没有随进程的结束而消失。
所以说共享内存的生命周期随内核不随进程。
如果不想要这几个共享内存了怎么删除呢同样有指令 指令ipcrm -m shimd功能删除指定shimd标识的共享内存。 此时所有的共享内存就被删除了但是需要我们手动的一个个去删除。
由于指令也是shell上运行的进程也是属于用户层所以操作共享内存时使用的时shmid而不是key值。
用命令行的形式未免也太麻烦了所以有系统调用shmctl也可以用来删除共享内存而且是自动的。 shmid获取共享内存后返回的标识符。cmd指定控制共享内存的方式。buf描述共享内存的数据结构指针。 cmdIPC_RMID
删除是最常用的选项。 key值和shmid也打印了说明共享内存创建成功了但是使用ipcs查看时发现什么都没有证明shmctl将创建的共享内存删除了。
cmdIPC_STAT
用来查看共享内存内核数据结构中的属性不常用。
上图所示就是共享内存的数据结构struct_shmid_ds也就是用来描述共享内存的数据结构这只是其中的一部分。
该结构体的第一个成员变量是struct ipc_prem shm_prem具体内容如红色箭头所指。可以看到key值就在这里共享内存就是通过它来标识唯一性的。
当cmd是IPC_STAT时我们就可以获取到共享内存的属性 此时共享内存内核数据结构中的属性信息就被我们看到了。
cmd可以传的值有很多分别对应这不同的操作但是最常用的还是删除。
⚽系统调用shmat和shmdt
shmat
shmat是让进程和共享内存挂接 shmid创建共享内存后返回的标识符。shmaddr指定共享内存映射到进程地址空间中的地址一般设置成NULL让系统自动来设置。shmflg不用管它是啥直接给0。返回值共享内存映射到进程地址空间中的地址。不成功返回-1但是是void*类型的。 让该进程挂接共享内存挂接成功打印映射到进程地址空间中的起始地址。 在运行的时候发现报错了说我们没有权限再查看共享内存发现确实是创建了但是共享内存的权限是0也就是我们谁都不能访问。
解决办法就让给共享内存开发相应的权限 在创建共享内存的时候让其开发对拥有者的读写权限。将0600和IPC_CREAT已经IPC_EXCL或在一起。 此时便挂接成功了并且成功打印出了共享内存映射在进程地址空间中的起始地址。
shmdt
shmdt是让进程和共享内存去关联。 shmaddr要去关联的共享内存映射在进程地址空间中的起始地址。返回值成功返回0不成功返回-1。 nattch值 在使用ipcs查看共享内存的时候有一栏是nattch。 在进程和共享内存挂接后查看共享内存信息发现这个共享内存的nattch变成了1。 nattch表示和这块共享内存挂接的进程数量。 在和共享内存挂接以后进行10s的延时时间到进行去关联。 可以看到在计时到之前nattch的值是1等计时到了nattch的值变成了0此时就将该进程和共享内存去关联了。
注意 去关联后共享内存还在只是和进程没有联系了。
共享内存方式的进程间通信用到的系统调用为shmgetshmctlshmatshmdt四个。
⚽共享内存的进程间通信
使用共享内存的方式实现进程server和client之间的通信 server负责创建共享内存删除共享内存 server从共享内存中读数据。 client向共享内存中写数据。
shmgetshmctlshmatshmdt四个系统调用通信双方都会使用而且会对返回值进行严格判断所以我们将这些共用的代码放在一个头文件中。
comm.h: 使用ftok生成key值如果成功的话返回key值失败的话之间退出进程。 server方需要负责维护共享内存所以就由它来创建将创建的具体过程封装在一个函数里只需要调用创建函数传入一个key值就可以创建。
创建成功返回共享内存标识符shmid失败的话退出进程。 client方只需获取共享内存的shmid即可同样将具体实现封装只需要调用获取函数传入一个key值就可以获取成功返回共享内存标识符失败退出进程。 挂接共享内存时同样将具体实现封装起来只需要调用挂接函数传一个shmid即可挂接成功返回映射后的虚拟地址失败则退出进程。 (long long)memp -1L解释说明memp是一个void类型的指针即使失败返回的-1也是void的所以在判断时要将其强转为整形。Linux是64位的机器所以指针的大小是8个字节所以为了不发生数据截断需要强转成对应8个字节的long long类型。常量-1默认是int类型的加一个后缀L成为-1L就表示这是一个long long类型的常量。此时相同类型的数据才可以进行判断。 将具体判断细节放在函数中使用的时候只需要调用函数即可不用再判断。若去关联失败则进程退出。 调用shmctl的具体传参细节在函数中实现删除的时候只需要调用删除函数即可删除失败则退出进程。
comm.h头文件中的各个函数都是为了通信双方更方便通信而设计的。
server.cpp和client.cpp: 通信双方的通信框架都是按照 获取key值 创建或获取共享内存(shmget) 和共享内存挂接 进行通信 和共享内存去关联 维护方伤处共享内存
双方数据传送 发送方一秒发送一次接收方一秒接收一次。 必须先执行server因为server负责维护共享内存只有共享内存创建了才能进行通信。在client没有发数据之前server就开始读取数据了但此时什么都没有。当client开始发送时server才能读取到数据。 特性 通信双方获取的key值相同 因通信双方是通过这个key值在内存中找到同一块共享内存的key值代表着唯一性所以必须相同 标识符shmid相同 这个标识符和文件描述符不一样具体是什么原因以后本喵再讲现在只需要知道双方获取的shmid是相同的。 映射的虚拟地址不同 通信双方根据通过页表将共享内存映射到各自的进程地址空间时除非指定地址否则不同进程情况不同内存使用也不同所以得到的映射虚拟地址也不同。 可以看到使用共享内存通信时不像管道那样通信双方是通过访问管道这个公共资源来实现数据交换的。
而是双方各自访问各自的虚拟地址就可以。 操作系统会通过各自的页表与共享内存建立联系。所以双方各自访问自己进程地址空间中映射的起始地址时就相当于访问到了同一块共享内存。 这样来看双方各自管自己的就行不用考虑对方所以这种通信方式比较简单。
共享内存的优势
考虑一个问题通信双方一方写入一方读取采用管道和共享内存分别发生了几次数据拷贝。
管道 一共需要四次。 键盘-写入端进程地址空间-管道-写出端进程地址空间-显示器 共享内存 一共需要两次。 键盘-共享内存(写入写出端进程地址空间)-显示器 由于共享内存在双方各自的进程地址空间中都有映射相当于三者是一个整体。 *如果考虑用户层缓冲区的话两种方式各自再增加两次拷贝。 当通信的数据量非常大时共享内存的方式大大减少了拷贝次数。 所以说共享内存是所有进程间通信速度最快的。
共享内存的劣势 在发送方有读取有写入每隔1秒发生一次。 在写入方有写入有读取每隔3秒发生一次。 可以看到结果非常混乱两个进程想读就读想写就写这样就会导致数据混乱通信的数据不准确。因为双方并不知道对方的存在。 原因是共享内存通信方式没有同步和互斥机制。 因为没有对公共资源的保护机制所以就会导致通信混乱。
如果非要使用共享内存的方式可以在通信双方之间再加入管道利用管道的互斥机制来实现共享内存的互斥。有兴趣的小伙伴可以去试试。
共享内存的特征 共享内存的生命周期随内核 共享内存是所有进程间通信速度最快的的方式 共享内存没有同步互斥机制不对公共资源进行保护
⚽共享内存的内核数据结构
我们知道操作系统中不仅有一块共享内存它们是通过key值来进行唯一性标识的。
操作系统也要管理这些共享内存上面本喵讲过采用的是先描述后组织的方式。 描述就是使用上图所示的结构体对象来描述的该结构体中包含了共享内存的所有属性。
将这些描述共享内存的结构体对象组织成某一种数据结构例如链表这样操作系统就将共享内存管理起来了。 数据结构中存放的是描述共享内存结构体对象的指针。 这里这个指针有点特殊 存放的并不是结构体struct shmid_ds对象的指针。 而是它的第一个成员变量struct ipc_perm shm_perm的指针。
由于结构体的地址和结构体中第一个成员的地址在数值上相等所以在访问结构体的时候将第一个成员的指针强转成结构体类型的指针就可以。
消息队列(了解)
消息队列的公共资源是链表结构。 通信双方不会和消息队列进行挂接而是像管道一样访问内存中的消息队列。 消息队列由操作系统维护但是由通信的某一方创建和删除 通信双方都需要获取到消息队列和共享内存一样。
当发送方有数据发送时将数据先打包成一个节点然后尾插到内核中的消息队列中去。
当接收方接收数据时从队列头部开始去找所需要的节点然后进行解包得到数据。 消息队列和普通队列不一样不是严格按照先进先出的规则。读取方可以跳过队头寻找自己需要的数据。但是相同的数据必须先读取靠近队头的。 如上图中当读取方需要的是香蕉但是队头是苹果此时就可以跳过苹果读取香蕉并且靠近队头的香蕉先被读取。
⚽系统调用
几乎和共享内存一样本喵就不详细介绍了。
msgget() key和共享内存一样也需要生成是消息队列唯一性的标识符。msgflg和共享内存一样可以是IPC_CREAT或者IPC_EXCL或者是二者的组合。返回值返回消息队列的标识符供用户层使用。 msgctl() 参数和共享内存的shmctl一样。 这是消息队列属性描述的结构体。 第一个成员变量的类型是struct ipc_perm变量名是msg_perm结构类型和共享内存的一样。 只要是采用system V的通信策略描述共享资源的结构体都是这个结构第一个变量类型都是struct ipc_perm。
msgsnd() msgid消息队列标识符msgp要发送数据所在的数组元素类型是 struct msgbufmsgsz要发生的数据大小以字节为单位msgflg创建标记如果使用IPC_NOWAIT失败就会立即返回。 0阻塞发送 IPCNOWAIT非阻塞发送返回值失败返回-1成功返回0 在发送数据之前需要先将数据进行打包
struct msgbuf
{long mtype; //数据类型必须大于0char mtext[1];//要发送的数据
};long mtype:该值是发送方用来表明数据的所属的。 char mtext[1]:数组大小可以改变和msgsend中的size是一个值。
在发送的时候需要将数据进行打包按照上面的规则。
msgrcv() msgid消息队列标识符msgp接收的数据后要存放的地址msgsz要接收的数据大小msgtype发送方设定的数据类型标识 0读取队列中的第一条消息不在乎当前队列头元素是什么消息类型将他当作普通队列来处理。大于0(约定值)读取队列中类型为msgtyp 的第一条消息。就是读取对列元素中第一个香蕉小于0读取队列中最小类型小于或等于msgtyp 绝对值的第一条消息。 msgflg创建标记如果指定IPC NOWAIT,获取失败会立刻返回 0阻塞接收NOWAIT非阻塞接收 返回值成功返回读取数据的字节数失败返回-1。 信号量(了解)
信号量本质上就是资源计数器能够保证多个进程之间访问临界资源执行临界区代码时。 临界资源多个进程都可以访问到的资源(例如:同一块内存)。临界区访问临界资源时的代码所在区域称之为临界区。 如上图将一大块公共资源划分成了多个小块的公共资源假设一个有100个小块。 这个100就是信号量它用来计数公共资源的个数。
当进程想要访问某一小块资源的时候首先要进行的就是申请信号量一旦申请成功信号量就会减1。 信号量申请成功进行减一的操作称为P操作。
当这个进程访问完这块小资源后需要将资源释放这时候信号量就会加1。 是否资源后信号量进行加一的操作称为V操作。
当信号量为0的时候进程就不能再申请了。
此时再来看所有进程在访问公共资源之前都必须申请信号量而申请信号量的前提是所有进程都能看到同一个信号量所以这个信号量本身就是公共资源。
既然信号量是公共资源就必须在很多进程对它进行PV操作时保证自身的安全。 试想当一个进程正在申请但是这个进程申请的比较慢还有几个其他进程也在申请但是申请的快。后面几个进程把信号量都申请完了当第一个进程申请完成以后发现信号量没了此时就会出错。 当然这是一种极端情况在PV操作的时候可能会因为时序问题导致信号量有中间状态从而导致数据不一致。
所以为了保证信号量的安全性
申请信号量-计数器减1-P操作必须具有原子性。释放信号量-计数器加1-B操作必须具有原子性。 原子性要么不做要做就做完。也就是信号量有互斥机制保护当一个进程在申请信号量的时候其他要申请信号量的进程处于阻塞状态。 信号量的具体使用在后面用到的时候本喵会详细讲解在这里只需要了解这些就可以。
总结
重点介绍了system V通信策略的共享内存方式包括它的原理及应该至于消息队列和信号量仅做了解就行在后面用到的时候本喵会详细讲解。
- 上一篇: 江西做网站找谁网站支付接口怎么做
- 下一篇: 江阴网站建设多少钱seo外链代发
相关文章
-
江西做网站找谁网站支付接口怎么做
江西做网站找谁网站支付接口怎么做
- 技术栈
- 2026年03月21日
-
江西做网站的公司网站开发 浏览器兼容性
江西做网站的公司网站开发 浏览器兼容性
- 技术栈
- 2026年03月21日
-
江西做企业网站的公司wordpress缓存首页不正常
江西做企业网站的公司wordpress缓存首页不正常
- 技术栈
- 2026年03月21日
-
江阴网站建设多少钱seo外链代发
江阴网站建设多少钱seo外链代发
- 技术栈
- 2026年03月21日
-
江阴响应式网站建设做系统去哪个网站好
江阴响应式网站建设做系统去哪个网站好
- 技术栈
- 2026年03月21日
-
江阴做网站的地方织梦栏目页不显示网站描述
江阴做网站的地方织梦栏目页不显示网站描述
- 技术栈
- 2026年03月21日
