只用于多进程间的并发控制
一个信息量集合(信号量集 或 信息量数组)中顺序存储着多个信号量
相关接口函数:
实际项目,直接调用semop函数来作为某个或某几个信号量的PV操作函数会很不方便,因此会对信号量集合的接口进行二次封装,封装方案:
1. 项目中用到几个信号量就创建几个信号量集合,每个信号量集合中仅包含一个信号量,然后基于每个信号量集合封装相应的初始化、P、V操作函数
2. 项目中创建一个信号量集合,该集合包含所有需要的信号量,然后针对每个信号量封装它的初始化、P、V操作函数
一般建议项目中使用的信号量比较少时用第1方案,否则使用第2方案
以下采用第2方案:
#define SEM_NUM ?union semun
{int val;unsigned short *pval;
};// create_and_init_sem_set() 创建并初始化
int create_and_init_sem_set(const char *pathname,int no,unsigned short *pallval,int semnum);// sem_p_by_index() p操作
int sem_p_by_index(int semid,int index,int shrnum);// sem_v_by_index() v操作
int sem_v_by_index(int semid,int index,int shrnum);/*针对集合中下标为0的信号量编写PV操作带参宏*/
#define sem_0_p(semid,shrnum) sem_p_by_index(semid,0,shrnum)
#define sem_0_v(semid,shrnum) sem_v_by_index(semid,0,shrnum)/*针对集合中下标为1的信号量编写PV操作带参宏*/
#define sem_1_p(semid,shrnum) sem_p_by_index(semid,1,shrnum)
#define sem_1_v(semid,shrnum) sem_v_by_index(semid,1,shrnum)
。。。。。。
/*针对集合中下标为x的信号量编写PV操作带参宏*/
#define sem_x_p(semid,shrnum) sem_p_by_index(semid,x,shrnum)
#define sem_x_v(semid,shrnum) sem_v_by_index(semid,x,shrnum)
示例代码:
my_semset.h
#ifndef MY_SEMSET_H
#define MY_SEMSET_H#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define MY_SEM_NUM 1union semun{ // 信号量集合int val;unsigned short *pvals;
};// create_and_init_sem_set() 创建并初始化
int create_and_init_semset(const char *pathname,int no,unsigned short *pallval,int valnum);// sem_p_by_index() p操作
int sem_p_by_index(int semid,int index,int shrnum);// sem_v_by_index() v操作
int sem_v_by_index(int semid,int index,int shrnum);#define sem_0_p(semid,shrnum) sem_p_by_index(semid,0,shrnum)
#define sem_0_v(semid,shrnum) sem_v_by_index(semid,0,shrnum)#if 0
#define sem_1_p(semid,shrnum) sem_p_by_index(semid,1,shrnum)
#define sem_1_v(semid,shrnum) sem_v_by_index(semid,1,shrnum)#define sem_2_p(semid,shrnum) sem_p_by_index(semid,2,shrnum)
#define sem_2_v(semid,shrnum) sem_v_by_index(semid,2,shrnum)
#endif#endif
my_semset.c
#include "my_semset.h"int create_and_init_semset(const char *pathname,int no,unsigned short *pallval,int valnum){key_t key;int semid = -1;union semun un;if(valnum < MY_SEM_NUM){printf("The valnum is too small\n");return -1;}key = ftok(pathname,no); // ftok() 获取key值semid = semget(key,MY_SEM_NUM,IPC_CREAT | IPC_EXCL | 0666); // semget() 获取idif(semid < 0){ // semset is exist 已经存在就不用初始化了直接获取semid = semget(key,MY_SEM_NUM,IPC_CREAT | 0666);}else{ // semset is not exist 不存在需要先创建再调用semctl()初始化un.pvals = pallval;semctl(semid,0,SETALL,un); // semctl()设置初值}return semid;
}int sem_p_by_index(int semid,int index,int shrnum){struct sembuf op;if(shrnum <= 0){printf("shrnum = %d is invalid\n",shrnum);return -1;}op.sem_num = index;op.sem_op = -shrnum;op.sem_flg = SEM_UNDO;return semop(semid,&op,1);
}int sem_v_by_index(int semid,int index,int shrnum){struct sembuf op;if(shrnum <= 0){printf("shrnum = %d is invalid\n",shrnum);return -1;}op.sem_num = index;op.sem_op = shrnum;op.sem_flg = 0;return semop(semid,&op,1);
}
input.c
#include "../../semset/my_semset.h"
#include <sys/ipc.h>
#include <sys/shm.h>char *input_string(char *buf,int size);int main(int argc, char *argv[]){unsigned short val = 0;int semid = create_and_init_semset("./input.c",8,&val,1);key_t shmkey;int shmid = -1;char *pshm = NULL;shmkey = ftok("./input.c",88); // 获取input.c的key值shmid = shmget(shmkey,40,IPC_CREAT | 0666); // shmget() 创建并打开一个共享内存,获取idpshm = shmat(shmid,NULL,0); // shmat() 获取指定内存块的首地址while(1){printf("Please input a string:\n");input_string(pshm,40); // 读到共享内存中sem_0_v(semid,1); // v操作if(strcmp(pshm,"byebye") == 0) break; // 出口}shmdt(pshm); //shmdt()pshm = NULL;return 0;
}
output.c
#include "../../semset/my_semset.h"
#include <sys/ipc.h>
#include <sys/shm.h>char *input_string(char *buf,int size);int main(int argc, char *argv[]){unsigned short val = 0;int semid = create_and_init_semset("./input.c",8,&val,1); // create_and_innit()key_t shmkey;int shmid = -1;char *pshm = NULL;shmkey = ftok("./input.c",88); // ftok()shmid = shmget(shmkey,40,IPC_CREAT | 0666); // shmget() 创建并打开一个共享内存pshm = shmat(shmid,NULL,0); // shmat() 获取指定内存块的首地址while(1){sem_0_p(semid,1); // p操作if(strcmp(pshm,"byebye") == 0) break; // 出口else printf("the input string is %s\n",pshm);}shmdt(pshm); // shmdt()pshm = NULL;return 0;
}
input_string.c (读取 size 个数据存到 buf 里返回)
#include <stdio.h>
#include <string.h>char *input_string(char *buf,int size){int len = 0;fgets(buf,size,stdin);len = strlen(buf);if(*(buf + len - 1) == '\n') *(buf + len - 1) = '\0';else while(getchar() != '\n'){}return buf;
}