进程的信号量通信

进程的信号量通信

【预备知识】

在Linux中,一个或多个信号量构成一个信号量集合。使用信号量机制用来实现进程之间的同步和互斥,允许并发进程一次对一组信号量进行相同或不同的操作。每个P、V操作不限于减1或加1,而是可以加减任何整数。在进程终止时,系统可根据需要自动消除所有被进程操作过的信号量的影响。

1.信号量机制中使用的数据结构

系统中的每个信号量都有一个数据结构定义在sys/sem.h中。结构定义如下:

struct sem

{

  ushort semval;   /*信号量的当前值*/

  short sempid;   /* 最近一次对该信号操作的进程标识*/

  ushort semncnt;  /*等待信号量值增加的进程数*/

  ushort semzcnt;  /*等待信号量值等于0的进程数*/

}

2.信号量集合的数据结构

系统为每个信号集合保持一个头文件定义在sys/sem.h中。结构定义如下:

struct semid_ds

{

struct ipc_perm  sem_perm;    /*对信号量的访问方式,见消息缓冲*/

time_t  sem_otime;           /*最后操作信号量集合的时间*/

time_t  sem_ctime;           /*最后修改信号量集合的时间*/

struct sem *sem_base;       /*指向集合中第一个信号量的指针*/

ushort  sem_nsems;        /*集合中的信号量个数*/       

}

3.信号量集合的建立

任何进程在使用信号量之前,通过使用如下函数申请建立一个信号量集合:

# include <sys/types.h>

# include <sys/ipc.h>

# include <sys/sem.h>

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

其中,key为用户进程指定的信号量集合的关键字,nsems为信号量集合中的信号量数,semflg为访问标志。

返回值:成功时,返回信号量集合的标识号,失败时,返回-1。

semflg 由以下成分组成:

·IPC_CREAT,创建一个新的信号量集合。如果该标志没有设置,将查找于key相关的信号量集合。

·IPC_EXCL,与IPC_CREAT一起使用,确保创建一个新的信号量集合。若该段已经存在,出错。

低9位为三类用户的访问方式的定义。

4.对信号量的操作

通过调用semop()函数,进程对一信号量集合中的一个或多个信号执行P/V操作,其操作命令由用户提供的信号操作模版(sembuf)定义,该模版的结构如下:

Structsembuf

  ushort sem_num;         /*信号量的序号*/

  short sem_op            /*具体执行的操作(即P或V操作)* /

  short sem_flg;          /*访问标志*/

调用语法为:

# include <sys/sem.h>

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

其中,semid是进程调用semget后返回的信号量集合的标识,sops是用户提供的信号量操作模版数组(sembuf)的指针,nsops为数组sembuf中的元素数(即一次需进行的操作数)。正常返回值为0。

5.对信号量执行控制操作

当要读取和修改信号量集合的有关状态信息,或撤销信号量集合时,调用命令semctl()对信号量执行控制操作。其调用语法为:

int semctl(int semid,int semnum,int cmd,union semun arg);

其中,semid是信号量集合的标识,semnum是要处理信号量集合中的信号量序号,cmd为要执行的操作命令,arg为控制操作需要的参数,是一个指向联合semun的指针。函数semctl成功返回时为0。semun定义为:

union  semun

{

   int  val;/ * value  forSETVAL * /

   struct  semid_ds * buf ; / * buffer  for  IPC_SET & IPC_STAT */

   ushort  array [ ];/ * buffor  for IPC_INFO * /

}  arg;

cmd的可能取值有:

GETPID      返回最近一个队信号量操作的进程标识  

GETVAL      返回索引为semnum的信号量的值  

GETALL      返回一个集合中所有信号量的值到arg.array中

GETNCNT    返回睡眠等待信号量值为正的进程的数

GETZCNT    返回睡眠等待信号量值为0的进程数

SETVAL      设置索引为semnum的信号量的值为arg.val

SETALL      按照数组arg.array,设置一个集合中所有信号量的值

IPC_STAT    若为读,可将指定semid的信号量头结构读入arg.buf中获得信号量的值

IPC_SET     按照arg.buf设置用户标识、组标识和访问权限。若不是给定的有效用户标识,立即返回

IPC_RMID   删除指定标识的信号量集合

编程实现例<一>

【任务】

编程实现多个进程对同一文件的互斥写入。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#define NUM_PROCS 5
#define SEM_ID 250
#define FILE_NAME "/tmp/sem_MUTEX"
#define DELAY 400000

union semun {
    int val;
    struct semid_ds *buf;
    ushort *array;
};

void update_file(int sem_set_id,char *file_name_path,int number)
{
   struct sembuf sem_op;
   FILE *file;
   sem_op.sem_num=0;
   sem_op.sem_op=-1;
   sem_op.sem_flg=0;
   semop(sem_set_id,&sem_op,1);
   file=fopen(file_name_path, "w");
   if(file)
   {
      fprintf(file, "%d\n", number);
      printf("%d\n", number);
      fclose(file);
   }

   sem_op.sem_num=0;
   sem_op.sem_op=1;
   sem_op.sem_flg=0;
   semop(sem_set_id,&sem_op,1);
}

void do_child_loop(int sem_set_id,char *file_name)
{
  pid_t pid=getpid();
  int i,j;
  for(i=0;i<3;i++)
  {
     update_file(sem_set_id,file_name,pid);
     for(j=0;j<200000;j++);
  }
}

int main(int argc,char**argv)
{
  int sem_set_id,child_pid,rc,i;
  union semun sem_val;
  sem_set_id=semget(SEM_ID,1,IPC_CREAT|0600);
  if(sem_set_id==-1)
  {
     perror("main's semget error ");
     exit(1);
  }
  sem_val.val=1;
  rc=semctl(sem_set_id,0,SETVAL,sem_val);
  if (rc==-1)
  {
     perror("main:setcl");
     exit(1);
  }
  for (i=0;i<NUM_PROCS;i++)
  {
     child_pid=fork();
     switch(child_pid) {
     case -1:
        perror("fork()");
        exit(0);
     case 0:
        do_child_loop(sem_set_id,FILE_NAME);
        exit(0);
     default: break;
     }
  }

  for (i=0;i<NUM_PROCS;i++)
  {
     int child_status;
     wait(&child_status);
  }
  printf("main is done.\n");
  fflush(stdout);
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值