写点什么

Linux 系统编程 - 进程间通信 (共享内存)

作者:DS小龙哥
  • 2022 年 2 月 16 日
  • 本文字数:3325 字

    阅读完需:约 11 分钟

学习 Linux 编程,进程间通信是一个非常重要的版块,当前文章介绍的是 Linux 进程间通信方式--共享内存段。通过共享内存方式实现多个进程间数据共享,完成通信。

1. 共享内存机制(shmget)

共享内存也是进程间(进程间不需要有继承关系)通信的一种常用手段。一般 OS 通过内存映射与页交换技术,使进程的内存空间映射到不同的物理内存,这样能保证每个进程运行的独立性,不至于受其它进程的影响。但可以通过共享内存的方式,使不同进程的虚拟内存映射到同一块物理内存,一个进程往这块物理内存中更新的数据,另外的进程可以立即看到这块物理内存中修改的内容。


内存映射和共享内存的区别:


mmap 内存映射:跟普通文件的读写相比,加快对文件/设备的访问速度。shmget 共享内存:多进程间进行通信。


原理及实现:


system V IPC 机制下的共享内存本质是一段特殊的内存区域,进程间需要共享的数据被放在该共享内存区域中,所有需要访问该共享区域的进程都要把该共享区域映射到本进程的地址空间中去。这样一个使用共享内存的进程可以将信息写入该空间,而另一个使用共享内存的进程又可以通过简单的内存读操作获取刚才写入的信息,使得两个不同进程之间进行了一次信息交换,从而实现进程间的通信。共享内存允许一个或多个进程通过同时出现在它们的虚拟地址空间的内存进行通信,而这块虚拟内存的页面被每个共享进程的页表条目所引用,同时并不需要在所有进程的虚拟内存都有相同的地址。进程对象对于共享内存的访问通过 key(键)来控制,同时通过 key 进行访问权限的检查。

2. 共享内存机制相关函数接口介绍

2.1 ftok 函数

#include <sys/types.h>#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
复制代码


函数功能: 用于创建一个关键字,可以用该关键字关联一个共享内存段。参数介绍:(1) pathname:全路径文件名,并且该文件必须可访问。(2) proj_id:通常传入一非 0 字符。通过 pathname 和 proj_id 组合可以创建唯一的 key(对任何进程都是唯一且相同的)。返回值:如果调用成功,返回一关键字,否则返回-1。

2.2 shmge 函数

#include <sys/ipc.h>#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
复制代码


shmget 函数用于创建或打开一共享内存段,该内存段由函数的第一个参数标识。函数成功则返回一个该共享内存段的唯一标识号(唯一的标识了这个共享内存段),对任何进程都是唯一且相同的。


参数介绍(1) key 是一个与共享内存段相关联的关键字,如果事先已经存在一个与指定关键字关联的共享内存段,则直接返回该内存段的标识。key 的值既可以用 ftok 函数产生,也可以是 IPC_RPIVATE(用于创建一个只属于创建进程的共享内存,主要用于父子通信),表示总是创建新的共享内存段。(2) size 指定共享内存段的大小,以字节为单位。(3) shmflg 是一掩码合成值,可以是访问权限值与(IPC_CREAT 或 IPC_EXCL)的合成。IPC_CREAT 表示如果不存在该内存段,则创建它。IPC_EXCL 表示如果该内存段存在,则函数返回失败结果(-1)。


返回值


如果调用成功,返回内存段标识,否则返回-1。

2.3 shmat 函数

函数 shmat 将共享内存段映射到进程空间的某一地址。


#include <sys/types.h>#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
复制代码


注意: 只有管理员用户权限才可以获取内存地址


参数(1) shmid 是共享内存段的标识通常应该是 shmget 的成功返回值。(2) shmaddr 指定的是共享内存连接到当前进程中的地址位置。通常是 NULL,表示让系统来选择共享内存出现的地址。(3) shmflg 是一组位标识,通常为 0 即可。如果是 SHM_RDONLY 的话,就是只读模式。其它的是读写模式。


返回值如果调用成功,返回映射后的进程空间的首地址,否则返回(void*)-1。

2.4 shmdt 函数

shmdt 用于将共享内存段与进程空间分离,与 shmat 函数相反。用于关闭共享内存段。


#include <sys/types.h>#include <sys/shm.h>int shmdt(const void *shmaddr);
复制代码


