semget函数 semopt 采用信号量处理多进程互斥同步

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

       信号量与消息类似,也是进程间通信的一种方法。我们在这里讲的信号量,实际上是一个包含信号量元素数组的信号量集。信号量元素与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
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值