首页 理论教育 共享内存-零点起步嵌入式Linux编程入门与实例

共享内存-零点起步嵌入式Linux编程入门与实例

时间:2023-10-31 理论教育 版权反馈
【摘要】:共享内存是存在于内核级别的一种资源。图9-13描述了多个进程使用共享内存的情况。图9-13 多个进程使用共享内存的情况如图9-13所示,箭头方向描述了进程地址空间映射到系统内存地址的位置。两个进程通过映射普通文件,实现共享内存方式通信。使用函数shmctl()可以对共享内存段进行多种操作。

共享内存-零点起步嵌入式Linux编程入门与实例

共享内存是最便捷、速度最快的进程通信方式,它将同一块物理内存分别映射到A、B两个进程的逻辑空间。共享内存是存在于内核级别的一种资源。在Shell中可以使用ipcs命令来查看当前系统IPC中的状态。在文件系统中的/proc目录下有对其描述的相应文件。

在系统内核为一个进程分配内存地址时,通过分页机制可以让一个进程的物理地址不连续,同时也可以让一段内存同时分配给不同的进程。由于多个进程共享同一块内存空间,因此需要其他同步机制协同工作。图9-13描述了多个进程使用共享内存的情况。

978-7-111-33316-6-Chapter09-79.jpg

图9-13 多个进程使用共享内存的情况

如图9-13所示,箭头方向描述了进程地址空间映射到系统内存地址的位置。对于每一个共享存储段,内核会为其维护一个shmid_ds类型的结构体(shmid_ds结构体定义在头文件<sys/shm.h>中)。

shmid_ds结构体的定义如下:

978-7-111-33316-6-Chapter09-80.jpg

结构体shmid_ds根据不同的系统内核版本略有不同,并且在不同的系统中会对共享存储段的大小有限制。应用时请查询相应的系统手册。

1.共享内存的创建

函数shmget()可以创建或打开一块共享内存区。该函数的原型如下:

978-7-111-33316-6-Chapter09-81.jpg

该函数用来取得参数key所关联的共享内存识别代码。size参数为要请求的内存长度(以字节为单位)。

内核是以页为单位分配内存的,当size参数的值不是系统内存页长的整数倍时,系统会分配给进程最小的可以满足size长的页数,但是最后一页的剩余部分内存是不可用的。

当打开一个内存段时,参数size的值为0。参数flag中的相应权限位用于初始化ipc_perm结构体中的mode域。同时,参数flag是函数行为参数,它指定一些当函数遇到阻塞或其他情况时应做出的反应。shmid_ds的初始化见表9-9。

表9-9 shmid_ds的初始化

978-7-111-33316-6-Chapter09-82.jpg

【例9-7】进程通信。

两个进程通过映射普通文件,实现共享内存方式通信。

设计步骤

1)在Vim中创建两个新工程文件,命名为“map1.c”和“map2.c”。

2)创建的代码如下所示。

978-7-111-33316-6-Chapter09-83.jpg

978-7-111-33316-6-Chapter09-84.jpg(www.xing528.com)

map1.c首先定义了一个people数据结构,(这里采用数据结构的方式是因为共享内存区的数据往往是有固定格式的,这由通信的各个进程所决定。采用结构的方式有普遍代表性)。map1.c先打开或创建一个文件,并把文件的长度设置为5个people结构大小,后从mmap()的返回地址开始设置10个people结构。然后,进程睡眠10s,等待其他进程映射同一个文件,最后解除映射。map2.c只是简单地映射一个文件,以people数据结构的格式从mmap()返回的地址处读取10个people结构,并输出读取的值,然后解除映射。

3)用GCC编译并运行,结果如图9-14所示。

978-7-111-33316-6-Chapter09-85.jpg

图9-14 map.c的运行结果图

在map1.c输出initializeover之后,输出umapok之前,在另一个终端上运行map2./m,将会产生如图9-15所示的输出结果。

978-7-111-33316-6-Chapter09-86.jpg

图9-15 map1.c和map2.c第一种情况运行结果图

在map1.c输出umapok后运行map2.c,则输出如图9-16所示的结果。

978-7-111-33316-6-Chapter09-87.jpg

图9-16 map1.c和map2.c第二种情况运行结果图

2.共享内存的操作

由于共享内存这一特殊的资源类型,使它不同于普通的文件,因此,系统需要为其提供专有的操作函数,而这无疑增加了程序员开发的难度(需要记忆额外的专有函数)。使用函数shmctl()可以对共享内存段进行多种操作。其函数原型如下:

978-7-111-33316-6-Chapter09-88.jpg

其中,参数shm_id为所要操作的共享内存段的标识符;structshmid_ds型指针参数buf的作用与参数cmd的值相关;参数cmd指明了所要进行的操作。shmct1()函数中的参数cmd详解见表9-10。

表9-10 shmctl()函数中的参数cmd详解

978-7-111-33316-6-Chapter09-89.jpg

使用函数shmat()将一个存在的共享内存段连接到本进程空间,其函数原型如下:

978-7-111-33316-6-Chapter09-90.jpg

其中,参数shm_id指定要引入的共享内存;参数addr与flag组合说明要引入的地址值,通常只有两种用法:addr为0,表明由内核来决定第1个可以引入的位置;add不为0,并且flag中指定了SHM_RND,则此段引入到addr所指向的位置(不推荐使用此操作,因为不会只在一种硬件上运行应用程序。为了程序的通用性,推荐使用第1种方法),在flag参数中可以指定要引入的方式(读写方式)。若函数成功,则执行返回值为实际引入的地址;若函数失败,则返回-1。若shmat()函数成功执行,则会将shm_id段的shmid_ds结构的shm_nattach计数器的值加1。

当对共享内存段操作结束时,应调用shmdt()函数,其作用是将指定的共享内存段从当前进程空间中脱离出去。函数原型如下:

978-7-111-33316-6-Chapter09-91.jpg

参数addr是调用shmad()函数的返回值。若函数执行成功,则返回0,并将该共享内存的shmid_ds结构的shm_nattach计数器减1;若函数执行失败,则返回-1。

免责声明:以上内容源自网络,版权归原作者所有,如有侵犯您的原创版权请告知,我们将尽快删除相关内容。

我要反馈