信号量SEM_UNDO设置

来源:互联网 发布:数据分析师报考条件 编辑:程序博客网 时间:2024/06/06 04:10
信号量的本质是计数器,只能进行两种操作等待和发送信号,即P(sv)和V(sv),他们的行为是这样的:
  P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
 
  举个例子,就是 两个进程共享信号量sv,一旦其中一个进程执行了P(sv)操作,它将得到信号量,并可以进入临界区,使sv减1。而第二个进程将被阻止进入临界区,因为 当它试图执行P(sv)时,sv为0,它会被挂起以等待第一个进程离开临界区域并执行V(sv)释放信号量,这时第二个进程就可以恢复执行。

内核为每个信号量集合设置了一个semid_ds结构  

struct semid_ds {struct ipc_permsem_perm ;structsem* sem_base ; //信号数组指针ushort sem_nsem ; //此集中信号个数time_t sem_otime ; //最后一次semop时间time_t sem_ctime ; //最后一次创建时间} ;每个信号量由一个无名结构表示,它至少包含下列成员: struct {ushort_t semval ; //信号量的值short sempid ; //最后一个调用semop的进程IDushort semncnt ; //等待该信号量值大于当前值的进程数(一有进程释放资源 就被唤醒)ushort semzcnt ; //等待该信号量值等于0的进程数} ;
信号量的使用
1、创建信号量
semget函数创建一个信号量集或访问一个已存在的信号量集。
#include <sys/sem.h>
int semget (key_t key, int nsems, int semflg) ;
返回值是一个称为信号量标识符的整数,semop和semctl函数将使用它。
参数nsem指定集合中的信号量数。(若用于访问一个已存在的集合,那就可以把该参数指定为0)
参数semflg可以是SEM_R(read)和SEM_A(alter)常值的组合。也可以是IPC_CREAT或IPC_EXCL ;
 
2、打开信号量
使用semget打开一个信号量集后,对其中一个或多个信号量的操作就使用semop函数来执行。
#include <sys/sem.h>
int semop (int semid, struct sembuf * sops, unsigned nsops) ;

参数sops是一个指针,它指向一个信号量操作数组,信号量操作由sembuf结构表示:

struct sembuf{short sem_num; // 除非使用一组信号量,否则它为0short sem_op; // 信号量在一次操作中需要改变的数据,通常是两个数,// 一个是-1,即P(等待)操作,一个是+1,即V(发送信号)操作short sem_flg; // 通常为SEM_UNDO,使操作系统跟踪信号,并在进程没有释放该信号量而终止时,// 操作系统释放信号量};
参数nsops规定sops数组中元素个数。
sem_op值:
1)若sem_op为正,这对应于进程释放占用的资源数。sem_op值加到信号量的值上。(V操作)
2)若sem_op为负,这表示要获取该信号量控制的资源数。信号量值减去sem_op的绝对值。(P操作)
3)若sem_op为0,这表示调用进程希望等待到该信号量值变成0
如果信号量值小于sem_op的绝对值(资源不能满足要求),则:
(1)若指定了IPC_NOWAIT,则semop()出错返回EAGAIN。
(2)若未指定IPC_NOWAIT,则信号量的semncnt值加1(因为调用进程将进 入休眠状态),然后调用进程被挂起直至:①此信号量变成大于或等于sem_op的绝对值;②从系统中删除了此信号量,返回EIDRM;③进程捕捉到一个信 号,并从信号处理程序返回,返回EINTR。(与消息队列的阻塞处理方式 很相似)
 3、信号量操作
semctl函数对一个信号量执行各种控制操作。
#include <sys/sem.h>
int semctl (int semid, int semnum, int cmd, ...) ;
第四个参数是可选的,取决于第三个参数cmd。
参数semnum指定信号集中的哪个信号(操作对象)
参数cmd指定以下10种命令中的一种,在semid指定的信号量集合上执行此命令。
IPC_STAT   读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
IPC_SET     设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。IPC_RMID  将信号量集从内存中删除。
GETALL      用于读取信号量集中的所有信号量的值。
GETNCNT  返回正在等待资源的进程数目。
GETPID      返回最后一个执行semop操作的进程的PID。
GETVAL      返回信号量集中的一个单个的信号量的值。
GETZCNT   返回这在等待完全空闲的资源的进程数目。
SETALL       设置信号量集中的所有的信号量的值。
SETVAL      设置信号量集中的一个单独的信号量的值。
信号量值的初始化

  semget并不初始化各个信号量的值,这个初始化必须通过以SETVAL命令(设置集合中的一个值)或SETALL命令(设置集合中的所有值) 调用semctl来完成。

  SystemV信号量的设计中,创建一个信号量集并将它初始化需两次函数调用是一个致命的缺陷。一个不完备的解决方案是:在调用semget时指定IPC_CREAT | IPC_EXCL标志,这样只有一个进程(首先调用semget的那个进程)创建所需信号量,该进程随后初始化该信号量。

 

  SEM_UNDO

  当操作信号量(semop)时,sem_flg可以设置SEM_UNDO标识;SEM_UNDO用于将修改的信号量值在进程正常退出(调用exit退出或main执行完)或异常退出(如段异常、除0异常、收到KILL信号等)时归还给信号量。
  如信号量初始值是20,进程以SEM_UNDO方式操作信号量减2,减5,加1;在进程未退出时,信号量变成20-2-5+1=14;在进程退出时,将修改的值归还给信号量,信号量变成14+2+5-1=20。