#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;
}
複雜PC問題——信號量與共享存儲區
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.