UNIX多用户系统下信号量操作详解

【UNIX多用户系统下信号量操作详解】
资源共享是Unix多用户系统的一个重要特征,信号量(SEMAPHORE)则是防止两个或多个进程同时访问共享资源的一种机制 。在信号量机制实现之前,通常采用加锁文件的方法,其算法描述如下:

⑴加锁算法

int lock(lockfile)
/*返回值0代表成功,其它为失败*/
char *lockfile; /*加锁文件名*/
{
intfd,ret=0;
extern int errno;
if((fd=open(lockfile,O_WRONLY|O_CREAT|O_EXCL,0666))==-1
&&errno==EEXIST) ret=1;
return(ret);
}

⑵解锁算法

unlock(lockfile)
char *lockfile; /*锁文件名*/
{
unlink(lockfile);
}

这种方法对访问共享资源次数较少的进程是可行的,但对重载的使用则开销太大了,况且一旦加锁失败则进程不知何时可以再试;当系统崩溃或重启动时,加锁文件可能会被忘掉了 。

Dijkstra发表的Dekker算法给出了信号量的一种实现,为整值对象定义了两个了原语操作:P和V 。其C描述如下:

void P(sem)
int *sem;
{
while (*sem<=0);
(*sem)--;
}

void V(sem)
int *sem;
{
(*sem);
}

但上述算法不能在用户空间编程,因为①sem指向的信号量变量不能在进程间共享,它们有自己的数据段;②函数非原子执行,内核可在任何时候中断一个进程;③若sem为0,进程并不释放CPU 。

所以信号量必须由内核提供,它可在进程间共享数据,可执行原子操作(即一组操作要么全部执行,要么都不执行),可在一个进程阻塞时将CPU给另外一个进程 。

UNIXSYSTEMV以一个长整数的键值作为信号量集合的唯一标识,信号量通常由下列元素组成:

①信号量的值,

②操作该信号量的最后一个进程的进程标识,

③等待增加该信号量的值的进程数,

④等待该信号量的值为0的进程数 。

与之有关的系统调用如下:

#include
#include
#include

int semget(key,count,flags)
/*获取信号量集合的标识符*/
key_tkey; /*信号量集合的键*/
intcount; /*信号量集合中元素个数*/
intflags; /*任选参数*/
/*返回信号量集合标识符,若出错则返回-1*/

int semop(sid,ops,nops) /*信号量操作*/
int sid; /*信号量集合标识符*/
struct sembuf *ops; /*信号量操作结构的指针*/
intnops; /*信号量操作结构的个数*/
/*返回运算完成前该组信号量中最后一个被运算的信号量的
值,若出错则返回-1*/

int semctl(sid,semnum,cmd,arg)
/*控制信号量操作*/
intsid; /*信号量集合标识符*/
intsemnum; /*信号量元素编号*/
intcmd; /*控制命令*/
union semun{
intval;
struct semid_ds *buf;
ushort*array;} arg; /*命令参数*/


系统调用semget用来把信号量集合的键值译成代表信号量集合的标识符,该集合中有count个元素,其存取权限定义与文件相同,由flags定义 。若flags的IPC_CREAT位被置位,则当该集合不存在时系统就创建之 。因此各进程可都用置IPC_CREAT位的flags参数来获取信号量集合的标识符,不需要由某一进程事先创建 。若flags为IPC_PRIDVATE则不管同键值的信号量集合是否存在系统都建立之,并返回下一个可用的标识符 。

系统调用semctl在一组信号量上做各种控制操作,诸如信号量集合的初始化、删除和状态查询等 。常用的操作及相关的命令格式如下:

①取消信号量集合

int semctl(sid,count,IPC_RMID,0)
int sid; /*信号量集合标识符*/
int count; /*信号量集合中元素个数*/

②设置信号量集合的初值(初始化)

信号量集合刚建立时,各信号量的初值不确定,需要设定初值 。初值的设定可用SETALL或SETVAL命令 。若用SETALL命令,其格式为:

int semctl(sid,count,SETALL,arg)

推荐阅读