System V共享内存区
创建或获取共享内存标识符:使用shmget系统调用,根据给定的关键字(key)、大小和标志创建一个新的共享内存段或获取一个已存在的共享内存段的标识符(ID)。这个关键字可以是任意值或者是通过ftok函数从文件路径和一个项目ID生成的。
将共享内存段附加到进程地址空间:使用shmat系统调用将由shmget返回的共享内存段ID所标识的共享内存段附加到调用进程的地址空间。这使得进程可以直接通过指针访问共享内存内容。
对于每个共享内存段,内核维护一个shmid_ds结构体,用于存储关于该段的信息。
struct ipc_perm shm_perm:包含共享内存段的权限和所有者信息。
size_t shm_segsz:共享内存段的大小(以字节为单位)。
pid_t shm_lpid:最后执行操作的进程ID。
pid_t shm_cpid:创建共享内存段的进程ID。
shmatt_t shm_nattch:当前附着到此共享内存段的进程数量。
shmatt_t shm_cnattch:核心中附着的进程数量(这个字段在某些实现中可能不可用)。
time_t shm_atime:最后一次附着时间。
time_t shm_dtime:最后一次分离时间。
time_t shm_ctime:本结构最后一次改变的时间。
ipc_perm结构包含了类似文件权限的信息,定义了哪些用户和组可以对共享内存段进行读取、写入和控制操作。shm_cnattch字段并不在所有的操作系统版本中都存在;它可能被废弃或者从未实现过。
shmatt_t 和 time_t 是类型定义,前者用于计数器,后者用于表示时间。
操作共享内存区:除了shmget和shmat之外,还有其他几个函数用来管理和控制共享内存段。
shmdt:从调用进程的地址空间中分离共享内存段。shmctl:用于执行各种控制操作,例如修改权限、获取或设置shmid_ds结构中的信息,以及销毁共享内存段。
shmat 函数用于将由 shmget 创建或获取的共享内存段附加到调用进程的地址空间中。
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int flag);
返回值:如果成功,函数返回指向共享内存段起始地址的指针;如果出错,则返回 (void *)-1。
shmid:这是通过 shmget 调用获得的共享内存标识符。shmaddr:指定共享内存段应该被附加到的地址。如果为 NULL(推荐做法),则操作系统选择合适的地址进行附加。
如果是一个非空指针,那么根据 flag 参数中的标志来决定如何处理这个地址。
flag:这是一个位掩码,用来指定附加时的行为。
SHM_RND:当设置了此标志并且 shmaddr 是一个非空指针时,共享内存段会附加到 shmaddr 向下舍入到最近的 SHMLBA 边界处。SHMLBA 代表低端边界地址(Lower Boundary Address),它通常是一个页面大小。SHM_RDONLY:设置此标志后,进程只能以只读方式访问共享内存段。
地址选择规则:当 shmaddr 是NULL 时,系统自动选择一个适当的地址。这种方法具有最好的可移植性,并且是推荐的做法。当shmaddr 不是 NULL 时,附加行为取决于是否设置了 SHM_RND 标志。
如果没有设置 SHM_RND,则直接使用 shmaddr 指定的地址作为附加位置。如果设置了 SHM_RND,则使用 shmaddr 向下舍入到 SHMLBA 的地址作为附加位置。
权限
默认情况下,只要调用进程对共享内存段有读写权限,它就可以在附加之后对该内存段进行读写操作。但是,如果在 flag 中指定了 SHM_RDONLY,那么该进程就只能以只读模式访问共享内存段。
使用建议
(1)除非有特殊需求,否则应尽量让操作系统选择附加地址,即令 shmaddr 为 NULL。
(2)在多进程环境中,确保所有进程都正确地同步它们对共享内存的操作,以避免竞态条件和其他并发问题。
shmdt 函数
shmdt 函数用于从调用进程的地址空间中分离(断接)一个之前通过 shmat 连接的共享内存段,删除共享内存段本身;它只是解除了该进程对共享内存段的访问。
#include <sys/shm.h>
int shmdt(const void *shmaddr);
返回值:如果成功,函数返回 0;如果出错,则返回 -1。
shmaddr:这是由 shmat 返回的指向共享内存段起始地址的指针。如果传递给 shmdt 的地址不是一个有效的共享内存段地址,则会引发错误。
注意事项
当一个进程终止时,它当前附接的所有共享内存区都会自动断开连接。
shmdt 不会删除共享内存段;要删除共享内存段,需要使用 shmctl 函数并指定 IPC_RMID 命令。
shmctl 函数
shmctl 提供了对一个共享内存段进行多种控制操作的能力。它可以用来获取或设置与共享内存段相关的参数,也可以用来删除共享内存段。
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
返回值:如果成功,函数返回 0;如果出错,则返回 -1。
shmid:这是通过 shmget 获得的共享内存标识符。
cmd:这个参数指定了要执行的操作命令。
IPC_RMID:立即从系统中移除由 shmid 标识的共享内存段,并减少其引用计数。如果引用计数降为零,那么共享内存段将被彻底删除。
IPC_SET:设置共享内存段的权限和其他属性。具体来说,它会更新 shmid_ds 结构中的 shm_perm.uid、shm_perm.gid 和 shm_perm.mode 成员,这些值来自于 buf 指向的结构体。此外,还会用当前时间更新 shm_ctime。
IPC_STAT:获取指定共享内存段的当前状态信息,并将其存储在 buf 指向的 shmid_ds 结构中。
buf:这是一个指向 shmid_ds 结构的指针,用于传递或接收共享内存段的信息。
shmget 程序
此程序用于创建一个指定大小的System V共享内存区。
#include "unpipc.h" // 假设包含了必要的头文件
int main(int argc, char **argv) {
int c, id, oflag = SVSHM_MODE | IPC_CREAT;
size_t length;
while ((c = getopt(argc, argv, "e")) != -1) {
switch (c) {
case 'e':
oflag |= IPC_EXCL;
break;
}
}
if (optind != argc - 2)
err_quit("usage: shmget [-e] <pathname> <length>");
length = atoi(argv[optind + 1]);
key_t key = ftok(argv[optind], 0);
id = shmget(key, length, oflag);
if (id == -1)
err_sys("shmget error");
void *ptr = shmat(id, NULL, 0);
if (ptr == (void *)-1)
err_sys("shmat error");
exit(0);
}
功能描述:
使用命令行参数提供的路径名(由 ftok 转换为键值)和长度创建一个新的共享内存段。
如果指定了 -e 选项,并且该内存区已经存在,则会出错。
将新创建或已存在的共享内存段附接到当前进程地址空间中。
shmrmid 程序
此程序用于删除一个指定的System V共享内存区。
#include "unpipc.h"
int main(int argc, char **argv) {
if (argc != 2)
err_quit("usage: shmrmid <pathname>");
key_t key = ftok(argv[1], 0);
int id = shmget(key, 0, SVSHM_MODE);
if (id == -1)
err_sys("shmget error");
if (shmctl(id, IPC_RMID, NULL) == -1)
err_sys("shmctl(IPC_RMID) error");
exit(0);
}
使用命令行参数提供的路径名获取共享内存段ID,并使用 IPC_RMID 命令删除它。
shmwrite 程序
此程序向一个指定的共享内存区写入一个模式(从0到255循环)。
#include "unpipc.h"
int main(int argc, char **argv) {
if (argc != 2)
err_quit("usage: shmwrite <pathname>");
key_t key = ftok(argv[1], 0);
int id = shmget(key, 0, SVSHM_MODE);
if (id == -1)
err_sys("shmget error");
unsigned char *ptr = (unsigned char *)shmat(id, NULL, 0);
if (ptr == (void *)-1)
err_sys("shmat error");
struct shmid_ds buff;
if (shmctl(id, IPC_STAT, &buff) == -1)
err_sys("shmctl(IPC_STAT) error");
for (size_t i = 0; i < buff.shm_segsz; i++)
ptr[i] = i % 256;
exit(0);
}
功能描述:
打开指定的共享内存区并将其附接到当前进程的地址空间。
写入从0到255循环的数据模式到共享内存区。
shmread 程序
此程序用于验证由 shmwrite 写入的数据模式是否正确。
#include "unpipc.h"
int main(int argc, char **argv) {
if (argc != 2)
err_quit("usage: shmread <pathname>");
key_t key = ftok(argv[1], 0);
int id = shmget(key, 0, SVSHM_MODE);
if (id == -1)
err_sys("shmget error");
unsigned char *ptr = (unsigned char *)shmat(id, NULL, 0);
if (ptr == (void *)-1)
err_sys("shmat error");
struct shmid_ds buff;
if (shmctl(id, IPC_STAT, &buff) == -1)
err_sys("shmctl(IPC_STAT) error");
for (size_t i = 0; i < buff.shm_segsz; i++) {
unsigned char c = ptr[i];
if (c != (i % 256))
err_ret("ptr[%zu] = %d", i, c);
}
exit(0);
}
打开指定的共享内存区并将其附接到当前进程的地址空间。验证共享内存区中的数据模式是否与预期相符(即从0到255循环)。