目录 1. System V IPC 概述 IPC键和ftok函数 ipc_perm结构 创建与打开IPC对象 ipcs和ipcrm命令 2. System V信号量 计数信号量集 semget semop semctl 3. 测试程序 代码实现 semcreate.c semrmid.c semsetvalues.c semgetvalues.c semops.c 运行测试 1. System V IPC 概述 以下三种类型的IPC合称为System V IPC: System V信号量 System V消息队列 System V共享内存 System V IPC在访问它们的函数和内核为它们维护的信息上有一些类似点,主要包括: IPC键和ftok函数 ipc_perm结构 创建或打开时指定的用户访问权限 ipcs和ipcrm命令 下表汇总了所有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 IPC键和ftok函数 三种类型的System V IPC都使用IPC键作为它们的标识,IPC键是一个key_t类型的整数,该类型在sys/types.h中定义。 IPC键通常是由ftok函数赋予的,该函数把一个已存在的路径名pathname和一个非0整数id组合转换成一个key_t值,即IPC键。 #include //成功返回IPC键,失败返回-1 key_t ftok(const char *pathname, int id); 参数说明: pathname在是程序运行期间必须稳定存在,不能反复创建与删除 id不能为0,可以是正数或者负数 ipc_perm结构 内核给每个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对象 创建或打开一个IPC对象使用相应的xxxget函数,它们都有两个共同的参数: 参数key,key_t类型的IPC键 参数oflag,用于指定IPC对象的读写权限(ipc_perm.mode),并选择是创建一个新的IPC对象还是打开一个已存在的IPC对象 对于参数key,应用程序有两种选择: 调用ftok,给它传pathname和id 指定key为IPC_PRIVATE,这将保证会创建一个新的、唯一的IPC对象,但该标志不能用于打开已存在的IPC对象,只能是新建 对于参数oflag,如上所述,它包含读写权限、创建或打开这两方面信息: 可以指定IPC_CREAT标志,其含义和Posix IPC的O_CREAT一样 还可以设置为下表所示的常值来指定读写权限 ipcs和ipcrm命令 由于System V IPC的三种类型不是以文件系统路径名标识的,因此无法使用ls和rm命令查看与删除它们 ipcs和ipcrm分别用于查看与删除系统中的System V IPC 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] ... ] 2. System V信号量 计数信号量集 我们已经知道了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 semget用于创建一个新的信号量集或打开一个已存在的信号量集。 nsems参数指定集合中的信号量个数,如果是打开一个已存在的信号量集,就把该参数设为0 oflag参数可设置为IPC_CREAT,以及SEM_R和SEM_A指定的访问权限,如果是打开一个已存在的信号量集,就把该参数设为0 成功时返回一个称为信号量标识符的非负整数,semop和semctrl函数将使用它 //成功返回信号量标识符,失败返回-1 int semget(key_t key, int nsems, int oflag); 当实际操作为创建一个新的信号量集时,相应semid_ds结构中与集合中每个信号量关联的struct sem结构并不初始化,而是在以SETVAL或SETALL命令调用semctrl时初始化的。 也就是说,创建一个新的System V信号量集(semget)并将它初始化(semctl)需要两次函数调用,这是System V信号量的一个致命缺陷。 semop 使用semget打开一个信号量集后,对其中一个或多个信号量值的操作就使用semop函数。 //成功返回0,失败返回-1 int semop(int semid, struct sembuf *ops, size_t nops); semid指定待操作的信号量集 nops为集合中的信号量个数 ops指向一个struct sembuf类型的结构数组,该数组中的每个元素给目标信号量集中某个特定的信号量指定sem_op操作,这个特定的信号量由sem_num指定 我们只保证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_op > 0:操作内容为semval += sem_op,这对应于释放某个信号量控制的资源 sem_op = 0:调用者阻塞等待直到semval变为0 sem_op < 0:调用者阻塞等待直到semval >= abs(sem_op),调用者阻塞等待直到semval>这对应于分配资源 sem_flg可设置的值有:0、IPC_NOWAIT、SEM_UNDO,一般使用0,对于其余两个值: IPC_NOWAIT用于给信号量集中某个特定信号量设置非阻塞标志 SEM_UNDO为System V信号量特有的复旧机制 semctl semctl函数对一个信号量集执行各种控制操作。 //成功根据cmd返回相应的非负值,失败返回-1 int semctl(int semid, int semnum, int cmd, ... /* union semun arg */); semid指定待控制的信号量集 semnum指定信号量集内的某个成员,仅在cmd为GETVAL、SETVAL、GETNCNT、GETZCNT和GETPID时使用 cmd指定控制命令 arg是可选的,取决于cmd的具体值 第四个参数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说明: semval:信号量的当前值 sempid:上一次成功调用semop,或以SETVAL、SETALL调用semctl的进程ID semncnt:等待semval变为大于当前值的线程数 semzcnt:等待semval变为0的线程数 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指定的信号量。 3. 测试程序 代码实现 semcreate.c #include #include #include #include #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定义了。 semrmid.c #include #include #include #include #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; } semsetvalues.c #include #include #include #include #include #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; } semgetvalues.c #include #include #include #include #include #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; } semops.c #include #include #include #include #include #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; } 运行测试 运行semcreate,通过运行前后的ipcs -s,可以确认信号量集创建成功 运行semsetvalues,将三个信号量的值分别设为1、2、3 然后运行semgetvalues,打印信息和我们设置的值一致,符合预期 运行semops,并指定sem_op > 0 然后运行semgetvalues,打印信息显示每个信号量的值都增加了1,符合预期 运行semrmid,通过运行前后的ipcs -s,可以确认信号量集已经从系统删除 https://www.cnblogs.com/songhe364826110/p/11537835.html