信号量是一个计数器,用于多进程或者多线程之间的数据同步,最常用的是二进制信号量。
为了获取资源,进程需要进行如下操作:
1 测试控制该资源的信号量
2 若信号量的值为正,表明进程可以使用此资源。进程将信号量的值减1
3 若信号量的值为0,则进程进入休眠状态,直至信号量大于零,进程被唤醒。
在linux中,信号量的测试和-1操作是原子性的,在内核中实现。
下面就来介绍XSI信号量的操作,主要用到了三个函数
1 int semget(key_t key,int nsems,int flag);
2 int semctl(int semid,int semnum,int cmd,union semun arg);
3 int semop(int semid,struct sembuf semoparray[], int nops);
下面来逐一介绍:
int semget(key_t key,int nsems,int flag) 的功能是创建一个信号量的集合。
参数key是信号量的标识,当在一个进程中创建了信号量之后,可以使用这个key在别的进程中打开。
参数 nsems表示信号量集合中包含的信号量的个数。
参数 flags为创建信号量的标志,有IPC_CREAT等,创建的时候,需要制定IPC_CREAT标志。
此函式也可以用来打开一个已经存在的信号量,此时key为此信号量创建时的key,nsems和flag填0即可。
int semctl(int semid,int semnum,int cmd,union semun arg),这个函数用来对信号量进行控制。
参数semid唯一标识系统中已经创建的信号量
参数semnum表示要操作的信号量集合中信号量的序号,序号从0开始,到semnum - 1.
参数cmd表示需要进行的操作命令,共有十个命令,常用的有SETVAL,GETVAL
参数arg的作用视cmd而定,相信的命令情况参考man手册。
int semop(int semid,struct sembuf semoparray[], int nops) 可以获取或者释放一个信号量
参数semid是信号量的标识id,主要的参数是semoparray,其类型是:
struct sembuf
{
unsigned short sem_num; //要操作的信号量在信号量集合中的序号
short sem_op; //1表示释放信号量,-1表示获取资源
short sem_flg; //IPC_NOWAIT,SEM_UNDO,若设为IPC_NOWAIT当不能获取资源时不阻塞,返回EAGAIN
};
参数nops表示struct sembuf数组中元素的个数
光说不练,印象不够深刻,实践是学习编程的最好的方法,下面就通过一个小程序来演示一下信号量的使用,程序分为两部分,一个服务端,一个客户端,服务端创建一个信号量,获取信号量资源,休眠20秒钟后释放信号量。客户端启动时去获取服务端创建的信号量,直到服务端释放后才能获取。
---------------------------------------------------------------------------------------------------------------
semsvr.cpp
-----------------------------------------------------------------------------------------------------------------
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
#include <iostream>
using namespace std;
union semun {
int val;
struct semid_ds *buf;
unsigned short *array;
} arg;
//创建一个信号量集合
int Create_Sem(int key)
{
int semid = 0;
if((semid = semget(key,1,IPC_CREAT)) == -1)
{
cout << "create sem failed" << endl;
return -1;
}
cout << "create sem success" << endl;
union semun sem;
sem.val = 1;
semctl(semid,0,SETVAL,sem); //初始化信号量的值为1,表明只允许一个进程拥有这个信号量
cout << "create compelte" << endl;
return semid;
}
//打开已经存在的信号量
int Open_Sem(int key)
{
return semget(key,0,0);
}
//获取信号量资源
int Lock_Sem(int semid)
{
struct sembuf buff;
buff.sem_num = 0;
buff.sem_op = -1;
return semop(semid,&buff,1);
}
//释放信号量资源
int Release_Sem(int semid)
{
struct sembuf buff;
buff.sem_num = 0;
buff.sem_op = 1;
return semop(semid,&buff,1);
}
//删除信号量
int Delete_Sem(int semid)
{
return semctl(semid,0,IPC_RMID,0);
}
int main()
{
int semid = Create_Sem(12345);
if(semid == -1)
{
cout << "create sem failed" << endl;
return 0;
}
if(Lock_Sem(semid) == -1)
{
cout << "lock sem failed" << endl;
return 0;
}
cout << "lock sem success" << endl;
sleep(20);
Release_Sem(semid);
cout << "release sem success" << endl;
return 0;
}
----------------------------------------------------------------------------------------------------------------
semcli.cpp
-----------------------------------------------------------------------------------------------------------------
int main()
{
int semid = Open_Sem(12345);
if(semid == -1)
{
cout << "open sem failed" << endl;
return 0;
}
if(Lock_Sem(semid) == -1)
{
cout << "lock sem failed" << endl;
return 0;
}
cout << "lock success" << endl;
Delete_Sem(semid);
return 0;
}