#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;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。