複雜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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章