信号量:是一个计数器,用于为多个进程提供对共享资源的访问。信号量是用来调协进程对共享资源的访问的。为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,通过生成并使用令牌来授权,在任一时刻只能有一个执行线程访问代码的临界区域。临界区域是指执行数据更新的代码需要独占式地执行,而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个线程在访问它。
pv操作:信号量S的物理意义,S>=0时表示某资源的可用数,s<0时其绝对值表示阻塞队列中等待该资源的进程数。P、V操作是实现进程同步与互斥的常用方法。P操作表示申请一个资源,V操作表示释放一个资源。
P操作的定义:S=S-1,若S>=0,则执行P操作的进程继续执行;若S<0,则置该进程为阻塞状态,并将其插入阻塞队列。
1.函数semget
我们使用函数semget来创建一个信号量
#include<sys/sem.h>
int semget(key_t key,int nsems,int flag);
返回值:成功返回信号量ID,失败返回-1
参数nsems:是该集合中的信号数量,如果是引用现有集合,nsms设定为0.
参数flags:权限。
2.函数semctl
#include<sys/sem.h>
int semctl(int semid,int semnum,int cmd,.......)
参数semid:是之前创建的信号量集的ID;
参数semnum:信号量中的某个成员,0和nsems之间的一个值,包括0和nsems-1
参数:cmd是命令,有10个。常用的SETVAL表示设置信号量集中的一个单独的信号量的值,IPC_RMID将信号量集从内存中删除。
第四个参数是可选的,是否使用跟cmd命令有关。它的类型是一个联合semun
union semun{
int val; //使用SETVAL设置信号量的值,就使用它赋值
struct semids_ds *buf; //为命令IPC_STAT和IPC_SET
unsigned short *array; //为命令GETALL 和SETALL
}
3函数semop
semop函数自动执行信号量集合上的操作数组,pv操作就是在这里实现的。
#inlcude<sys/sem.h>
int semop(int semid,struct sembuf semoparray[],size_t nops);
返回值:成功返回0;出错返回-1;
参数semoparray是一个指针,指向一个由sembuf结构表示的信号量数组;
struct sembuf{
unsigned short sem_num;
short sem_op;
short sem_flag;
};
undo标志:(这个标志对应sembuf结构体中sem_flag成员的SEM_UNDO位)
SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。
对集合中信号的量的操作由sem_op值确定,它可以是负数,0或正数。
sem_op为正值:进程释放占用的资源数。sem_op的值加到信号量的值上。
sem_op为负值:则表示要获取该信号量控制的资源。如果该信号量的值大于sem_op的绝对值:则从该信号量中减去sem_op的值。
如果信号量值小于sem_op的绝对值(资源不能满足要求):
1)若指定了IPC_NOWAIT,则semop出错返回
2)如果没有指定IPC_NOWAIT,进程进入休眠,直到条件满足
例程
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/sem.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *arry;
}
struct sem_buf sem_b;
int main()
{
int sem_id;
union semun sem_union;
char message='c';
sem_id=semget((key_t)1234,1,0666|IPC_CREAT); //创建一个信号量
sem_union.val=1;
semctl(sem_id,0,SETVAL,sem_union); //初始化一个信号量
for(int i=0;i<10;i++)
{
sem_b.sem_num=0; //进入临界区,p操作
sem_b.sem_op=-1;
sem_semflg=SEM_UNDO;
semop(sem_id,&sem_b,1);
printf("hello");
fflush(stdout);
sleep(1);
sem_b.sem_op=1; //离开临界区,v操作
semop(sem_id,&sem_b,1);
sleep(2);
}
semctl(sem_id, 0, IPC_RMID, sem_union); //删除信号量
return 0;
}
代码若错误,请指正,谢谢!