【Linux】进程间通信之共享内存

文章目录

共享内存是最快的进程间通信方式,很多追求效率的程序之间进行通信的时候,都会选择共享内存,比如守护进程和被守护的进程

共享内存原理

在物理内存当中开辟一段空间,不同的进程通过页表将物理内存空间映射到自己的进程虚拟地址空间当中,不同的的进程通过操作自己进程虚拟地址空间当中的虚拟地址,来操作共享内存(读或者写)

共享内存相关函数及命令

创建或获取共享内存函数

需要包含的头文件:sys/ipc.h、sys/shm.h

cpp 复制代码
int shmget(key_t key, size_t size, int shmflg);

功能:开辟一段共享内存,用共享内存标识符key去标识这块内存,并返回共享内存的操作句柄

参数:

  • key : 共享内存标识符,相当于共享内存的身份证,唯一标识共享内存区域
  • size : 需要开辟的共享内存大小,单位是字节
  • shmflg
    • IPC_ CREAT:如果获取的共享内存不存在,则创建
    • IPC_ EXCL | IPC_ CREAT:如果获取的共享内存存在,则函数报错;如果获取的共享内存不存在,则创建。

可以按位或上权限,权限是八进制数字

返回值:创建成功返回共享内存操作句柄 ,创建失败返回 -1

共享内存标识符:用于唯一标识某块共享内存,进程可以通过共享内存标识符找到这块共享内存;

共享内存操作句柄:用于操作这块内存的

代码演示:

cpp 复制代码
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/ipc.h>
  4 #include <sys/shm.h>
  5 
  6 #define  KEY 0x23232323
  7 
  8 int main()
  9 {
 10   int shmid = shmget(KEY,1024,IPC_CREAT);
 11 
 12   if(shmid < 0)                                                                                
 13   {
 14     perror("shmget");
 15     return -1;
 16   }
 17 
 18   printf("shmid : %d\n",shmid);
 19 
 20   return 0;
 21 }

查看共享内存命令

ipcs命令用于查看所有进程间通信(Inter-Process Communication,IPC)信息,-m选项是所有信息中过滤出共享内存段的进程间通信信息

ipcs -m

  • key : 共享内存标识符
  • shmid : 共享内存操作句柄
  • owner : 所属用户,谁创建的
  • perms/权限 : 对该共享内存的操作权限
  • bytes/字节 : 共享内存大小
  • nattch : 附加的进程数量
  • status : 共享内存段的状态,dest表示销毁

上面的程序创建了两个共享内存段,虽然程序已经执行结束,但是共享内存段仍然存在,说明共享内存的生命周期的结束不是跟随程序的,而是跟随内核的,也就是跟随操作系统的

删除共享内存命令

ipcrm用于删除进程间通信资源,-m代表删除的是共享内存段

ipcrm -m 共享内存操作句柄

一旦共享内存被删除掉之后,共享内存的在物理内存当中的空间被销毁了。

如果删除共享内存的时候,共享内存附加的进程数量为0,则内核当中描述该共享内存的结构体也被释放了。

如果删除的共享内存的时候,共享内存附加的进程数量不为0,则会将该共享内存的key,变成0x00000000。表示当前共享内存不能被其他进程所附加,共享内存的状态会被设置为destory。附加的进行一旦全部退出之后,该共享内存在内核的结构体会被操作系统释放掉

将共享内存附加到进程的虚拟地址空间

cpp 复制代码
void *shmat(int shmid, const void *shmaddr, int shmflg);

功能:将指定的共享内存段映射到进程虚拟地址的共享区中的某一块中

头文件:sys/types.h、ys/shm.h

参数:

  • shmid : 共享内存操作句柄
  • shmaddr : 映射到共享区的哪一块地址,需要映射到哪一块地址就填哪块地址;如果填入NULL就代表想让操作系统替我们选择一个合适的地址,一般让操作系统自己分配,传递NULL
  • shmflg:以什么权限将共享内存附加到进程当中
    • SHM_RDONLY:规定当前进程对该共享内存有只读权限
    • 0:规定当前进程对该共享内存有读写权限

返回值:成功:返回附加的虚拟地址;失败:-1

代码演示:

cpp 复制代码
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/ipc.h>
  4 #include <sys/shm.h>
  5 
  6 int main()
  7 {
  8   int shmid = shmget(0x34343434,1024,IPC_CREAT | 0664);
  9 
 10   if(shmid < 0)
 11   {
 12     perror("shmget");
 13     return -1;
 14   }
 15   printf("shmid : %d\n",shmid);
 16 
 17   void* lp = shmat(shmid, NULL, 0);
 18   //让操作系统分配共享区,该进程对该共享区的权限为可读可写
 19   printf("%p\n", lp);                                                                          
 20   return 0;
 21 }

执行结果:

将共享内存与进程进行分离

cpp 复制代码
int shmdt(const void *shmaddr);

功能:将进程中附加到共享区的某个共享内存段进行分离开

头文件:sys/types.h、sys/shm.h

参数:shmaddr : shmat函数的返回值,也就是那段共享区

返回值:0 代表分离成功 ; -1 代表分离失败

程序演示:

首先将共享内存附加到本进程中:

