loading ...

2007-09-08 | 采用信号量处理多进程互斥同步

分享
标签: 信号量  进程互斥 

 

采用信号量处理多进程互斥同步

       信号量与消息类似,也是进程间通信的一种方法。我们在这里讲的信号量,实际上是一个包含信号量元素数组的信号量集。信号量元素与E.W.Dijkstra提出的整数信号量相对应。在一个单系统调用中,进程可在完整的信号量集上操作。

       信号量集的内部表示和各自信号量元素不是直接可以访问的,但每个信号量元素必须包括下列各项:

l         一个标识信号量元素的非负整数

l         最后操作信号量元素的进程ID

l         等待信号量元素值加1的进程数

l         等待信号量元素值等于0的进程数

信号量操作允许一个进程阻塞直到信号量元素值为0或者直到它变为正数。每个元素具有两个相关联的队列:一个是等待信号量元素值加1的进程队列,另一个是等待信号量元素值等于0的进程队列。

1、  semget函数

系统调用semget用来创建一个信号量集并将每个元素初始化为0

#include<sys/ipc.h>

#include<sys/sem.h>

int semget(key_t key, int nsems, int semflg );

函数semget有3个参数。第1个参数为标识信号量集的关键字,程序指定关键字的方法有三种:使用IPC_PRIVATE让系统产生一个关键字、挑选一个随机数,或者是使用ftok从文件路径名中产生一个关键字。第2个参数为信号量集中的元素个数。第3个参数为信号量存取权标志与建立标志,该参数的低9位是信号量的存取权标志;建立标志有2个,IPC_CTEAT和IPC_EXCL。IPC_CREAT表示如果信号量集不存在,则创建它;IPC_EXCL表示只有在信号量集不存在的情况下,新的信号量集才会被创建,否则semget返回-1,并设定errno。如果这两个标志联合使用,则功能如同O_EXCL标志于open。

函数semget调用成功时返回一个用于以后semctl等操作的整数句柄,失败时返回-1。

在指定信号量集的关键字时,我们可以使用ftok根据文件路径名产生一个关键字。

#include<sys/ipc.h>

key_t ftok(const char * pathname, int proj_id);

函数ftok的第1个参数为文件路径名,该文件必须存在且进程对该文件有访问权。第2个参数为程序员指定的一个整数ID。该函数成功调用时返回一个关键字,如调用失败,则返回-1。

2、  semctl函数

系统调用semctl可以对信号量进行很多控制。

#include<sys/ipc.h>

#include<sem.h>

int semctl(int semid, int semnum, int cmd, /*union senum arg*/…);

函数的第1个参数为信号量集的句柄。第2个参数指定信号量集中的元素。第3个参数为执行的控制命令,常见的命令如下表:

命令名称

表示的含义

GETVAL

获得一个指定信号量的值

GETPID

获得操作此元素的最后进程的ID

GETNCNT

获得等待元素增1的进程数

GETZCNT

获得等待元素变为0的进程数

SETVAL

设置一个指定信号量元素的值为arg.val

IPC_RMID

删除一个信号量

IPC_SET

设置信号量的许可权

函数semctl在出现错误时返回-1并设置erro。当调用成功时,其返回值决定于参数cmd,当cmd为GETVAL、GETPID、GETNCNT或GETZCNT时,函数返回相应的值,当cmd为别的值时,返回0。

union senum定义可直接包含在程序中,因为系统的头文件中并没有定义它。它的定义如下:

union senum

{

int val;

struct semid_ds *buf;

ushort *array;

};

union senum中提到的semid_ds结构定义如下:

struct semid_ds

{

struct ipc_perm sem_perm;    /*operation permission struct*/

time_t sem_otime;   /*last semop() time*/

time_t sem_ctime;   /*last time changed by semctl()*/

struct sem*sembase;             /*ptr to first semaphore in array*/

struct sem_queue *sem_pending;   /*pending operations*/

struct sem_queue *sem_pending_last;   /*last pending operation*/

struct sem_undo *undo;  /*undo requests on this array*/

unsigned short int sem_nsems;      /*number of semaphores in set*/

};

