Linux信号量PV操作


 

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 <stdio.h>

   4:  #include <stdlib.h>

   5:  #include <sys/ipc.h>

   6:  #include <sys/sem.h>

   7:  #include <sys/types.h>

   8:  #include <fcntl.h>

   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;

相关内容

    暂无相关文章