参数 shmaddr 通常为 shmat 的成功返回值。返回值成功返回 0,失败时返回-1。注意:只是将共享内存分离,并没有没删除它,只是使得该共享内存对当前进程不再可用。

2.5 shmctl 函数

函数 shmctl 是共享内存的控制函数,可以用来删除共享内存段。


#include <sys/ipc.h>#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
复制代码


参数:


(1)shmid:共享内存段标识 通常应该是shmget的成功返回值


(2)cmd:对共享内存段的操作方式



可选为IPC_STAT,IPC_SET,IPC_RMID。通常为IPC_RMID,表示删除共享内存段。


(3)buf:表示共享内存段的信息结构体数据,通常为 NULL。


例如: shmctl(kshareMem,IPC_RMID,NULL)表示删除共享内存段kHareMem

3. 案例: 基本用法示例 1

3.1 创建内存段写数据示例

下面代码使用/work/1.dat的文件属性获取 key,作为内存标识符;再创建共享内存段,映射内存地址,然后向内存空间写入数据"hello world",再取消映射。这时候其他进程就可以访问这个内存段读取里面的数据。


#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>int main(){    key_t key = ftok("/work/1.dat",1);              //1. 写入端先用ftok函数获得key    int shmid = shmget(key,4096,IPC_CREAT);   //2. 写入端用shmget函数创建共享内存段    printf("key = %d shmid = %d\n", key, shmid);    char *p = (char *)shmat(shmid, NULL, 0);     //3. 获得共享内存段的首地址    memset(p, 0, 4096);                       //  清除内存空间    memcpy(p, "hello world", 4096);              //4. 往共享内存段中写入内容    shmdt(p);                               //5. 关闭共享内存段    return 0;}
复制代码

3.2 打开内存段读取示例

下面代码用来访问,上面写端代码创建的共享内存段里的数据,流程一样。shmget函数如果判断出共享内存段已经存在,就不会再重复创建(依靠 key 作为标识符判断的);接着再映射空间地址,读取内存里的数据,打印出hello world,最后再销毁内存空间。


#include <stdio.h>#include <unistd.h>#include <sys/ipc.h>#include <sys/shm.h>#include <sys/types.h>int main(){    key_t key = ftok("/work/1.dat",1);    int shmid = shmget(key,4096,IPC_CREAT);    printf("key = %d shmid = %d\n", key, shmid);    char *p = (char *)shmat(shmid, NULL, 0);    printf("receive the data:%s\n",p);         //4. 读取共享内存段中的内容    shmctl(shmid, IPC_RMID, 0);       //5. 删除共享内存段//不删除内存空间,数据会一直存在的    return 0;}
复制代码

4. 案例: 基本用法示例 2

上面例子代码是通过获取文件的属性得到唯一的 key,实际上也可以自己指定 key,只要保证唯一性即可。

4.1 创建内存写数据示例

#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <dirent.h>#include <stdlib.h>#include <sys/mman.h>#include <sys/wait.h>#include <sys/shm.h>
int main(int argc,char **argv){ /*1. 创建共享内存段*/ int shmid=shmget(123456,4096,IPC_CREAT); /*2. 映射共享内存到进程空间*/ unsigned char *p; p=shmat(shmid,NULL,0); /*3. 对共享内存实现读写*/ strcpy(p,"Linux下进程间通信共享内存学习"); /*4. 取消映射*/ shmdt(p); return 0;}
复制代码

4.2 打开内存读数据示例

#include <stdio.h>#include <unistd.h>#include <string.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <dirent.h>#include <stdlib.h>#include <sys/mman.h>#include <sys/wait.h>#include <sys/shm.h>
int main(int argc,char **argv){ /*1. 创建共享内存段*/ int shmid=shmget(123456,4096,IPC_CREAT); /*2. 映射共享内存到进程空间*/ unsigned char *p; p=shmat(shmid,NULL,0); /*3. 对共享内存实现读写*/ printf("p=%s\n",p); /*4. 取消映射*/ shmdt(p); /*5. 释放共享内存空间*/ shmctl(shmid,IPC_RMID,NULL); return 0;}
复制代码


发布于: 刚刚阅读数: 3
用户头像

DS小龙哥

关注

之所以觉得累,是因为说的比做的多。 2022.01.06 加入

熟悉C/C++、51单片机、STM32、Linux应用开发、Linux驱动开发、音视频开发、QT开发. 目前已经完成的项目涉及音视频、物联网、智能家居、工业控制领域

评论

发布
暂无评论
Linux系统编程-进程间通信(共享内存)