为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个进/线程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。
信号量是一个特殊的变量,程序对其访问都是原子操作,且只允许对它进行等待(即P(信号变量))和释放(即V(信号变量))信息操作。最简单的信号量是只能取0和1的变量,这也是信号量最常见的一种形式,叫做二进制信号量。可以取多个正整数的信号量被称为通用信号量。
P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。
semget函数:
作用是创建一个新信号量或取得一个已有信号量
int semget(key_t key, int num_sems, int sem_flags);
成功返回一个相应信号标识符(非零),失败返回-1.
semop函数:
作用是改变信号量的值
int semop(int sem_id, struct sembuf *sem_opa, size_t num_sem_ops);
struct sembuf{
short sem_num;//除非使用一组信号量,否则它为0
short sem_op;//信号量在一次操作中需要改变的数据,通常是两个数,
//一个是-1,即P(等待)操作,
//一个是+1,即V(释放信号)操作。
short sem_flg;//通常为SEM_UNDO,使操作系统跟踪信号, 并在进程没有释放该信号量而终止时,操作系统释放信号量
};
sem_op值的意义:
如果其值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值大于或等于sem_op内含值的绝对值,通常用于获取资源的使用权(P操作)。
如果其值为正数,该值会加到现有的信号内含值中,通常用于释放所控资源的使用权(V操作);
semctl函数:
该函数用来直接控制信号量信息
int semctl(int sem_id, int sem_num, int command, …);
command命令主要是两种:
SETVAL:用来把信号量初始化为一个已知的值。
IPC_RMID:用于删除一个已经无需继续使用的信号量标识符。
使用semaphore.h:
#include<iostream>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<pthread.h>
#include<semaphore.h>
using namespace std;
sem_t sema;
sem_t semb;
sem_t semc;
void* funa(void* arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&sema);//ps1;
cout << "A" << endl;
sem_post(&semb);//vs2
}
}
void* funb(void* arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&semb);//psb;
cout << "B" << endl;
sem_post(&semc);//vsc
}
}
void * func(void *arg)
{
for(int i=0;i<5;i++)
{
sem_wait(&semc);
cout << "C" << endl;
sem_post(&sema);
}
}
int main()
{
sem_init(&sema,0,1);
sem_init(&semb,0,0);
sem_init(&semc,0,0);
pthread_t id1,id2,id3;
pthread_create(&id1,NULL,funa,NULL);
pthread_create(&id2,NULL,funb,NULL);
pthread_create(&id3,NULL,func,NULL);
pthread_join(id1,NULL);
pthread_join(id2,NULL);
pthread_join(id3,NULL);
sem_destroy(&sema);
sem_destroy(&semb);
sem_destroy(&semc);
exit(0);
}