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

 semop操作中:sembuf结构的sem_flg成员可以为0、IPC_NOWAIT、SEM_UNDO 。为SEM_UNDO时,它将使操作系统跟踪当前进程对这个信号量的修改情况,如果这个进程在没有释放该信号量的情况下终止,操作系统将自动释放该进程持有的信号量。除非你对信号量的行为有特殊的要求,否则应该养成设置sem_flg为SEM_UNDO的好习惯。 
   1:  //假设两个进程(父子进程)对一个文件进行写操作,但是这个文件同一时间只能有一个进程进行写操作。
   2:  //利用信号量实现pv操作
   3:  #include 
   4:  #include 
   5:  #include 
   6:  #include 
   7:  #include 
   8:  #include 
   9:  struct sembuf sops;
  10:  static int sid;
  11:  //创建一个新的信号量集
  12:  int createSemset(void)
  13:  {
  14:  char* pathname="semset";
  15:  if( access(pathname, F_OK)!=0 )
  16:  {
  17:  int fd=open(pathname, O_RDWR | O_CREAT, 0666);    
  18:  if( fd<0 )
  19:  {
  20:  perror("open");
  21:  return -1;
  22:  }
  23:  }
  24:  key_t key=ftok(pathname, 'a');
  25:  if( -1==key )
  26:  {
  27:  perror("ftok");
  28:  return -1;
  29:  }
  30:  return semget(key, 1, IPC_CREAT | 0666) ;
  31:  }
  32:   
  33:  //P操作
  34:  int P(void)
  35:  {
  36:  sops.sem_num=0;
  37:  sops.sem_op=-1;
  38:  sops.sem_flg=0;
  39:  return semop(sid, &sops, 1);
  40:  }
  41:  //V操作
  42:  int V(void)
  43:  {
  44:  sops.sem_num=0;
  45:  sops.sem_op=1;
  46:  sops.sem_flg=0;
  47:  return semop(sid, &sops, 1);
  48:  }
  49:  int main(int argc, char *argv[])
  50:  {
  51:  sid=createSemset();
  52:  if( -1==sid )
  53:  {
  54:  perror("createSemset");
  55:  exit(1);
  56:  }
  57:   
  58:  if( -1==semctl(sid, 0, SETVAL, 1) )
  59:  {
  60:  perror("SETVAL");
  61:  exit(1);
  62:  }
  63:  pid_t pid=fork();
  64:  if( pid<0 )
  65:  {
  66:  perror("fork");
  67:  exit(1);
  68:  }
  69:  else if( 0==pid )
  70:  {
  71:  while(1)
  72:  {
  73:  if( -1==P() )
  74:  {
  75:  printf("P操作失败!
");    
  76:  exit(1);
  77:  }
  78:  printf("子进程正在对文件进行写操作!
");
  79:  sleep(1);    
  80:  printf("子进程写操作完毕,释放资源!
");
  81:  if( -1==V() )
  82:  {
  83:  printf("V操作失败!");
  84:  exit(1);
  85:  }
  86:  }
  87:  }
  88:  else
  89:  {
  90:  while(1)
  91:  {
  92:  if( -1==P() )
  93:  {
  94:  printf("P操作失败!
");    
  95:  exit(1);
  96:  }
  97:  printf("父进程进程正在对文件进行写操作!
");    
  98:  sleep(1);
  99:  printf("父进程写操作完毕,释放资源!
");
 100:  if( -1==V() )
 101:  {
 102:  printf("V操作失败!");
 103:  exit(1);
 104:  } ......
   1:  void P(int semid)
   2:  {
   3:      struct sembuf sem_p;
   4:      sem_p.sem_num = 0;
   5:      sem_p.sem_op = -1;
   6:      sem_p.sem_flg = SEM_UNDO;
   7:      if (semop(semid, &sem_p, 1) == -1) {
   8:          perror("p op failed
");
   9:          exit(1);
  10:      }
  11:  }
  12:   
  13:  void V(int semid)
  14:  {
  15:      struct sembuf sem_p;
  16:      sem_p.sem_num = 0;
  17:      sem_p.sem_op = 1;
  18:      sem_p.sem_flg = SEM_UNDO;
  19:      if (semop(semid, &sem_p, 1) == -1) {
  20:          perror("v op failed
");
  21:          exit(1);
  22:      }
  23:  }

PV原语通过操作信号量来处理进程间的同步与互斥的问题。其核心就是一段不可分割不可中断的程序。

信号量的概念1965年由著名的荷兰计算机科学家Dijkstra提出,其基本思路是用一种新的变量类型(semaphore)来记录当前可用资源的数量。有两种实现方式:1)semaphore的取值必须大于或等于0。0表示当前已没有空闲资源,而正数表示当前空闲资源的数量;2) semaphore的取值可正可负,负数的绝对值表示正在等待进入临界区的进程个数。

信号量是由操作系统来维护的,用户进程只能通过初始化和两个标准原语(P、V原语)来访问。初始化可指定一个非负整数,即空闲资源总数。

P原语:P是荷兰语Proberen(测试)的首字母。为阻塞原语,负责把当前进程由运行状态转换为阻塞状态,直到另外一个进程唤醒它。操作为:申请一个空闲资源(把信号量减1),若成功,则退出;若失败,则该进程被阻塞;

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

具体PV原语对信号量的操作可以分为三种情况:

1)把信号量视为一个加锁标志位,实现对一个共享变量的互斥访问。

实现过程:

P(mutex); // mutex的初始值为1 访问该共享数据;

V(mutex);

非临界区

2)把信号量视为是某种类型的共享资源的剩余个数,实现对一类共享资源的访问。

实现过程:

P(resource); // resource的初始值为该资源的个数N 使用该资源;

V(resource); 非临界区

3)把信号量作为进程间的同步工具

实现过程:

临界区C1;

P(S);

V(S);

临界区C2;