cpp 复制代码
  1 #include <stdio.h>                                                                             
  2 #include <unistd.h>    
  3 #include <sys/ipc.h>   
  4 #include <sys/shm.h>   
  5                        
  6 int main()             
  7 {                      
  8   int shmid = shmget(0x34343434,1024,IPC_CREAT | 0664);
  9                        
 10   if(shmid < 0)        
 11   {                    
 12     perror("shmget");  
 13     return -1;         
 14   }                    
 15   printf("shmid : %d\n",shmid);
 16 
 17   void* lp = shmat(shmid, NULL, 0);
 18   //让操作系统分配共享区,该进程对该共享区的权限为可读可写
 19   printf("%p\n", lp);
 20   while(1)
 21   {
 22     sleep(1);
 23   }
 24   return 0;
 25 } 

附加在0x34343434上的进程数量nattch为1

进程分离后的程序及现象:

代码演示:

cpp 复制代码
  1 #include <stdio.h>     
  2 #include <unistd.h>    
  3 #include <sys/ipc.h>   
  4 #include <sys/shm.h>   
  5                        
  6 int main()             
  7 {                      
  8   int shmid = shmget(0x34343434,1024,IPC_CREAT | 0664);
  9                        
 10   if(shmid < 0)        
 11   {                    
 12     perror("shmget");  
 13     return -1;         
 14   }                    
 15   printf("shmid : %d\n",shmid);
 16 
 17   void* lp = shmat(shmid, NULL, 0);
 18 
 19   //将本进程和共享内存分离
 20   int ret = shmdt(lp);
 21   if(ret < 0)
 22     perror("shmdt");
 23   else
 24     printf("shmdt success\n");                                                                 
 25   while(1){
 26     sleep(1);
 27   }
 28   return 0;
 29 }   

执行结果:

操作共享内存

cpp 复制代码
int shmctl(int shmid, int cmd, struct shmid_ds *buf);

功能:操作共享内存的属性

头文件:sys/types.h、sys/shm.h

参数:

  • shmid : 共享内存操作句柄
  • cmd : 告诉shmctl函数需要做什么操作,操作可以填入以下的宏:
含义
IPC_STAT 获取共享内存属性,将指定的共享内存填入buf中
IPC_SET 设置共享内存属性信息,这个时候buf就是输入型参数,内容由程序员组织,传递给shmctl函数,并由他修改共享内存的属性
IPC_RMID 删除共享内存,buf传递NULL
  • buf:共享内存结构体

shmid_ds结构体源码

cpp 复制代码
struct shmid_ds {
    struct ipc_perm shm_perm;    /* Ownership and permissions */
    size_t          shm_segsz;   /* Size of segment (bytes) */
    time_t          shm_atime;   /* Last attach time */
    time_t          shm_dtime;   /* Last detach time */
    time_t          shm_ctime;   /* Last change time */
    pid_t           shm_cpid;    /* PID of creator */
    pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
    shmatt_t        shm_nattch;  /* No. of current attaches */
    ...
};
struct ipc_perm {
    key_t          __key;    /* Key supplied to shmget(2) */
    uid_t          uid;      /* Effective UID of owner */
    gid_t          gid;      /* Effective GID of owner */
    uid_t          cuid;     /* Effective UID of creator */
    gid_t          cgid;     /* Effective GID of creator */
    unsigned short mode;     /* Permissions + SHM_DEST and SHM_LOCKED flags */
    unsigned short __seq;    /* Sequence number */
};

程序演示:获取共享内存属性,并输出共享内存大小

代码演示:

cpp 复制代码
  1 #include <stdio.h>
  2 #include <unistd.h>
  3 #include <sys/ipc.h>
  4 #include <sys/shm.h>
  5 
  6 int main()
  7 {
  8   int shmid = shmget(0x34343434,1024,IPC_CREAT | 0664);
  9 
 10   if(shmid < 0)
 11   {
 12     perror("shmget");
 13     return -1;
 14   }
 15   struct shmid_ds buf;
 16   shmctl(shmid, IPC_STAT, &buf);
 17   //获取共享内存段大小
 18   printf("shm_size: %ld字节\n", buf.shm_segsz);                                                
 19   return 0;
 20 }

执行结果:

进程读取共享内存中数据时,并不会将共享内存中的数据拿走,数据依然存在共享内存之中

相关推荐
码农君莫笑7 分钟前
Blazor项目中使用EF读写 SQLite 数据库
linux·数据库·sqlite·c#·.netcore·人机交互·visual studio
mubeibeinv17 分钟前
项目搭建+图片(添加+图片)
java·服务器·前端
dessler22 分钟前
Docker-如何启动docker
运维·docker·云原生·容器·eureka
zhy2956322 分钟前
【DOCKER】基于DOCKER的服务之DUFS
运维·docker·容器·dufs
无为之士27 分钟前
Linux自动备份Mysql数据库
linux·数据库·mysql
秋名山小桃子37 分钟前
Kunlun 2280服务器(ARM)Raid卡磁盘盘符漂移问题解决
运维·服务器
与君共勉1213838 分钟前
Nginx 负载均衡的实现
运维·服务器·nginx·负载均衡
岑梓铭44 分钟前
(CentOs系统虚拟机)Standalone模式下安装部署“基于Python编写”的Spark框架
linux·python·spark·centos
努力学习的小廉44 分钟前
深入了解Linux —— make和makefile自动化构建工具
linux·服务器·自动化
MZWeiei1 小时前
Zookeeper基本命令解析
大数据·linux·运维·服务器·zookeeper