1概述
一般信号量分为二值信号量和计数信号量。
二值信号量:其值或为0或为1的信号量。若资源被锁住则信号量值为0,若资源可用则信号量为1.
计数信号量:其值在0和某个限制值之间的信号量。使用这些信号量在生产者-消费者问题中统计资源,信号量就是可用资源数。
Posix信号量指的是单个计数信号量,而System V信号量指的是计数信号量集。
计数信号量集:一个或多个信号量构成一个集合,其中每个信号量都是计数信号量。每个集合的信号量数存在一个限制,一般在25个数量级上。
对于系统中的每个信号量集,内核维护一个semid_ds信息结构,定义在<sys/sem.h>头文件: struct semid_ds { struct ipc_perm sem_perm; struct sem *sem_base; unsigned short sem_nsems;/*信号量数目*/ time_t sem_otime; time_t sem_ctime; }; sem结构是内核用于维护某个给定信号量的一组值的内部数据结构。一个信号量集的每个成员由如下这个结构描述: struct sem { unsigned short semval;//信号量值 pid_t sempid;//对信号量值执行最后一个操作的进程的ID unsigned short semncnt;//等待信号量值增加的进程数计数 unsigned short semzcnd;//等待信号量值为0的进程数计数 }; |
2函数
头文件 | #include <sys/sem.h> |
函数 | int semget(key_t key, int nsems, int flag); |
参数 | key:键值 nsems:该信号量集中的信号量数。如果是创建新集合(一般在服务器进程中),则必须指定nsems;如果引用一个现存的集合(一个客户进程),则将nsems指定为0. flag:标志位,包括读写权限位(SEM_R和SEM_A)和创建打开标志位(IPC_CREAT、IPC_CREAT|IPC_EXCL) 当只有IPC_CREAT时,若不存在则创建信号量集并返回其ID;若存在,则返回其ID。 当为IPC_CREAT | IPC_EXCL时,若信号量集存在则返回-1,若不存在则创建。 |
返回值 | 若成功则返回信号量ID,若出错则返回-1 |
功能 | 创建一个信号量集或访问一个已存在的信号量集 |
头文件 | #include <sys/sem.h> |
函数 | int semctl(int semid, int semnum, int cmd, ../* union semun arg */); |
参数 | semid:信号量集标识符 semnum:信号量集中的一个信号量成员,值为0~nsems-1之间。 cmd:有10种命令,IPC_STAT、IPC_SET、IPC_RMID、GETVAL、SETVAL、…….. arg:该参数是可选的,是多个特定命令参数的联合。 union semun{ int var; struct semid_ds *buf; unsigned short *array; }; |
返回值 | 除GETALL以外的所有GET命令都返回相应的值。其他命令返回0。出错返回-1. |
功能 | 信号量多种操作 |
头文件 | #include <sys/sem.h> |
函数 | int semop(int semid, struct sembuf semoparray[], size_t nops); |
参数 | semid:信号量集标识符 semoparray:操作数组 struct sembuf{ unsigned short sem_num;//信号量成员,值为0~nsems-1 short sem_op;//指定成员的操作,值为负数、0、正数 short sem_flg;//0、IPC_NOWAIT、SEM_UNDO }; nops:数组中操作的数量(元素个数) |
返回值 | 若成功则返回0,若出错则返回-1 |
功能 | 自动执行信号量集合上的操作数组 |
3应用
信号量集示例:子进程打印字母c,父进程打印字母p
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/wait.h>
int semphore_p(int);
int semphore_v(int);
union semun
{
int val;/* Value for SETVAL */
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int main(void)
{
key_t key;
int semid;
pid_t pid;
int i;
int nsems = 1;
union semun semval;
/*创建键值*/
if((key = ftok("15semaphore.c", 0)) == -1)
{
printf("ftok error\n");
exit(1);
}
/*创建信号量集*/
if((semid = semget(key, nsems, IPC_CREAT)) == -1)
{
printf("semget error\n");
exit(1);
}
/*设置信号量的值*/
semval.val = nsems;
if(semctl(semid, 0, SETVAL, semval) == -1)
{
printf("semctl error\n");
exit(1);
}
if((pid = fork()) < 0)
{
printf("fork error\n");
exit(1);
}
else if(pid == 0)
{
/*子进程:信号量加锁*/
if(semphore_p(semid) == -1)
{
printf("child semphore_p error\n");
exit(1);
}
printf("child start...\n");
for (i = 0; i < 10; i++)
{
printf("%c", 'C');
fflush(stdout);
sleep(1);
}
printf("\nchild end...\n");
/*子进程:信号量解锁*/
if(semphore_v(semid) == -1)
{
printf("child semphore_v error\n");
exit(1);
}
exit(0);
}
/*父进程:信号量加锁*/
if(semphore_p(semid) == -1)
{
printf("parent semphore_p error\n");
exit(1);
}
printf("parent start...\n");
for (i = 0; i < 10; i++)
{
printf("%c", 'P');
fflush(stdout);
sleep(1);
}
printf("\nparent end...\n");
/*父进程:信号量解锁*/
if(semphore_v(semid) == -1)
{
printf("parent semphore_v error\n");
exit(1);
}
if(semctl(semid,0,IPC_RMID,NULL) == -1)
{
printf("semctl rmid error\n");
exit(1);
}
waitpid(pid, NULL, 0);
exit(0);
}
/*P操作,申请一个资源单位*/
int semphore_p(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;/*值为0,代表信号量集的第一个元素*/
sem_buf.sem_op = -1;
sem_buf.sem_flg = SEM_UNDO;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}
/*V操作,释放一个资源单位*/
int semphore_v(int semid)
{
struct sembuf sem_buf;
sem_buf.sem_num = 0;
sem_buf.sem_op = 1;
sem_buf.sem_flg = IPC_NOWAIT;
if(semop(semid, &sem_buf, 1) == -1)
return -1;
return 0;
}