什么是信号量
信号量的本质是一种数据操作锁,它本身不具有数据交换的功能,而是通过控制其他的通信资源(文件,外部设备)来实现进程间通信,它本身只是一种外部资源的标识。信号量在此过程中负责数据操作的互斥、同步等功能。
当请求一个使用信号量来表示的资源时,进程需要先读取信号量的值来判断资源是否可用。大于0,资源可以请求,等于0,无资源可用,进程会进入睡眠状态直至资源可用。 当进程不再使用一个信号量控制的共享资源时,信号量的值+1,对信号量的值进行的增减操作均为原子操作,这是由于信号量主要的作用是维护资源的互斥或多进程的同步访问。而在信号量的创建及初始化上,不能保证操作均为原子性。
信号量的工作原理
由于信号量只能进行两种操作等待和发送信号,即P(sv)和V(sv)
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂 起,就给它加1。
相关函数
#include <sys/shm.h>
int semget( key_t key, int nsems, int semflg);
函数中参数key用来变换成一个标识符,每一个IPC对象与一个key相对应。
nsems:这个参数表示你要创建的信号量集合中的信号量的个数。信号量只能以集合的形式创建。
semflg:同时使用IPC_CREAT和IPC_EXCL则会创建一个新的信号量集合。若已经存在的话则返回-1。单独使用IPC_CREAT的话会返回一个新的或者已经存在的信号量集合。
int semop(int semid,struct sembuf *sops,unsigned nsops);
int semtimedop(int semid, struct sembuf *sops, unsigned nsops, struct timespec *timeout);
struct sembuf *sops;
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
参数semid是一个通过semget函数返回的一个信号量标识符,参数nops标明了参数sops所指向数组中的元素个数。参数sops是一个struct sembuf结构类型的数组指针。
sem_op:表示该信号量的操作(P操作还是V操作)。如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op的绝对值。通常用于获取资源的使用权。
sem_flg:信号操作标志,它的取值有两种。IPC_NOWAIT和SEM_UNDO。
IPC_NOWAIT:对信号量的操作不能满足时,semop()不会阻塞,而是立即返回,同时设定错误信息。
SEM_UNDO:程序结束时(不管是正常还是不正常),保证信号值会被设定semop()调用之前的值。这样做的目的在于避免程序在异常的情况下结束未将锁定的资源解锁(死锁),造成资源永远锁定。
int semctl(int semid,int semnum,int cmd,...);
semctl()在semid标识的信号量集合上,或者该信号量集合上第semnum个信号量上执行cmd指定的控制命令。根据cmd不同,这个函数有三个或四个参数,当有第四个参数时,第四个参数的类型是union。
union semun
{
int val; //使用的值
struct semid_ds *buf; //IPC_STAT、IPC_SET使用缓存区
unsigned short *array; //GETALL、SETALL使用的缓存区
struct seminfo *__buf; //IPC_INFO(linux特有)使用缓存区
};
命令:
ipcs -s //查看创建的信号量集合的个数
ipcrm -s semid //删除一个信号量集合
#include "sem.h"
int create_sem(int semset_num)
{
int create_flag = IPC_CREAT | IPC_EXCL;
key_t k = ftok(KEY_PATH, PROJECT_ID);
return semget(k, semset_num, create_flag);
}
int get_sem()
{
key_t k = ftok(KEY_PATH, PROJECT_ID);
return semget(k, 0, 0);
}
static int op_sem(int sem_id, int op, int which)
{
struct sembuf sem;
memset(&sem, '\0', sizeof(sme));
sem.sem_num = which;
sem.sem_op = op;
sem.sem_flg = 0;
return semop(sem_id, &sem, 1);
}
int init_sem(int sem_id, int which);
{
semun_t_semun;
_semun.val = 1;
int ret = semctl(sem_id, which, SETVAL, _semun);
if(ret == -1)
{
printf("init sem error:errno code is:%d\n", errno);
return ret;
}
else
{
printf("init sem success:errno code is:%d\n", errno);
return ret;
}
}
int sem_p(int sem_id,int which)
{
int ret = op_sem(sem_id, -1, which);
if(ret == 0)
{
printf("P opertor is success! errrno code is:%d\n", errno);
}
else
{
printf("P opertor id failed! errrno code is:%d\n", errno);
}
return ret;
}
int sem_v(int sem_id, int which)
{
int ret = op_sem(sem_id, 1, which);
if(ret == 0)
{
printf("V opertor is success! errno code is:%d\n", errno);
}
else
{
printf("V opertor is failed! errno code is:%d\n", errno);
}
return ret;
}
int destroy_sem(int sem_id)
{
int ret = semctl(sem_id, 0, IPC_RMID, NULL);
if(ret == -1)
{
printf("destroy sem error\n");
return ret;
}
else
{
printf("destroy sem success, errno code is:%d\n", errno);
return ret;
}
}
int show_sem_val(int sem_id, int sem_num)
{
semun_t_semun;
unsigned short *_sem_list = (unsigned short *)malloc(sizeof(unsigned short)*sem_num);
if(_sem_list == NULL)
{
printf("malloc error, errno code is:%d\n", errno);
return 1;
}
memset(_sem_list, '\0', sizeof(unsigned short)*sem_num);
_semun.array = sem_list;
int ret = semctl(sem_id, 0, GETALL,_semun);
if(ret == -1)
{
printf(“get sem error: errno code is:%d\n”, errno);
}
else
{
int i = 0;
for(i=0; i<sem_num; i++)
{
printf("sem[%d] is:%d\n", i, _semun.array[i]);
}
free(_sem_list);
_semun.array = NULL;
return ret;
}
}