信号量是不同进程间或一个给定进程内部不同线程间同步的机制。System V信号量是一个或多个信号量的集合,其中的每一个都是氮素的计数信号量。System V信号量由内核维护,主要函数有:semget,semop,semctl。

我们重点来讨论semop函数,该函数的主要功能是对信号进行PV操作。

P操作负责把当前进程由运行状态转换为阻塞状态,知道另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出,若失败,则该进程被阻塞。

V操作负责把被阻塞的进程唤醒,它有一个参数表,存放着等待被唤醒的进程信息,操作为:释放一个被占用的资源(把信号量加1),如果发现有被阻塞的进程,则选择一个唤醒。

semop函数原型:

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

semop操作中:sembuf结构的sem_flg成员可以为0,IPC_NOWAIT,SEM_UNDO,为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。

下面我们用一段程序来看看SEM_UNDO的作用:

 

#include "comm.c"文件:

   

static int comm_creat_sem_set(int _sem_nums,int flag)
   {
       key_t _key=ftok(_PATH_,_PROJ_ID_);
       if(_key<0)
       {
           perror("ftok");
           return -1;
      }
      int sem_id=semget(_key,_sem_nums,flag);
      if(sem_id<0)
      {
          return -1;
      }
      return sem_id;
  }
  
  int creat_sem_set(int _sem_nums)
  {
      umask(0);
      int flag=IPC_CREAT|IPC_EXCL|0666;
      return comm_creat_sem_set(_sem_nums,flag);
  }
  
  int get_sem_set(int _sem_nums)
  {
      int flag=IPC_CREAT;
      return comm_creat_sem_set(_sem_nums,flag);
  }
  
  int init_sem_set(int _sem_id,int _sem_num,int _init_val)
  {                                                                                                                                                                   
      union semun _un;
      _un.val=_init_val;
      if(semctl(_sem_id,_sem_num,SETVAL,_un)<0)
      {
          perror("semctl");
          return -1;
      }
      return 0;
  }
  
   int p_sem(int _sem_id,int _seq_num)
  {
      struct sembuf _sem_buf[1];                                                                                                                                      
      _sem_buf[0].sem_num=_seq_num;
      _sem_buf[0].sem_op=-1;
  //  _sem_buf[0].sem_flg=SEM_UNDO;
      _sem_buf[0].sem_flg=0;
      if(semop(_sem_id,_sem_buf,1)<0)
      {
          perror("semop");
          return -1;
      }
      return 0;
  }
  
  
   int v_sem(int _sem_id,int _seq_num)
  {
      struct sembuf _sem_buf[1];
      _sem_buf[0].sem_num=_seq_num;
      _sem_buf[0].sem_op=1;
      _sem_buf[0].sem_flg=0;
 //  _sem_buf[0].sem_flg=SEM_UNDO;
      if(semop(_sem_id,_sem_buf,1)<0)
      {
          perror("semop");
          return -1;
      }
      return 0;                                                                        
}
comm.h文件:
  #pragma once 
  #include <stdio.h>                                                                                                                                       
  #include <unistd.h>
  #include <stdlib.h>
  #include <sys/types.h>
  #include <sys/ipc.h>
  #include <sys/sem.h>
   
  #define _PATH_ "."
  #define _PROJ_ID_ 0x6666
  
  union semun
  {
      int val;    /* Value for SETVAL */
      struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
      unsigned short  *array;  /* Array for GETALL, SETALL */
      struct seminfo  *__buf;  /* Buffer for IPC_INFO*/
  };
  
  int creat_sem_set(int _sem_nums);
  int get_sem_set(int _sem_nums);
  int init_sem_set(int _sem_id,int _sem_nums,int _init_val);
  int p_sem(int _sem_id,int _seq_num);
  int v_sem(int _sem_id,int _seq_num);
  int destroy_sem_set(int _sem_id);

sem.c文件:

int main()
  {
      int sem_id=creat_sem_set(1);                                                                                                                                    
      init_sem_set(sem_id,0,1);
      pid_t id=fork();
      if(id <0)
      {
          perror("fork");
          exit(1);
      }
      else if(id==0)
      {
          int childid=getpid();
          int fatherid=getppid();
          printf("childid:%d,fatherid:%d\n",childid,fatherid);
          int sem_id=get_sem_set(0);
          while(1)
          {
              p_sem(sem_id,0);
              printf("child wrinting\n");
              sleep(1);
              fflush(stdout);
              printf("child finish post\n");
              sleep(10);
              fflush(stdout);
              v_sem(sem_id,0);
          }
      }
      else
      {
          while(1)
          {
              p_sem(sem_id,0);
              printf("father wrinting\n");
              sleep(1);
              fflush(stdout);
              printf("father finish post\n");
              sleep(1);
              fflush(stdout);                                                                                                                              
              v_sem(sem_id,0);
          }
  
      }
  
      destroy_sem_set(sem_id);
      return 0;
  }

在没用SEM_UNDO参数前,运行结果:

 

SEM_UNDO_#include

P操作之后,我们让子进程sleep了10秒,在这之前我们把子进程傻掉了,也就是子进程没有进行V操作,所以没有释放占用的信号量,我们会看到,把子进程杀掉之后,父进程阻塞了。

而我们用了SEM_UNDO这个参数后,运行结果如下:

 

 

SEM_UNDO_子进程_02

我们可以看到,在把子进程kill后,父进程照样可以运行,因为使用了SEM_UNDO后,操作系统自动释放该进程持有的信号量,从而可以使得另一个进程可以继续工作。否则,另外一个进程将永远阻塞。



转载于:https://blog.51cto.com/10706198/1764316