每个system v IPC结构对应着一个标识符,标识符是一个非负的整型数据,这是一个内部名,对应的外部名为键值,多个进程通过共同的键值可以获取到同一个标识符从而能够操作同一个IPC结构。
键值
- IPC_PRIVATE键值属于匿名键值,一般用于父子进程IPC的情况。也可以把返回的标识符(整型值)保存到外部的文件中,由不相关进程获取从而进行IPC。
- 头文件显式定义键值(key_t),它存在的问题是无法知道键值是否已经被其他人使用,可以选择删除已经存在的IPC重新生成,但这还是存在误删的风险。
- 利用ftok生成一个键值,通过指定项目路径和代号生成。
#include <sys/ipc.h>
key_t ftok(const char *path, int id);
成功返回键值,出错返回(key_t)-1
消息队列
#include <sys/msg.h>
int msgget(key_t key, int flag); //根据键值key,返回消息队列id
int msgctl(int msqid, int cmd, struct msqid_ds *buf); //消息队列control操作函数,类似于ioctl
int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag); //发送消息到消息队列msqid
ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag); //从消息队列msqid中获取对应type类型的消息
这些函数出错返回-1
msgctl:
cmd参数可以选择如下:
IPC_STAT:表示取此队列的msqid_ds结构体
IPC_SET:设置此队列的msqid_ds结构体
IPC_RMID:从系统中删除该消息队列以及仍在该队列中的所有数据
msgsnd:
msg格式要求:
struct message {
long type; /* 消息类型,以在msgrcv中可以根据此type接收数据 */
char data[512]; /* nbytes 表示实际数据长度,本示例中最大不超过512 */
};
flag可以指定IPC_NOWAIT,以非阻塞I/O方式才做,当消息队列满时不会阻塞。
msgrcv
type参数可选:
type == 0 按照先进先出,返回消息队列的第一个消息
type > 0 返回type类型的第一个消息
type < 0 返回消息类型小于type绝对值的第一个消息,并且是type是最小的那个消息
消息队列一般情况下是先进先出,如果指定了type,那么可以不以先进先出的策略,而以type类型来接收。为了数据传递的正确性,一般可以定义不同type的消息具有不同的固定长度,这样发送和接收时按照定义的长度去接收就不会出错。
信号量(semaphore)
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag); //根据键值key,返回信号量id
int semctl(int semid, int semnum, int cmd, .../* union semun arg */); //信号量control操作函数,类似于ioctl
int semop(int semid, struct sembuf semoparray[], size_t nops); //信号量P/V操作,nops表示数组的长度
semget
system v信号量在每次申请使用时都不是单个的,而是一个集合,所以semget中才需要指定对应的集合中包含有nsems个信号量。如果是引用现有集合,nsems设置为0,比如服务进程已经创建了信号量集合,而客户端要使用,那么nsems设置为0即可。
flags用来指定访问权限,对于信号量只有读和写权限,不存在执行权限一说。
semctl
第4个参数的定义是一个联合体:
union semun {
int val; /* SETVAL */
struct semid_ds *buf; /* IPC_STAT / IPC_SET*/
unsigned short *array; /* GETALL / SETALL */
}
cmd参数可选:
IPC_STAT:表示取此信号量集合的semid_ds结构体
IPC_SET:设置此信号量集合的semid_ds结构体
IPC_RMID:从系统中删除该信号量集合
信号量专属命令:
GETVAL:返回信号集中对应semnum信号的值
SETVAL:设置信号集中对应semnum信号的值,通过arg设置
GETPID:返回信号集中对应semnum信号上次操作的进程pid号
GETNCNT:返回等待信号集中对应semnum信号的值大于当前值的进程数
GETZCNT:返回等待信号集中对应semnum信号的值为0的进程数
GETALL:获取信号集中所有信号的值,赋值给arg
SETALL:设置信号集中所有信号的值为arg
当获取一个信号集后,必须使用SETVAL或者SETALL来对信号进行初始化赋值。
semop
对信号执行P或者V操作。对应的结构体:
struct sembuf {
unsigned short sem_num;
short sem_op;
short sem_flag;
};
sem_num:表示信号集中的信号semnum
sem_op:可以选择一个正值,信号量需要加上此值;如果是个负值,那么信号量需要减去此值,如果不够减,根据sem_flag来选择是否要等待。特殊情况如果为0,则表示等待信号量为0。
sem_flag:IPC_NOWAIT指定当信号量不够减时不需要等待;SEM_UNDO表示在进程退出后,内核自动释放其拥有的信号量。
共享内存
#include <sys/shm.h>
int shmget(key_t key, size_t size, int flag); //根据键值key,返回共享内存id
int shmctl(int shmid, int cmd, struct shmid_ds *buf); //共享内存control操作函数,类似于ioctl
void *shmat(int shmid, const void *addr, int flag); //连接共享内存到地址addr,如果addr为0,则由内核分配一个可用的用户空间地址
int shmdt(const void *addr); //断开共享内存的连接
这些函数出错返回-1
shmget
key为IPC键值,size表示请求的共享内存大小,flag表示访问权限。
shmctl
cmd可选参数:
IPC_STAT:表示取此共享内存的shmid_ds结构体
IPC_SET:设置此共享内存的shmid_ds结构体
IPC_RMID:从系统中删除该共享存储段
共享内存专属命令:
SHM_LOCK:对共享存储段加锁,超级用户才可以执行
SHM_UNLOCK:解锁共享存储段,超级用户才可以执行
shmat
attch共享存储段到对应的addr地址,一般传入addr为0,则由系统自动分配
flag可选:
SHM_RND:对传入的绑定地址取整
SHM_RDONLY:只读操作,否则默认为读写
shmdt
解除地址与共享存储的绑定
shell命令查看
ipcs 查看IPC
ipcrm 删除IPC
ipcmk 创建IPC