进程间通信——共享内存

Posted WoLannnnn

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了进程间通信——共享内存相关的知识,希望对你有一定的参考价值。

system V共享内存

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

共享内存示意图

理解:共享内存是物理内存中开辟给进行通信的进程之间的共享内存,这样一个进程向内存里写了什么,另一个进程就能马上看到,因为它们都把共享内存当成了自己应有的内存,所以能够进行通信,并且减少了拷贝次数。

共享内存数据结构

struct shmid_ds 
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
;

共享内存函数

在讲这些函数前,要先了解一个ftok函数

ftok函数

功能:通过一定的算法获取一个唯一的数字key
这个数字用来给操作系统标识一块共享内存
原型:
key_t ftok(const char *pathname, int proj_id); 
参数:可以自己随意设置
返回值:生成数字成功返回一个唯一的数字,失败了返回-1

shmget函数

功能:用来创建共享内存
原型
int shmget(key_t key, size_t size, int shmflg);
参数
key:这个共享内存段名字
size:共享内存大小
shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
常用选项为IPC_CREAT | IPC_EXCL,意思是如果这个key所代表的共享内存已经开发,则返回错误结果,也就是说,这个选项创建的是全新的共享内存块
返回值:成功返回一个非负整数,即该共享内存段的标识码,这个标识码是给用户使用的,key是给操作系统标识的;失败返回-1

IPC开辟的资源生命周期随内核

命令ipcs -m查看创建的共享内存

其中,nattch表示当前正在该共享内存上通信的进程数

perms表示权限

命令ipcrm -m shmid 表示删除标识码为shmid的共享内存块

创建共享内存时,是以页为单位分配空间的,一页的大小是4096字节,如果我们创建共享内存时size给了4097,操作系统在底层实际上给我们分配了两页内存,只不过我们能使用的还是4097个字节。

shmctl函数

功能:用于控制共享内存
原型
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数
shmid:由shmget返回的共享内存标识码
cmd:将要采取的动作(有三个可取值),通常我们使用的是IPC_RMID,也就是删除共享内存
buf:指向一个保存着共享内存的模式状态和访问权限的数据结构,没有特殊要求就传NULL
返回值:成功返回0;失败返回-1

cmd选项:

命令说明
IPC_STAT把shmid_ds结构中的数据设置为共享内存的当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存的当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

shmat函数

功能:将共享内存段连接到进程地址空间
原型
void *shmat(int shmid, const void *shmaddr, int shmflg);
参数
shmid: 共享内存标识
shmaddr:指定连接的虚拟空间地址,没有特殊要求就传NULL
shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY,如果默认的话就传0
返回值:成功返回一个指针,指向共享内存第一个字节;失败返回-1

​ 说明:

shmaddr为NULL,内核自动选择一个地址
shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -
(shmaddr % SHMLBA)
shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

shmdt函数

功能:将共享内存段与当前进程脱离
原型
int shmdt(const void *shmaddr);
参数
shmaddr: 由shmat所返回的指针
返回值:成功返回0;失败返回-1
注意:将共享内存段与当前进程脱离不等于删除共享内存段

实例代码

实现客户端与服务器端的通信

含有三个部分:client.c server.c command.h

command.h

#ifndef __COMM_H__
#define __COMM_H__ 

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdlib.h>


#define PATHNAME "."
#define PROJ_ID 0x6666

#endif

server.c

#include "command.h"

int main()

  //获取key
  int key = ftok(PATHNAME,PROJ_ID);
  
  //创建共享内存
  int shmid = shmget(key, 4096, IPC_CREAT|IPC_EXCL|0666);

  //将当前进程连接上该共享内存
  char *str = (char*)shmat(shmid, NULL, 0);
  while (1)
  
    printf("client say:# %s\\n", str);
    sleep(1);
    

  //将当前进程与共享内存脱离
  shmdt(str);
  
  //回收共享内存
  shmctl(shmid, IPC_RMID, NULL); 
  return 0;


client.c

#include "command.h"

int main()

  //获取key
  int key = ftok(PATHNAME,PROJ_ID);
  
  //获取shmid
  int shmid = shmget(key, 4096, IPC_CREAT);
  
  //将当前进程连接上该共享内存
  char* str = shmat(shmid, NULL, 0);

  int i = 0;
  while (1)
  
    
    str[i] = 'a' + i;
    ++i;
    str[i] = 0;
    sleep(3);
  
  

  //将当前进程与共享内存脱离
  shmdt(str);
  
  return 0;

结果演示

注意:共享内存没有进行同步与互斥!

以上是关于进程间通信——共享内存的主要内容,如果未能解决你的问题,请参考以下文章

Linux进程IPC浅析[进程间通信SystemV共享内存]

进程间通信(共享内存)

进程间通信

进程间通信

进程间通信

进程间通信——共享内存