1. 基本特点
1)两个或者更多进程,共享同一块由系统内核负责维护的内存区域,其地址空间通常被映射到堆和栈之间。
如图所示,每个进程都有自己的share memory,共享内存其实是内核中的一块空间,是对不同进程share memory的映射,对share memory的操作实质就是对内核中共享内存的操作,操作形式和普通内存没什么区别。
2) 无需复制信息,最快的一种IPC机制,所以适合大文件的传输。
3) 需要考虑同步访问的问题,不能那边还没写完,这边就读。
4) 内核为每个共享内存,维护一个shmid_ds结构体形式(描述大小,位置等信息)的共享内存对象
struct shmid_ds { struct ipc_perm shm_perm; // 所有者及其权限 size_t shm_segsz; // 大小(以字节为单位) time_t shm_atime; // 最后加载时间 time_t shm_dtime; // 最后卸载时间 time_t shm_ctime; // 最后改变时间 pid_t shm_cpid; // 创建进程PID pid_t shm_lpid; // 最后加载/卸载进程PID shmatt_t shm_nattch; // 当前加载计数 ... };
struct ipc_perm { key_t __key; // 键值 uid_t uid; // 有效属主ID gid_t gid; // 有效属组ID uid_t cuid; // 有效创建者ID gid_t cgid; // 有效创建组ID unsigned short mode; // 权限字 unsigned short __seq; // 序列号 };
2. 常用函数:
1) 创建/获取共享内存
int shmget (key_t key, size_t size, int shmflg);
A. 该函数以key参数为键值创建共享内存, 或获取已有的共享内存。
B. size参数为共享内存的字节数, 建议取内存页字节数(4096)的整数倍。若希望创建共享内存,则必需指定size参数。若只为获取 已有的共享内存,则size参数可取0。
C. shmflg取值:
0 - 获取,不存在即失败。
PC_CREAT - 创建,不存在即创建,已存在即获取,除非...或了一个IPC_EXCL
IPC_EXCL - 排斥,已存在即失败
D. 成功返回共享内存标识,失败返回-1。
2) 加载共享内存 , 即建立一个映射
void* shmat (int shmid, const void* shmaddr,int shmflg);
A. 将shmid参数所标识的共享内存,映射到调用进程的地址空间。
B. 可通过shmaddr参数人为指定映射地址,也可将该参数置NULL,由系统自动选择。
C. shmflg取值:
0 - 以读写方式使用共享内存。
SHM_RDONLY - 以只读方式使用共享内存。
SHM_RND - 只在shmaddr参数非NULL时起作用。表示对该参数向下取内存页的整数倍,作为映射地址。
D. 成功返回映射地址,失败返回-1。
E. 内核将该共享内存的加载计数加1。(映射是有计数的,类似于单例)
3) 卸载共享内存 即 解除映射
int shmdt (const void* shmaddr);
A. 从调用进程的地址空间中,取消由shmaddr参数所指向的,共享内存映射区域。
B. 成功返回0,失败返回-1。
C. 内核将该共享内存的加载计数减1。
4) 销毁/控制共享内存
int shmctl (int shmid, int cmd, struct shmid_ds* buf);
A. cmd取值:
IPC_STAT - 获取共享内存的属性,通过buf参数输出。
IPC_SET - 设置共享内存的属性,通过buf参数输入,仅以下三个属性可设置:
shmid_ds::shm_perm.uid
shmid_ds::shm_perm.gid
shmid_ds::shm_perm.mode
IPC_RMID - 标记删除共享内存。并非真正删除共享内存,只是做一个删除标记, 禁止其被继续加载,但已有加载依然保留只有当 该共享内存的加载计数为0时,才真正被删除。
B. 成功返回0,失败返回-1。
3.编程模型
wshm.c
#include <stdio.h> #include <unistd.h> #include <sys/shm.h> int main (void) { printf ("创建共享内存...\n"); key_t key = ftok (".", 100); if (key == -1) { perror ("ftok"); return -1; } int shmid = shmget (key, 4096, 0644 | IPC_CREAT | IPC_EXCL); if (shmid == -1) { perror ("shmget"); return -1; } printf ("加载共享内存...\n"); void* shmaddr = shmat (shmid, NULL, 0); if (shmaddr == (void*)-1) { perror ("shmat"); return -1; } printf ("写入共享内存...\n"); sprintf (shmaddr, "我是%u进程写入的数据。", getpid ()); printf ("按<回车>卸载共享内存(0x%08x/%d)...", key, shmid); getchar (); if (shmdt (shmaddr) == -1) { perror ("shmdt"); return -1; } printf ("按<回车>销毁共享内存(0x%08x/%d)...", key, shmid); getchar (); if (shmctl (shmid, IPC_RMID, NULL) == -1) { perror ("shmctl"); return -1; } printf ("大功告成!\n"); return 0; }
使用ipcs -m 命令我们可以得到系统中刚创建的共享内存:
rshm.c
#include <stdio.h> #include <sys/shm.h> int shmstat (int shmid) { struct shmid_ds shm; if (shmctl (shmid, IPC_STAT, &shm) == -1) { perror ("shmctl"); return -1; } printf ("------------------------------------------------\n"); printf (" 共享内存信息\n"); printf ("----+----------------+--------------------------\n"); printf (" 所 | 键值 | 0x%08x\n", shm.shm_perm.__key); printf (" 有 | 有效属主ID | %u\n", shm.shm_perm.uid); printf (" 者 | 有效属组ID | %u\n", shm.shm_perm.gid); printf (" 及 | 有效创建者ID | %u\n", shm.shm_perm.cuid); printf (" 其 | 有效创建组ID | %u\n", shm.shm_perm.cgid); printf (" 权 | 权限字 | %#o\n", shm.shm_perm.mode); printf (" 限 | 序列号 | %u\n", shm.shm_perm.__seq); printf ("----+----------------+--------------------------\n"); printf (" 大小(字节) | %u\n", shm.shm_segsz); printf (" 最后加载时间 | %s", ctime (&shm.shm_atime)); printf (" 最后卸载时间 | %s", ctime (&shm.shm_dtime)); printf (" 最后改变时间 | %s", ctime (&shm.shm_ctime)); printf (" 创建进程ID | %u\n", shm.shm_cpid); printf (" 最后加载/卸载进程ID | %u\n", shm.shm_lpid); printf (" 当前加载计数 | %u\n", shm.shm_nattch); printf ("---------------------+--------------------------\n"); return 0; } int shmset (int shmid) { struct shmid_ds shm; if (shmctl (shmid, IPC_STAT, &shm) == -1) { perror ("shmctl"); return -1; } shm.shm_perm.mode = 0600; shm.shm_segsz = 8192; if (shmctl (shmid, IPC_SET, &shm) == -1) { perror ("shmctl"); return -1; } return 0; } int main (void) { printf ("获取共享内存...\n"); key_t key = ftok (".", 100); if (key == -1) { perror ("ftok"); return -1; } int shmid = shmget (key, 0, 0); if (shmid == -1) { perror ("shmget"); return -1; } printf ("加载共享内存...\n"); void* shmaddr = shmat (shmid, NULL, 0); if (shmaddr == (void*)-1) { perror ("shmat"); return -1; } shmstat (shmid); printf ("读取共享内存...\n"); printf ("共享内存(0x%08x/%d):%s\n", key, shmid, shmaddr); printf ("卸载共享内存...\n"); if (shmdt (shmaddr) == -1) { perror ("shmdt"); return -1; } shmstat (shmid); printf ("设置共享内存...\n"); shmset (shmid); shmstat (shmid); printf ("大功告成!\n"); return 0; }
版权声明:本文为博主原创文章,未经博主允许不得转载。