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;
}
执行效果如下:
可发现,在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;
}
执行效果如下:
可见子进程崩溃,产生了僵尸进程(Z),程序死锁,导致无法往下执行。