shmget
是 System V 共享内存的核心系统调用之一,其权限位(shmflg
参数)决定了共享内存段的访问控制和创建行为。以下是权限位的详细解析:
权限位的组成
shmflg
参数由两部分组成:
- 权限标志(低 9 位):控制用户/组/其他进程的访问权限,格式与文件权限一致(八进制表示)。
- 创建标志(高 位):控制共享内存段的创建行为(如
IPC_CREAT
)。
1. 权限标志(八进制格式)
权限位使用 9 位二进制 表示三类用户的权限:
- 用户(Owner)权限:前 3 位(八进制的百位)。
- 组(Group)权限:中间 3 位(八进制的十位)。
- 其他(Others)权限:后 3 位(八进制的个位)。
权限位的含义
二进制位 | 八进制值 | 含义 |
---|---|---|
0400 | 4 | 用户可读 |
0200 | 2 | 用户可写 |
0100 | 1 | 用户可执行(通常忽略) |
0040 | 40 | 组可读 |
0020 | 20 | 组可写 |
0010 | 10 | 组可执行(通常忽略) |
0004 | 4 | 其他用户可读 |
0002 | 2 | 其他用户可写 |
0001 | 1 | 其他用户可执行(通常忽略) |
常用权限示例
0600
:用户可读写,组和其他用户无权限。0644
:用户可读写,组和其他用户只读。0666
:所有用户可读写(需谨慎使用)。
2. 创建标志
通过按位或(|
)组合以下标志,控制共享内存的创建行为:
标志 | 值(十六进制) | 作用 |
---|---|---|
IPC_CREAT | 0x200 | 如果共享内存不存在,则创建;否则直接返回现有段的标识符。 |
IPC_EXCL | 0x400 | 与 IPC_CREAT 联用,若共享内存已存在,则返回错误(errno = EEXIST )。 |
IPC_NOWAIT | 0x800 | 非阻塞模式,若操作需等待(如资源不足),立即返回错误(较少使用)。 |
3. 权限检查规则
- 创建共享内存段时:
shmflg
的权限位会写入共享内存段的shm_perm.mode
字段,后续进程访问时需匹配这些权限。 - 访问现有段时:
内核会检查进程的 有效用户 ID 和 有效组 ID 是否符合权限:- 若进程的 EUID 与段的所有者 EUID 相同,检查 用户权限位。
- 若进程的 EGID 与段的组 EGID 相同,检查 组权限位。
- 否则检查 其他用户权限位。
4. 示例代码
#include <sys/ipc.h>
#include <sys/shm.h>
int main() {
key_t key = ftok("/tmp/shm_example", 'A'); // 生成唯一键值
// 创建共享内存段:用户可读写,组和其他用户只读
int shmid = shmget(key, 4096, IPC_CREAT | 0644);
if (shmid == -1) {
perror("shmget failed");
return 1;
}
// 其他操作(shmat、读写数据等)...
return 0;
}
5. 常见问题与错误
权限不足(EACCES
)
- 原因:进程没有权限访问现有共享内存段。
- 解决方案:
- 检查权限位是否允许当前用户/组访问。
- 以 root 权限运行进程(不推荐,存在安全风险)。
- 修改现有段的权限(需拥有者或 root 权限):
struct shmid_ds shm_info; shmctl(shmid, IPC_STAT, &shm_info); shm_info.shm_perm.mode = 0666; // 开放所有用户读写 shmctl(shmid, IPC_SET, &shm_info);
段已存在(EEXIST
)
- 原因:使用了
IPC_CREAT | IPC_EXCL
,但共享内存已存在。 - 解决方案:删除旧段或调整键值(
key
)。
6. 总结
- 权限位:定义共享内存段的访问规则,格式与文件权限一致(如
0644
)。 - 创建标志:
IPC_CREAT
创建新段,IPC_EXCL
确保唯一性。 - 权限检查:基于进程的 EUID/EGID 和权限位匹配。
合理设置权限位是保证共享内存安全性和功能性的关键!