1.
信号量本身并不具备数据交换的功能,它本质只是一个数据操作锁,是通过控制临界资源来实现进程间通信的,它在此过程中实现数据的同步与互斥等功能。
每个信号量的都维护着一个如下接构体:
struct semid_ds {
struct ipc_perm sem_perm; /* 信号量集的操作许可权限 */
struct sem *sem_base; /* 某个信号量sem结构数组的指针,当前信号量集
中的每个信号量对应其中一个数组元素 */
ushort sem_nsems; /* sem_base 数组的个数 */
time_t sem_otime; /* 最后一次成功修改信号量数组的时间 */
time_t sem_ctime; /* 成功创建时间 */
};
struct sem {
ushort semval; /* 信号量的当前值 */
short sempid; /* 最后一次返回该信号量的进程ID 号 */
ushort semncnt; /* 等待semval大于当前值的进程个数 */
ushort semzcnt; /* 等待semval变成0的进程个数 */
};
2.
信号量工作时有两个操作,分别是p操作,v操作,p(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行(申请资源)v(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.(释放资源)
使用示例:
void p_func(int id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(id,&sb,1);
}
void v_func(int id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
semop(id,&sb,1);
}
3. 相关函数
semget:int semget(key_t key, int nsems, int semflg)
- key参数:可以用ftok函数获取。
- nsems参数:当nsems>0 : 创建一个信的信号量集,指定集合中信号量的数量,一旦创建就不更 改。当nsems==0 : 访问一个已存在的集合。
- semflg参数:IPC_CREATE 和 IPC_EXCL
- 返回值:返回一个信号量标识符的整数,semop和semctl函数将使用它,失败返回-1。
创建成功后信号量结构被设置:
.sem_perm 的uid和gid成员被设置成的调用进程的有效用户ID和有效组ID
.oflag 参数中的读写权限位存入sem_perm.mode
.sem_otime 被置为0,sem_ctime被设置为当前时间
.sem_nsems 被置为nsems参数的值
semop:int semop(int semid, struct sembuf* sops, size_t nsops)
- semid参数:semget产生的信号量标识符。
- struct sembuf:
struct sembuf {
short sem_num; // 要操作的信号量在信号量集里的编号,
short sem_op; // 信号量操作(一般是 v:1 或 p:-1)
short sem_flg; // 操作表示符(0 或 SEM_UNDO 或 IPC_NOWAIT)
}; (这样做应该是为了减少semop中的参数个数)
IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
IPC_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
- nsops参数:sops所指向的数组中的sembuf结构体的个数(表示要操作的信号个数)。
- 返回值:成功返回0,失败返回-1。
semctl:int semctl(int semid, int semnum, int cmd, . . . )
-
semid参数:semget产生的信号量标识符。
-
semnum参数:对在信号集中的第semnum个信号进行cmd操作(从0开始)。
-
cmd参数:semctl可能有三个或四个参数,这取决于cmd。
cmd:
IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
IPC_RMID将信号量集从系统中删除
GETALL用于读取信号量集中的所有信号量的值,存于semnu的array中
SETALL 设置所指定的信号量集的每个成员semval的值
GETPID返回最后一个执行semop操作的进程的PID。
SETVAL把的val数据成员设置为当前资源数
GETVAL把semval中的当前值作为函数的返回,即现有的资源数,返回值为非负数。 -
第四个参数:为一个用户自定义的联合体:
union semun
{
int val; // cmd == SETVAL
struct semid_ds *buf // cmd == IPC_SET或者 cmd == IPC_STAT
unsigned short *array; // cmd == SETALL,或 cmd = GETALL
struct seminfo *__buf; // cmd == IPC_INFO只有在linux下才有。
};
- 返回值:错误时返回-1,成功时的返回值根据cmd的结果出来相应的结果。
用法示例:
#include <iostream>
#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/sem.h>
using namespace std;
union semun{
int val;
};
void p_func(int id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = -1;
sb.sem_flg = 0;
semop(id,&sb,1);
}
void v_func(int id)
{
struct sembuf sb;
sb.sem_num = 0;
sb.sem_op = 1;
sb.sem_flg = 0;
semop(id,&sb,1);
}
int main(void)
{
int Sem_id = semget(123,1,IPC_CREAT|0644);
if(Sem_id < 0)
{
cout << "semget failed" << endl;
exit(1);
}
else
{
cout << "semget successed" << endl;
union semun su;
su.val = 5;
cout << "before sem num: " << semctl(Sem_id,0,GETVAL,0) << endl;
semctl(Sem_id,0,SETVAL,su);
cout << "after sem num: " << semctl(Sem_id,0,GETVAL,0) << endl;
p_func(Sem_id);
p_func(Sem_id);
p_func(Sem_id);
p_func(Sem_id);
cout << "after sem num: " << semctl(Sem_id,0,GETVAL,0) << endl;
v_func(Sem_id);
cout << "after sem num: " << semctl(Sem_id,0,GETVAL,0) << endl;
}
return 0;
}