第九章第三节 共享内存
共享内存是最高效的IPC机制,因为它不涉及进程之间任何的数据传输。但是这种高效率必须依靠其他辅助手段来同步进程对共享内存的访问,否则会产生竞态条件。因此,共享内存退出需要与其他进程间通信方式一起使用。下面介绍共享内存的4个系统调用。
shmget系统调用
shmget系统调用创建一段新的共享内存,或者获取一段已经存在的共享内存。其定义如下。
1 | /* |
使用shmget创建共享内存之后,这段共享内存的所以字节均被初始化为0,与之关联的内核数据结构shmid_ds将被创建并初始化。结构体shmid_ds定义如下。
1 | /* |
shmget对shmid_ds结构体的初始化如下。
- shm_perm.cuid与shm_perm.uid设置为调用进程的有效用户ID;
- shm_perm.cgid与shm_perm.gid设置为调用进程的有效组ID;
- shm_perm.mode低9位设置为flags参数的最低9位;
- shm_segsz设置为size;
- shm_atime、shm_dtime、shm_lpid、shm_nattch设置为0;
- shm_ctime设置为当前系统时间;
shmat与shmdt系统调用
共享内存被创建/获取之后,此时应用程序并不能立即访问它,而是需要先将它关联到进程的地址空间中。使用完共享内存之后,应用程序也要将其从进程的地址空间中分离出来。这两项任务分别由如下两个系统调用实现。
1 | /* |
- 若shm_addr非空,并且未设置SHM_SND标志,则共享内存被关联到addr指定地址;
- 若shm_addr非空,并且设置了SHM_SND标志,则被关联的地址为shm_addr-(shm_addr%SHMLBA),其中SHMLBA为”段低端边界地址倍数”。它必须是内存页面大小的整数倍;
shmat成功返回将修改内核数据结构shmid_ds的部分字段。
- shm_nattch置1;
- shm_lpid置为调用进程的PID;
- shm_atime置为当前时间;
1 | /* |
shmdt系统调用将关联到shm_addr处的共享内存从进程中分离。shmdt成功返回时将修改内核数据结构shmid_ds的部分字段。
- shm_nattch减1;
- shm_lpid置为调用进程的PID;
- shm_dtime置为当前时间;
shmctl系统调用
shmctl系统调用控制共享内存的某些属性。其定义如下。
1 | /* |
其中,shmctl支持的命令如下表。
共享内存的POSIX方法
Linux提供另一种利用mmap在无关内存之间共享内存的方式,这种方式无须任何文件的支持,但它需要使用如下函数创建/打开一个POSIX共享内存对象。
1 | /* |
shm_open调用成功返回的文件描述符可用于后续的mmap调用,从而将共享内存关联到调用进程。
一般,shm_open返回的文件描述符会配合ftruncate函数使用,ftruncate函数设置指定文件的大小。其定义如下。
1 | /* |
同理,shm_open创建的共享内存对象也需要在使用完释放,使用shm_unlink函数将共享内存对象标记为”等待删除”,当所有关联了此共享内存对象的进程都使用munmap函数将共享内存对象从进程的地址空间中分离之后,系统将销毁这个共享内存对象所占据的资源。
1 | /* |
注:使用gcc编译调用了POSIX共享内存函数的代码时,需要加上链接选项-lrt。