Linux 学习笔记17 计数信号量

Linux 学习笔记17 计数信号量

计数信号量的使用场景——生产者和消费者模型

计数信号量原理:
计数信号量原理

计数信号量代码如下:

#include <func.h>                                                                                           
  //生产者和消费者模型
  //仓库货架空位 10 定为0号信号量
  //产品数目 0  定为 1 号信号量
 int main(int argc,char * argv[])
  {
      int sems_id;
      sems_id=semget(1000,2,IPC_CREAT|0600);
      ERROR_CHECK(sems_id,-1,"semget");
      unsigned short arr[2]={10,0};//第一个为仓库货架空位个数,第二个为产品个数
      int ret=semctl(sems_id,0,SETALL,arr);
      ERROR_CHECK(ret,-1,"semctl");
      //令父进程为生产者,子进程为消费者。
      struct sembuf sopp,sopv;
  
  
      if(!fork())
      {//子进程为出库,空位+1,产品数-1
          sopp.sem_num=1;//1号信号量为产品数
          sopp.sem_op=-1;//对产品数进行+1
          sopp.sem_flg=SEM_UNDO;//防止程序死锁
          sopv.sem_num=0;//0号信号量为空位
          sopv.sem_op=1;//对空位数进行+1
          sopv.sem_flg=SEM_UNDO;
          while(1)
          {   
              semop(sems_id,&sopp,1);//产品出库,产品数目-1
              semop(sems_id,&sopv,1);//货架空位+1
              printf("customer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("Now starring produce\n");
              printf("customer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("-------------------------------------------------------\n");
              sleep(2);
          }
      }else
      {//父进程入库,空位-1.产品数+1
          sopp.sem_num=0;//0号信号量为空位
          sopp.sem_op=-1;//对空位-1
          sopp.sem_flg=SEM_UNDO;
          //sopp.sem_flg=0;
          sopv.sem_num=1;//1号信号量为产品数
          sopv.sem_op=1;//对产品数进行+1
          sopv.sem_flg=SEM_UNDO;
          while(1)
          {
              semop(sems_id,&sopp,1);//产品入库,空位-1,p操作
              semop(sems_id,&sopv,1);//产品个数+1
              printf("producer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("Now starring produce\n");
              printf("producer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("-------------------------------------------------------\n");
              sleep(1);
          }
      }
      return 0;
  }

执行效果如下:
执行效果图

  • 对SEM_UNDO的理解
    将上述代码稍微改变一下
#include <func.h>                                                                                           
  //生产者和消费者模型
  //仓库货架空位 10 定为0号信号量
  //产品数目 0  定为 1 号信号量
? int main(int argc,char * argv[])
  {
      int sems_id;
      sems_id=semget(1000,2,IPC_CREAT|0600);
      ERROR_CHECK(sems_id,-1,"semget");
      unsigned short arr[2]={10,0};//第一个为仓库货架空位个数,第二个为产品个数
      int ret=semctl(sems_id,0,SETALL,arr);
      ERROR_CHECK(ret,-1,"semctl");
      //令父进程为生产者,子进程为消费者。
      struct sembuf sopp,sopv;
  
  
      if(fork())
      {
          //父进程为出库,空位+1,产品数-1
          sopp.sem_num=1;//1号信号量为产品数
          sopp.sem_op=-1;//对产品数进行+1
          sopp.sem_flg=SEM_UNDO;
          sopv.sem_num=0;//0号信号量为空位
          sopv.sem_op=1;//对空位数进行+1
          sopv.sem_flg=SEM_UNDO;
          while(1)
          {
              printf("customer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              semop(sems_id,&sopp,1);//产品出库,产品数目-1
              printf("Now starring produce\n");
              semop(sems_id,&sopv,1);//货架空位+1
              printf("customer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("-------------------------------------------------------\n");
              sleep(2);
          }
      }else
      {//子进程入库,空位-1.产品数+1
          sopp.sem_num=0;//0号信号量为空位
          sopp.sem_op=-1;//对空位-1
          sopp.sem_flg=SEM_UNDO;
          //sopp.sem_flg=0;
          sopv.sem_num=1;//1号信号量为产品数
          sopv.sem_op=1;//对产品数进行+1
          sopv.sem_flg=SEM_UNDO;
          while(1)
          {
              printf("producer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              semop(sems_id,&sopp,1);//产品入库,空位-1,p操作
              printf("Now starring produce\n");
              semop(sems_id,&sopv,1);//产品个数+1
              printf("producer Mark1,stack pos num=%d,product num=%d\n",semctl(sems_id,0,GETVAL),semctl(sems_i
              printf("-------------------------------------------------------\n");
              sleep(1);
          }
      }
      return 0;
  }

执行效果如下:
semundo 演示
可发现,在SEM_UNDO 的执行下,将子进程(生产者)终止后,会使之前生产的产品重置,即让代码回到原来还未执行时的状态。

如果将SEM_UNDO 的效果去除,则会得到如下的执行效果:
SEM_UNDO
显然,仓库产品数和货架空位超过了最大值10,发生了错误。

没有使用 SEM_UNDO 死锁的代码如下:

#include <func.h>                                                                                           
  #define N 10000000
  //例:信号量崩溃,产生死锁
 int main(int argc,char * argv[])
  {
      //设置共享内存
      int sems_id;
      int ret,shmid;
      shmid=shmget(1000,4096,IPC_CREAT|0600);
      ERROR_CHECK(shmid,-1,"shmget");
      printf("shmid is %d\n",shmid);
      int *p=(int *)shmat(shmid,NULL,0);
      ERROR_CHECK(p,(int *)-1,"shmat");
      p[0]=0;//初始化共享内存
      //设置信号量
      sems_id=semget(1000,1,IPC_CREAT|0600);
      ERROR_CHECK(sems_id,-1,"semget");
      ret=semctl(sems_id,0,SETVAL,1);
      ERROR_CHECK(ret,-1,"semctl");
      struct sembuf sopp,sopv;
      sopp.sem_num=0;
      sopp.sem_op=-1;
      //sopp.sem_flg=SEM_UNDO;
      sopp.sem_flg=0;
      sopv.sem_num=0;
      sopv.sem_op=1;
      //sopv.sem_flg=SEM_UNDO;
      sopv.sem_flg=0;//改成0使信号量崩溃
      //父子进程对共享内存进行写操作
      int i;
      if(!fork())
      {//子进程
          semop(sems_id,&sopp,1);
          printf("child lock success\n");
          p[0]=1/0;//程序崩溃
          semop(sems_id,&sopv,1);
          exit(0);
      }else
      {//父进程
          sleep(1);//为了制造死锁,让子进程先加锁
          int status;
          printf("Mark start lock\n");
          semop(sems_id,&sopp,1);
          p[0]=p[0]+1;
          semop(sems_id,&sopv,1);
          wait(&status);
          if(WIFEXITED(status))
          {
              printf("child Mark is exit reagularly\n");
          }else
          {
              printf("child Crash,exit code=%d\n",WEXITSTATUS(status));
          }
          printf("result=%d\n",p[0]);
      }
      return 0;
  }

执行效果如下:
sem_crash
可见子进程崩溃,产生了僵尸进程(Z),程序死锁,导致无法往下执行。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值