3、  semop函数

系统调用semop可以对信号量增1、减1或测试其是否为0。

#include <sys/ipc.h>

#include <sys/sem.h>

int sempo(int semid, struct sembuf *sops, unsigned int nsops);

函数semop的第1个参数为semget返回的句柄,第2个参数指向元素操作数组,第3个参数指定在数组中元素操作的个数。

函数成功调用时返回0,失败时返回-1。如果时被信号中断,则返回-1,同时设置errno为EINTR。

结构体sembuf的定义如下:

struct sembuf

{

  short int sem_num;  //信号量元素个数

  short int sem_op;   //信号量元素上的操作

  short int sem_flg;  //操作选项

}

在结构sembuf中,sem_num表示信号量元素的个数,sem_op表示在信号量元素上执行的特别操作,sem_flg表示操作选项的标志。如果sem_op为正数,semop函数将该值加到相应的信号量元素中,并唤醒所有等待元素增1的进程。如果sem_op为0而信号量的值不为0,semop将阻塞调用进程并增加那个元素的等零进程个数。如果sem_op为负数,semop将该值加到相应的信号量元素中(只要结果不为负数),如结果为负数,semop将阻塞进程等待信号量元素值增加;如值为0,semop将唤醒等零进程。

上面的描述假设sem_flg为0。如果sem_flg&IPC_NOWAIT为真,调用不会阻塞,而是返回-1并设置error为EAGAIN。如果sem_flg&SEM_UNDO为真,函数也将为进程修改信号量的调整值。这个调整值允许进程在退出时恢复它在信号量上的作用。

下面的程序给出了关于信号量集的系统调用semget,semctl和semop的基本用法。

#include <stdio.h>

#include <stdlib.h>

#include <sys/sem.h>

#include <sys/ipc.h>

main()

{

int semid,pid,I,j;

static struct sembuf lock={0,-1,SEM_UNDO};

static struct sembuf unlock={0,1,SEM_UNDO|IPC_NOWAIT};

 

if((semid=semget(998,1,IPC_CREAT|IPC_EXCL|0666))==-1)

{

               printf(“error:semget!\n”);

               exit(1);

}

 

if(semctl(semid,0,SETVAL,1)==-1)

{

printf(“error:semctl!\n”);

exit(1);

}

 

       setbuf(stdout,(char *)NULL);

       for(i=0;i<3;i++)

       {

              if(fork()==0)

              {

                     if((semid=semget(998,1,0))==-1)

                     {

                            printf(“error:semget!\n”);

                            exit(1);

}

 

for(j=0;j<3;j++)

{

       sleep(i);

       if(semop(semid,&lock,1)==-1)

       {

              printf(“error:semop!\n”);

              exit(1);

}//if lock

 

printf(“process %d get into critical section!\n”,getpid());

sleep(1);

printf(“process %d left critical section!\n”,getpid());

if(semop(semid,&unlock,1)==-1)

{

       printf(“error:semop!\n”);

       exit(1);

}

}//for j

exit(0);

}//if fork

}//for i

 

for(i=0;i<3;i++)

               wait(NULL);

 

if(semctl(semid,0,IPC_RMID,0)==-1)

{

        printf(“error:semctl!\n”);

        exit(1);

}

exit(0);

}

分享 分享 |  评论 (0) |  阅读 (?)  |  固定链接 |  类别 (应用层) |  发表于 22:13
搜狐博客温馨提示:警惕博客留言诈骗, 搜狐博客管理员的正确地址为http://admin.blog.sohu.com, 其他都是冒牌。搜狐博客官方不会要求参加活动的各位博友缴纳任何的手续费用。请勿轻信留言、评论中的中奖信息,更不要拨打陌生电话及向陌生帐户汇款,谨防受骗!识别更多网络骗术,请 点击查看详情
您还未登录,只能匿名发表评论。或者您可以 登录 后发表。
 
  一个单亲妈妈的心愿:治好7岁儿子的白血病
表  情:
加载中...
回复通知: 同时用小纸条通知对方该回复