目录
以下三种类型的IPC合称为System V IPC:
System V IPC在访问它们的函数和内核为它们维护的信息上有一些类似点,主要包括:
下表汇总了所有System V IPC函数。
? | 信号量 | 消息队列 | 共享内存 |
---|---|---|---|
头文件 | sys/sem.h | sys/msg.h | sys/shm.h |
创建或打开IPC的函数 | semget | msgget | shmget |
控制IPC操作的函数 | semctl | msgctl | shmctl |
IPC操作函数 | semop | msgsnd msgrcv |
shmat shmdt |
三种类型的System V IPC都使用IPC键作为它们的标识,IPC键是一个key_t类型的整数,该类型在sys/types.h
中定义。
IPC键通常是由ftok
函数赋予的,该函数把一个已存在的路径名pathname和一个非0整数id组合转换成一个key_t值,即IPC键。
#include <sys/ipc.h>
//成功返回IPC键,失败返回-1
key_t ftok(const char *pathname, int id);
参数说明:
内核给每个IPC对象维护一个信息结构,即struct ipc_perm
结构,该结构及System V IPC函数经常使用的常值定义在sys/ipc.h
头文件中。
struct ipc_perm
{
uid_t uid; //owner's user id
gid_t gid; //owner's group id
uid_t cuid; //creator's group id
gid_t cgid; //creator's group id
mode_t mode; //read-write permissions
ulong_t seq; //slot usage sequence number
key_t key; //IPC key
};
创建或打开一个IPC对象使用相应的xxxget函数,它们都有两个共同的参数:
对于参数key,应用程序有两种选择:
IPC_PRIVATE
,这将保证会创建一个新的、唯一的IPC对象,但该标志不能用于打开已存在的IPC对象,只能是新建对于参数oflag,如上所述,它包含读写权限、创建或打开这两方面信息:
IPC_CREAT
标志,其含义和Posix IPC的O_CREAT一样usage : ipcs -asmq -tclup
ipcs [-s -m -q] -i id
ipcs -h for help.
usage: ipcrm [ [-q msqid] [-m shmid] [-s semid]
[-Q msgkey] [-M shmkey] [-S semkey] ... ]
我们已经知道了Posix信号量采用计数信号量,System V信号量在此基础上增加了一级复杂度,它采用计数信号量集,计数信号量集是由一个或多个计数信号量构成的集合。
对于系统中的每个信号量集,内核都维护一个struct semid_ds
信息结构,它定义在sys/sem.h
头文件中。
struct semid_ds
{
struct ipc_perm sem_perm;
struct sem *sem_base; //指向信号量集的指针
ushort sem_nsems; //信号量集中的信号量个数
time_t sem_otime; //上一次调用semop的时间
time_t sem_ctime; //创建时间或上一次以IPC_SET调用semctl的时间
};
其中,sem_base是指向信号量集的指针,信号量集中的每个成员都对应一个struct sem结构:
struct sem
{
ushort_t semval; //信号量的值
short sempid; //上一次成功调用semop,或以SETVAL、SETALL调用semctl的进程ID
ushort_t semncnt; //等待semval变为大于当前值的线程数
ushort_t semzcnt; //等待semval变为0的线程数
};
semget用于创建一个新的信号量集或打开一个已存在的信号量集。
//成功返回信号量标识符,失败返回-1
int semget(key_t key, int nsems, int oflag);
当实际操作为创建一个新的信号量集时,相应semid_ds结构中与集合中每个信号量关联的struct sem结构并不初始化,而是在以SETVAL或SETALL命令调用semctrl时初始化的。
也就是说,创建一个新的System V信号量集(semget)并将它初始化(semctl)需要两次函数调用,这是System V信号量的一个致命缺陷。
使用semget打开一个信号量集后,对其中一个或多个信号量值的操作就使用semop函数。
//成功返回0,失败返回-1
int semop(int semid, struct sembuf *ops, size_t nops);
我们只保证sembuf含有以下三个成员,不保证这三个成员的顺序,也不保证还有其他成员,因此sembuf数组元素必须采用如ops[0].sem_num = 0所示的方法进行初始化。
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_op指定的操作有三类:
sem_flg可设置的值有:0、IPC_NOWAIT、SEM_UNDO,一般使用0,对于其余两个值:
semctl函数对一个信号量集执行各种控制操作。
//成功根据cmd返回相应的非负值,失败返回-1
int semctl(int semid, int semnum, int cmd, ... /* union semun arg */);
第四个参数arg是可选的,取决于cmd的值,当需要用到该参数时,应用程序必须按如下结构定义union semun:
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
定义如下术语用于后续cmd说明:
System V支持下列cmd值,除非特殊说明,否则成功时返回值均为0。
cmd | 说 明 |
---|---|
GETVAL | 把semval的当前值作为函数返回值返回,它是一个unsigned short类型的整数 |
SETVAL | 把semval的值设为arg.val |
GETPID | 把sempid的当前值作为函数返回值返回 |
GETNCNT | 把semncnt的当前值作为函数返回值返回 |
GETZCNT | 把semzcnt的当前值作为函数返回值返回 |
GETALL | 返回信号量集内每个成员的semval值,这些值通过arg.array返回,arg.array需由调用者分配内存 |
SETALL | 设置信号量集内每个成员的semval值,这些值通过arg.array指定,此时第二个参数semnum设为0即可 |
IPC_STAT | 返回信号量集当前的semid_ds结构,该结构通过arg.buf返回,arg.buf需由调用者分配内存,可以用该命令获得信号量集中的信号量个数 |
IPC_SET | 设置信号量集对应semid_ds结构中的sem_perm.uid、sem_perm.gid和sem_perm.mode,设置的值来自arg.buf |
IPC_RMID | 删除由semid指定的信号量集,此时第二个参数semnum设为0即可 |
其中,前五个命令针对的都是信号量集semid中由semnum指定的信号量。
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
#define SEM_MODE 0666
/*
#define SEM_MODE_OWNER SEM_R | SEM_A
#define SEM_MODE_GROUP (SEM_R >> 3) | (SEM_A >> 3)
#define SEM_MODE_OTHER (SEM_R >> 6) | (SEM_A >> 6)
#define SEM_MODE (SEM_MODE_OWNER | SEM_MODE_GROUP | SEM_MODE_OTHER)
*/
int main()
{
int nsems = 3;
int oflag = IPC_CREAT | SEM_MODE;
key_t key = ftok(FTOK_FILE, FTOK_ID);
int semid = semget(key, nsems, oflag);
if (semid >= 0)
{
printf("semcreate success, semid = %d\n", semid);
}
return 0;
}
这里遇到个问题,SEM_MODE一开始是按注释部分定义的,但man semget给出的三个头文件都已经包含了,编译时却报错,提示SEM_R和SEM_A未定义,不知道为什么,只能用0666定义了。
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
int main()
{
key_t key = ftok(FTOK_FILE, FTOK_ID);
int semid = semget(key, 0, 0);
semctl(semid, 0, IPC_RMID);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
unsigned short *semvals;
union semun arg;
struct semid_ds seminfo;
int i;
/* 打开semcreate创建的信号量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 获得信号量集中的信号量个数 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 设置信号量集中每个信号量的值 */
semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));
for (i = 0; i < nsems; i++)
{
semvals[i] = atoi(argv[i + 1]); //通过命令行参数分别指定集合中每个信号量的值
}
arg.array = semvals;
semctl(semid, 0, SETALL, arg);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
unsigned short *semvals;
union semun arg;
struct semid_ds seminfo;
int i;
/* 打开semcreate创建的信号量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 获得信号量集中的信号量个数 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 获得信号量集中每个信号量的值 */
semvals = (unsigned short *)calloc(nsems, sizeof(unsigned short));
arg.array = semvals;
semctl(semid, 0, GETALL, arg);
for (i = 0; i < nsems; i++)
{
printf("semvals[%d] = %d\n", i, semvals[i]);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define FTOK_FILE "/home/delphi/ftok.file"
#define FTOK_ID 1
union semun
{
int val; /* Value for SETVAL */
struct semid_ds *buf; /* Buffer for IPC_STAT, IPC_SET */
unsigned short *array; /* Array for GETALL, SETALL */
struct seminfo *__buf; /* Buffer for IPC_INFO (Linux-specific) */
};
int main(int argc, char **argv)
{
key_t key;
int semid;
int nsems;
union semun arg;
struct semid_ds seminfo;
struct sembuf *semops;
int i;
/* 打开semcreate创建的信号量集 */
key = ftok(FTOK_FILE, FTOK_ID);
semid = semget(key, 0, 0);
/* 获得信号量集中的信号量个数 */
arg.buf = &seminfo;
semctl(semid, 0, IPC_STAT, arg);
nsems = arg.buf->sem_nsems;
/* 对信号量集中的所有信号量进行相同的semop操作 */
semops = (struct sembuf *)calloc(nsems, sizeof(struct sembuf));
for (i = 0; i < nsems; i++)
{
semops[i].sem_num = i;
semops[i].sem_op = atoi(argv[1]); //操作类型由命令行参数指定,>0, 0, <0
semops[i].sem_flg = 0;
}
semop(semid, semops, nsems);
return 0;
}
原文:https://www.cnblogs.com/songhe364826110/p/11537835.html