1. 概念
共享内存使得多个进程可以访问同一块内存空间,无需进行若干次的内存拷贝,是最快的可用 IPC 形式。它本身无同步互斥机制,它的同步与互斥需要由进程自己完成,通过与信号量结合使用,来达到进程间的同步及互斥。
2. 操作
(共享内存同信号量和消息队列有异曲同工之妙,前面我们已经了解了一些知识,此处相通的将不再赘述。)
共享内存的操作:
#define SHMAT //挂接:把上面打开的内存区域连接到用户的进程空间中
#define SHMDT //分离:将共享内存从当前进程中分离
#define SHMGET //创建:打开一个内存区域
#define SHMCTL //内存区域的控制:包括初始化和删除内存区域。
#include <sys/types.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg); //创建共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg); //映射到自己的内存空间
int shmdt(const void *shmaddr); //解除映射
int shmctl(int shmid, int cmd, struct shmid_ds *buf); //控制共享内存
参数解释:
sheget:创建或者打开一个共享内存,成功就返回相应的共享内存标识符,失败返回-1。
shmat:空间映射(挂接)。通过创建的共享内存,在它能被进程访问之前,需要把该段内存映射到用户进程空间。shmaddr 是用来指定共享内存映射到当前进程中的地址位置,要想该设置有用,shmflg 必须设置为 SHM_RND 标志。大部分情况下,应该设置为为空指针 (void *)0。让系统自动选择地址,从而减小程序对硬件的依赖性。shmflg 除了上面的设置以外,还可以设置为 SHM_RDONLY,使得映射过来的地址只读。如果函数调用成功,返回映射的地址的第一个字节,否则返回-1。
shmdt:解除映射(分离)。
shmctl:控制共享内存,明确的三个参数。struct shmid_ds 定义在 include/linux/shm.h,如下。cmd 有 IPC_STAT,IPC_SET,IPC_RMID,含义和消息队列一样。
struct shmid_ds {
struct ipc_perm
{
__kernel_key_t key;
__kernel_uid_t uid;
__kernel_gid_t gid;
__kernel_uid_t cuid;
__kernel_gid_t cgid;
__kernel_mode_t mode;
unsigned short seq;
};
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 */
};
3. 命令
查看共享内存:ipcs -m
删除共享内存:ipcrm -m shmid
4. 举例来实现进程间通信。
Makefile 的编写:
comm.h 的代码:
comm.c 的代码:
server.c 的代码:
client.c 的代码:
运行结果如下:
当只是运行了 server 并未运行 client 时,屏幕上不输出任何东西,我们可以看到刚开始的几行是空白的;
当在运行 server 的基础上继续在另一个终端运行 client 后,会发现开始输出 A,而只要不终止 client 的运行,就会一直挂接,连续打印,输出呈递增规律。
当我们终止了 client 的运行后,会发现此时输出在屏幕上的 A 不再增加,而是维持最后一次的输出结果继续打印。
以上的结果就很明显地告诉我们已经实现了进程间通信。
我们可以通过命令查看一下挂接数,同样印证了我们共享内存进程间通信的观点。
当查看挂接数的时候,需要在 root 用户下进行操作,否则将看不到想要的结果,我们切换到 root 用户下运行,然后查看。
打开第三个终端查看或者在 Xshell 下查看均可。
分析:当我们只是输入命令而不运行的话会发现并不存在任何东西,且屏幕上未输出结果;
当我们先运行 server 后出现了共享内存,此时挂接数为 1,屏幕上还是不输出结果;
当我们再运行 client,此时挂接数为 2,屏幕上开始输出打印 A,可以论证此时已经通过共享内存实现了进程间通信;
当我们关闭其中的任一个使其停止,挂接数就变为 1,全部关闭挂接数为 0,但是共享内存依然存在,可以说明共享内存的生命周期是随内核的。