四、共享内存
1.基本特点
(1) 两个或者更多进程,共享同一块由系统内核负责维护的内存区域,其地址空间通常被映射到堆和栈之间。
(2) 无需复制信息,最快的一种IPC机制。
(3) 需要考虑同步访问的问题。
(4) 内核为每个共享内存,维护一个shmid_ds结构体形式的共享内存对象。
2.常用函数
#include
(1) 创建/获取共享内存
int shmget (key_t key, size_t size, int shmflg);
A. 该函数以key参数为键值创建共享内存,或获取已有的共享内存。
B. size参数为共享内存的字节数, 建议取内存页字节数(4096)的整数倍。
若希望创建共享内存,则必需指定size参数。
若只为获取已有的共享内存,则size参数可取0。
C. shmflg取值:
0 - 获取,不存在即失败。
IPC_CREAT - 创建,不存在即创建,
已存在即获取,除非…
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。
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; // 序列号
};
范例:wshm.c、rshm.c
wshm.c
#include
#include
#include
int main ()
{
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;
}
rshm.c
#include
#include
#include
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 (" 当前加载计数 | %ld\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 ()
{
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, (char*)shmaddr);
printf ("卸载共享内存...\n");
if (shmdt (shmaddr) == -1)
{
perror ("shmdt");
return -1;
}
shmstat (shmid);
printf ("设置共享内存...\n");
shmset (shmid);
shmstat (shmid);
printf ("大功告成!\n");
return 0;
}