复杂PC问题——信号量与共享存储区

#include <stdio.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <time.h>
#include <stdlib.h>
#include <sys/shm.h>
#define PRO 1
#define CON 0
#define P -1
#define V +1
typedef int MySem;
MySem empty,full,mutex1,mutex2;
int *buf;
MySem newsem(int intVal)// 新建信号量 
{
  int r,semID;
  semID=semget(0,1,IPC_CREAT|0666); //创建新信号量集
  r=semctl(semID,0,SETVAL,intVal); //对指定信号量赋intVal值 返回值:如果成功,则为一个正数;如果失败,则为-1
    return semID;//获得的信号量的标识,用于此后的信号量操作 
}
void psem(MySem semID)//对ID为semID的信号量做p
{
   struct sembuf s;
   s.sem_num=0;
   s.sem_op=P;
   s.sem_flg=0;
   int r=semop(semID,&s,1);//对指定的信号量执行P操作
}
void vsem(MySem semID)//对ID为semID的信号量做v
{
		//unsigned short sem_num; 欲操作的信号量在信号量集中的编号
		//short sem_op; 信号量PV操作的增量(例如+1或-1)
		//short sem_flg; 额外选项标识(0表示无额外设置;IPC_NOWAIT表示不允许阻塞;
		//SEM_UNDO表示进程结束时恢复信号量 等)};
   struct sembuf s;
   s.sem_num=0;
   s.sem_op=V;
   s.sem_flg=0;
   int r=semop(semID,&s,1);//对指定的信号量执行V操作
}
void freesem(MySem semID)//注销ID为semID的信号量
{
   int r;
   r=semctl(semID,0,IPC_RMID);//IPC_RMID:注销(删除)信号量集,无需参数
}

int init(int n)
{
  int shpid;
  shpid=shmget(0,sizeof(int)*(n+2),IPC_CREAT|0666);//create 共享存储区+2 in out 
  buf=(int *)shmat(shpid,0,0);//将共享存储区映射到用户进程空间
  empty=newsem(n);//缓冲区单元格有n个,初始化标记为null,允许生产者进程一开始就连续执行k次 
  full=newsem(0);  //初始时没有满标记单元格,置初值full=0 
  mutex1=newsem(1);	   //生产者的互斥
  mutex2=newsem(1);   //消费者的互斥 
  buf[n]=0;           //缓冲区单元格in 
  buf[n+1]=0;         //缓冲区单元格out  
  return shpid;       //存储区id                
}
void pro(pid_t pid,int n)
{
  printf("<P> <%d> started\n",getpid());//取得进程识别码 旧版 新_getpid();
  int index=buf[n]; //buf[n]->in  用来标识in的位置   
  psem(empty);  //同步,如果没有足够p值的话会放入队列中,系统进行维护
  psem(mutex1);
  buf[n]=(buf[n]+1)%n;
  buf[ buf[n]]=1;//模拟存入,置1
  printf("P <%d> put an item to <%d>\n",getpid(),index);
  vsem(mutex1);//回调p函数,取出队首,
  vsem(full);
  
}
/*
demo:
n=3
p:index = buf[3] =in=0    in=buf[3]=1  buf[1]=1
p:index = buf[3] =in=1    in=buf[3]=2  buf[2]=1
v:index = buf[4] =out=0   out=buf[4]=1  buf[1]=0
v:index = buf[4] =out=1	  out=buf[4]=2  buf[2]=0
v:index = buf[4] =out=2
psem(full) 等待释放
p:index = buf[3] =in=2    in=buf[3]=0  buf[0]=1
回调v
v:out=buf[4]=0 buf[0]=0
....
*/
void con(pid_t pid,int n)
{
  printf("<C> <%d> started\n",getpid());
  int index=buf[n+1];//out
  psem(full);
  psem(mutex2);
  buf[n+1]=(buf[n+1]+1)%n;
  buf[buf[n+1]]=0;//模拟取出,置0
  printf("C <%d> got an item from <%d>\n",getpid(),index);
  vsem(mutex2);
  vsem(empty);
}
int main()
{
   int t,k,n;
   printf("Please input n:\n");
   scanf("%d",&n);
   int shpid=init(n);
   k=rand()%1+1;
   pid_t pid;  //定义进程标示符 
   while(1)
   {
     srand((unsigned)time(NULL));//每次置随机数种子 
     pid=fork();//建立一个新进程(子进程) ,返回子进程的进程ID 在子进程中返回0
     //注意:子进程与原进程(父进程)共享代码段,并拥有父进程的其他资源(数据、堆栈等)的一个副本
     if(pid==0)   //子线程  
     {                                                                                                     
       t=rand()%2;//0,1 
       if(t==PRO)
         pro(pid,n);
       else if(t==CON)
         con(pid,n);
       return 0; //记得return 
      }
     else  //父进程 
       sleep(k);
    }
    int x1=shmdt(0);//断开已有的映射
	int x2=shctl(shpid,IPC_RMID,0);
    return 0;
}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本方法。编译原理不仅是计算机科学理论的重要组成部分,也是实现高效、可靠的计算机程序设计的关键。本文将对编译原理的基本概念、发展历程、主要内容和实际应用进行详细介绍编译原理是计算机专业的一门核心课程,旨在介绍编译程序构造的一般原理和基本

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值