文章目录
System V IPC
-
IPC 对象包含: 共享内存、消息队列和信号灯集
-
每个IPC对象有唯一的ID(IPC对象创建的时候由系统分配的一个数字,只有创建IPC对象的进程可以获得ID,别的进程不知道这个ID号)
-
IPC对象创建后一直存在,直到被显式地删除
-
每个IPC对象有一个关联的KEY(可以看成IPC对象的一个属性,通过KEY值,可以使不同的进程能够打开同一个IPC对象。创建IPC对象的进程把KEY值和IPC对象关联)
ipcs / ipcrm -
·ipcs查看System V的IPC对象(显示当前系统中所有的IPC对象)
·iprm删除System V的IPC对象linux@linux:~$ ipcs ------------ 共享内存段 -------------- 键 shmid 拥有者 权限 字节 连接数 状态 0x00000000 1409024 linux 600 524288 2 目标 0x00000000 2293761 linux 600 524288 2 目标 0x00000000 425986 linux 600 524288 2 目标 0x00000000 622595 linux 600 524288 2 目标 0x00000000 983044 linux 600 524288 2 目标 0x00000000 1245189 linux 600 524288 2 目标 0x00000000 1015814 linux 600 16777216 2 0x00000000 1114119 linux 600 33554432 2 目标 0x00000000 1540104 linux 600 1048576 2 目标 0x00000000 1966090 linux 600 524288 2 目标 0x00000000 2031627 linux 600 2097152 2 目标 0x00000000 2916364 linux 600 524288 2 目标 --------- 信号量数组 ----------- 键 semid 拥有者 权限 nsems --------- 消息队列 ----------- 键 msqid 拥有者 权限 已用字节数 消息 linux@linux:~$
- KEY值为0,表示私有的IPC对象
使用IPC对象的大致流程
- 进程创建IPC对象之前先指定一个KEY,KEY的值可以为0(宏IPC_PRIVATE的值为0),为0表示这是一个私有对象,不为0表示这个对象会被多个进程访问
- KEY值还可以通过函数ftok()创建(避免指定的KEY值会与系统中已有的KEY值冲突)
- 根据KEY值去创建IPC对象
生成KEY值ftok()
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *path, int proj_id);
- 成功时返回合法的key值,失败时返回EOF
- path 存在且可访问的文件的路径(ftok()生成的KEY值实际上path所指的文件的i节点的编号)
- proj_id 用于生成key的数字,不能为0(i节点的编号和proj_id的低8为进行移位,拼成一个新的数字)
ftok示例
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
key_t key;
if ((key = ftok(“.”, ‘a’)) == -1)
{
perror(“key”);
exit(-1);
}
……
- 每个进程必须生成相同的KEY,才能通过相同的KEY,找到相同的IPC对象(每个进程都需要调用ftok()函数,且参数必须一致,参数一致才能生成相同的KEY)
共享内存
- 共享内存是一种最为高效的进程间通信方式,进程可以直接读写内存,而不需要任何数据的拷贝
- 共享内存在内核空间创建,可被进程映射到用户空间访问,使用灵活
- 由于多个进程可同时访问共享内存,因此需要同步和互斥机制配合使用
共享内存使用步骤
- :创建/打开共享内存
- :映射共享内存,即把指定的共享内存映射到进程的地址空间用于访问
- :读写共享内存
- :撤销共享内存映射
- :删除共享内存对象
共享内存创建 – shmget()
- 成功时返回共享内存的id,失败时返回EOF
- key 和共享内存关联的key,IPC_PRIVATE 或 ftok生成
- size指定共享内存的大小,以字节为单位
- shmflg 共享内存标志位 IPC_CREAT|0666(是否新建,以及对共享内存的读写权限)
共享内存创建 - shmget – 示例
示例一:创建一个私有的共享内存,大小为512字节,权限为0666
int main(int argc, const char *argv[])
{
int shmid;
if ((shmid = shmget(IPC_PRIVATE, 512, 0666)) < 0)
{
perror("shmget");
exit(-1);
}
return 0;
}
- 私有的共享内存一定是新建的,所以不需要加 IPC_CREAT标志
示例二:创建/打开一个和key关联的共享内存,大小为1024字节,权限为0666
int main(int argc, const char *argv[])
{
key_t key;
int shmid;
if ((key = ftok(".", 'm')) == -1)
{
perror("ftok");
exit(-1);
}
if ((shmid = shmget(key, 1024, IPC_CREAT|0666)) < 0)
{
perror("shmget");
exit(-1);
}
return 0;
}
- 指定了IPC_CREAT标志,执行shmget()函数的时候,系统会检查,如果和key关联的共享内存对象不存在则创建一个,并把key值和对象关联;如果已经存在,直接返回对象ID,并打开
共享内存映射 – shmat
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
- 成功时返回映射后的地址,失败时返回(void *)-1
- shmid 要映射的共享内存id
- shmaddr 映射后的地址, NULL表示由系统自动映射
- shmflg 标志位 0表示可读写;SHM_RDONLY表示只读
共享内存读写
例如:在共享内存中存放键盘输入的字符串
char *addr;
int shmid;
……
if ((addr = (char *)shmat(shmid, NULL, 0)) == (char *)-1)
{
perror(“shmat”);
exit(-1);
}
fgets(addr, N, stdin);
- 通过指针访问共享内存,指针类型取决于共享内存中存放的数据类型
共享内存撤销映射-shmdt
- 成功时返回0,失败时返回EOF
- 不使用共享内存时应撤销映射
- 进程结束时自动撤销
- shmaddr 映射后的地址
共享内存控制 – shmctl
- 成功时返回0,失败时返回EOF
- shmid 要操作的共享内存的id
- cmd 要执行的操作 IPC_STAT(获取当前共享内存的shmid_ds结构并保存在buf中) IPC_SET(使用buf中的值设置当前共享内存的shmid_ds结构) IPC_RMID(删除当前共享内存,第三个参数传NULL)
- buf 保存或设置共享内存属性的地址
- shmid_ds结构 ,共享内存的属性,包括:大小、跟它关联的key值、权限、创建者用户ID等
共享内存 - 注意事项
- 每块共享内存大小有限制
·ipcs -l(查看当前系统中关于三类IPC对象的设置)
·cat /proc/sys/kernel/shmmax(查看当前系统中关于共享内存最大大小的设定) - 修改共享内存大小
·sysctl -w kernel.shmmax=134217728
·echo 134217728 >/proc/sys/kernel/shmmax - 共享内存删除的时间点(最后一个进程负责删除共享内存)
·shmctl(shmid, IPC_RMID, NULL) 添加删除标记
·nattach 变成0时真正删除(nattach记录了有几个进程映射了共享内存,调用shmat()时nattach加一,调用shmdt()nattach减一)