信号量是不同进程间或一个给定进程内部不同线程间同步的机制。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成员可以为0IPC_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参数前,运行结果:

 wKiom1cQ-pOgbd2yAABPWUMI-wA646.png

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

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

 

 wKiom1cQ-qui3XMpAABWXE6ztn8217.png

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