OS:生産者消費者問題(マルチプロセス+共有メモリ+信号量)
8215 ワード
一.リード
一年ぶりにcoutを使ったとき、兄は涙を流した.久しぶりに会った感動で、ほとんど忘れてしまったが.大きな時間があるうちに、生産者の消費者問題を強固にして、純Cを使いましょう.コードを書くことができる幸せな時間を大切にします.
二.ぶんせき
生産者と消費者の問題は複数の相互協力のプロセスの間の抽象である.生産者と消費者の関係:
1.バッファへのアクセスは反発します.どちらもバッファを変更するため、一方がバッファを変更する場合、他方は変更できません.これが反発です.
2.一方の行為が他方に影響を与える.バッファが空いていないと、消費できますが、いつ空いていませんか?生産したら暇ではない.バッファがいっぱいで、生産できません.いつ不満ですか.消費したら不満だ.これは同期関係です.
この関係を記述するために、一方、共有メモリを使用してバッファを表す.一方,反発信号量制御を用いてバッファへのアクセスを制御し,同期信号量を用いて両者の依存関係を記述した.
三.共有ストレージ
共有ストレージはプロセス間通信の手段であり、通常、信号量同期または反発を使用して共有ストレージにアクセスする.共有ストレージの原理は、プロセスのアドレス空間を共有ストレージセグメントにマッピングすることである.LINUXでは、shmget関数を使用して共有メモリを作成または取得します.
1.作成
1)KEY//IPCを指定しないPRIVATEはメモリを作成する必要があることを指摘した.
//SHM_SIZEはバイトサイズを示す.
//SHM_MODEはアクセス権の字を0600のように指摘して、ユーザーはこのメモリを読み書きすることができます
int shmget(key_t IPC_PRIVATE,size_t SHM_SIZE,int SHM_MODE);
2)KEYの指定
//SHM_ならKEYが指す共有ストレージが既に存在する場合、共有ストレージのIDを返す.
//それ以外の場合は、共有ストレージを作成してIDを返します
int shmget(key_t SHM_KEY,size_t SHM_SIZE,int SHM_MODE);
2.アクセス
方法1
共有ストレージのIDを共有するだけでshmat関数により共有ストレージが占有する実際のアドレスを得ることができる.したがって、親プロセスのスタックに共有ストレージへのポインタを変数で格納することができ、fork後、サブプロセスはこのポインタを介して共有ストレージに容易にアクセスすることができる.
方法2
プロセス間に親子関係がなく、共有ストレージのKEYが協議されている場合、各プロセスにおいて、KEYおよびshmget関数により共有ストレージのI Dを取得し、さらにshmat関数により共有ストレージの実際のアドレスを取得し、最後にアクセスすることができる.
私の実装では、生産者を親プロセス、消費者をサブプロセスとして実装し、プロセス間のメモリ共有を方法によって実現します.
四.しんごうりょうしゅうごう
信号量は2種類の原語PとVがあり,Pはリソースをロックし,Vはリソースを解放する.LINUXでの信号量集合を用いたインタフェースは特に複雑である.使用した関数は次のとおりです.
1.信号量セットの作成または取得
//IPC_PRIVATEは、信号量セットの作成、NUM_を示すOF_SEMは、その集合にどれだけの信号量があるかを示す.FLAGS複雑追及なし
semget(IPC_PRIVATE, NUM_OF_SEM, FLAGS );
//SEM_KEYはkey_tタイプ
//SEM_ならKEYが表す信号量セットが存在すると、信号量セットのIDが返される
//存在しない場合、信号量セットを作成してIDを返す
semget(SEM_KEY, NUM_OF_SEM,FLAGS);
2.初期化信号量
作成されたプロシージャは信号量の初期値を指定していません.semctl関数を使用して指定する必要があります.
semctl(int semSetId , int semIdx , int cmd, union semun su);
ここでsemSetIdとは信号量セットのIDである、semIdxとは信号量セットのある信号量のインデックス(ゼロから)を指し、信号量を設定する値であればSETVALを記入すればよい、信号量の値を設定するためにsuを指定することができる.valは、設定する値です.
UBUNTUでunion semunを使用してコンパイルしたとき、いつもエラーが発生しました.
invalid use of undefined type ‘union semun’
Linuxでsemunの定義が削除されたそうです.カスタムsemunで解決できます.
五.コードぶんかい
1.ヘッダファイル
2.信号量
合計3つの信号量が必要です.
第1の信号量は、生産者がバッファが不満の場合に生産しなければならないことを制限するために使用され、同期信号量である.
第2の信号量は、消費者がバッファに製品がある場合に消費しなければならないことを制限するために使用され、同期信号量である.
第3の信号量は、生産者と消費者がバッファにアクセスする際に反発しなければならないことを制限するために用いられ、反発信号量である.
信号量セット、semgetの作成
3つの信号量、semctlを初期化するにはunion semunが必要です
信号量セットのある信号量に対する値をカプセル化する+1または-1動作
3.共有メモリの使用
4.生産プロセス
5.消費プロセス
六.コード全文
一年ぶりにcoutを使ったとき、兄は涙を流した.久しぶりに会った感動で、ほとんど忘れてしまったが.大きな時間があるうちに、生産者の消費者問題を強固にして、純Cを使いましょう.コードを書くことができる幸せな時間を大切にします.
二.ぶんせき
生産者と消費者の問題は複数の相互協力のプロセスの間の抽象である.生産者と消費者の関係:
1.バッファへのアクセスは反発します.どちらもバッファを変更するため、一方がバッファを変更する場合、他方は変更できません.これが反発です.
2.一方の行為が他方に影響を与える.バッファが空いていないと、消費できますが、いつ空いていませんか?生産したら暇ではない.バッファがいっぱいで、生産できません.いつ不満ですか.消費したら不満だ.これは同期関係です.
この関係を記述するために、一方、共有メモリを使用してバッファを表す.一方,反発信号量制御を用いてバッファへのアクセスを制御し,同期信号量を用いて両者の依存関係を記述した.
三.共有ストレージ
共有ストレージはプロセス間通信の手段であり、通常、信号量同期または反発を使用して共有ストレージにアクセスする.共有ストレージの原理は、プロセスのアドレス空間を共有ストレージセグメントにマッピングすることである.LINUXでは、shmget関数を使用して共有メモリを作成または取得します.
1.作成
1)KEY//IPCを指定しないPRIVATEはメモリを作成する必要があることを指摘した.
//SHM_SIZEはバイトサイズを示す.
//SHM_MODEはアクセス権の字を0600のように指摘して、ユーザーはこのメモリを読み書きすることができます
int shmget(key_t IPC_PRIVATE,size_t SHM_SIZE,int SHM_MODE);
2)KEYの指定
//SHM_ならKEYが指す共有ストレージが既に存在する場合、共有ストレージのIDを返す.
//それ以外の場合は、共有ストレージを作成してIDを返します
int shmget(key_t SHM_KEY,size_t SHM_SIZE,int SHM_MODE);
2.アクセス
方法1
共有ストレージのIDを共有するだけでshmat関数により共有ストレージが占有する実際のアドレスを得ることができる.したがって、親プロセスのスタックに共有ストレージへのポインタを変数で格納することができ、fork後、サブプロセスはこのポインタを介して共有ストレージに容易にアクセスすることができる.
方法2
プロセス間に親子関係がなく、共有ストレージのKEYが協議されている場合、各プロセスにおいて、KEYおよびshmget関数により共有ストレージのI Dを取得し、さらにshmat関数により共有ストレージの実際のアドレスを取得し、最後にアクセスすることができる.
私の実装では、生産者を親プロセス、消費者をサブプロセスとして実装し、プロセス間のメモリ共有を方法によって実現します.
四.しんごうりょうしゅうごう
信号量は2種類の原語PとVがあり,Pはリソースをロックし,Vはリソースを解放する.LINUXでの信号量集合を用いたインタフェースは特に複雑である.使用した関数は次のとおりです.
1.信号量セットの作成または取得
//IPC_PRIVATEは、信号量セットの作成、NUM_を示すOF_SEMは、その集合にどれだけの信号量があるかを示す.FLAGS複雑追及なし
semget(IPC_PRIVATE, NUM_OF_SEM, FLAGS );
//SEM_KEYはkey_tタイプ
//SEM_ならKEYが表す信号量セットが存在すると、信号量セットのIDが返される
//存在しない場合、信号量セットを作成してIDを返す
semget(SEM_KEY, NUM_OF_SEM,FLAGS);
2.初期化信号量
作成されたプロシージャは信号量の初期値を指定していません.semctl関数を使用して指定する必要があります.
semctl(int semSetId , int semIdx , int cmd, union semun su);
ここでsemSetIdとは信号量セットのIDである、semIdxとは信号量セットのある信号量のインデックス(ゼロから)を指し、信号量を設定する値であればSETVALを記入すればよい、信号量の値を設定するためにsuを指定することができる.valは、設定する値です.
UBUNTUでunion semunを使用してコンパイルしたとき、いつもエラーが発生しました.
invalid use of undefined type ‘union semun’
Linuxでsemunの定義が削除されたそうです.カスタムsemunで解決できます.
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including */
#else
/* according to X/OPEN we have to define it ourselves */
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
#endif
五.コードぶんかい
1.ヘッダファイル
#include "stdio.h" // printf
#include // shmget shmat
#include // semget
#include // exit
2.信号量
合計3つの信号量が必要です.
第1の信号量は、生産者がバッファが不満の場合に生産しなければならないことを制限するために使用され、同期信号量である.
第2の信号量は、消費者がバッファに製品がある場合に消費しなければならないことを制限するために使用され、同期信号量である.
第3の信号量は、生産者と消費者がバッファにアクセスする際に反発しなければならないことを制限するために用いられ、反発信号量である.
信号量セット、semgetの作成
if((semSetId = semget(IPC_PRIVATE,3,SEM_MODE)) < 0)
{
perror("create semaphore failed");
exit(1);
}
3つの信号量、semctlを初期化するにはunion semunが必要です
union semun su;
// , su union semun
su.val = N_BUFFER;//
if(semctl(semSetId,0,SETVAL, su) < 0){
perror("semctl failed");
exit(1);
}
su.val = 0;//
if(semctl(semSetId,1,SETVAL,su) < 0){
perror("semctl failed");
exit(1);
}
su.val = 1;// 1
if(semctl(semSetId,2,SETVAL,su) < 0){
perror("semctl failed");
exit(1);
}
信号量セットのある信号量に対する値をカプセル化する+1または-1動作
//semSetId id
//semNum
void waitSem(int semSetId,int semNum)
{
struct sembuf sb;
sb.sem_num = semNum;
sb.sem_op = -1;//
sb.sem_flg = SEM_UNDO;//
// sembuf [] ,
//
if(semop(semSetId,&sb,1) < 0){
perror("waitSem failed");
exit(1);
}
}
void sigSem(int semSetId,int semNum)
{
struct sembuf sb;
sb.sem_num = semNum;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
// sembuf [] ,
//
if(semop(semSetId,&sb,1) < 0){
perror("waitSem failed");
exit(1);
}
}
3.共有メモリの使用
//
struct ShM{
int start;
int end;
}* pSM;
//
if((shmId = shmget(IPC_PRIVATE,SHM_SIZE,SHM_MODE)) < 0)
{
perror("create shared memory failed");
exit(1);
}
//shmat void*
pSM = (struct ShM *)shmat(shmId,0,0);
//
pSM->start = 0;
pSM->end = 0;
4.生産プロセス
while(1)
{
waitSem(semSetId,0);//
waitSem(semSetId,2);//
produce();
sigSem(semSetId,2);//
sleep(1);//
sigSem(semSetId,1);//
}
5.消費プロセス
while(1)
{
waitSem(semSetId,1);//
waitSem(semSetId,2);//
consume();// ,
sigSem(semSetId,2);//
sigSem(semSetId,0);// ,
sleep(2);//
}
六.コード全文
#include "stdio.h"
#include
#include
#include
#define SHM_SIZE (1024*1024)
#define SHM_MODE 0600
#define SEM_MODE 0600
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including */
#else
/* according to X/OPEN we have to define it ourselves */
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
};
#endif
struct ShM{
int start;
int end;
}* pSM;
const int N_CONSUMER = 3;//
const int N_BUFFER = 5;//
int shmId = -1,semSetId=-1;
union semun su;//sem union,
//semSetId id
//semNum
void waitSem(int semSetId,int semNum)
{
struct sembuf sb;
sb.sem_num = semNum;
sb.sem_op = -1;//
sb.sem_flg = SEM_UNDO;//
// sembuf [] ,
//
if(semop(semSetId,&sb,1) < 0){
perror("waitSem failed");
exit(1);
}
}
void sigSem(int semSetId,int semNum)
{
struct sembuf sb;
sb.sem_num = semNum;
sb.sem_op = 1;
sb.sem_flg = SEM_UNDO;
// sembuf [] ,
//
if(semop(semSetId,&sb,1) < 0){
perror("waitSem failed");
exit(1);
}
}
//
void produce()
{
int last = pSM->end;
pSM->end = (pSM->end+1) % N_BUFFER;
printf(" %d
",last);
}
//
void consume()
{
int last = pSM->start;
pSM->start = (pSM->start + 1)%N_BUFFER;
printf(" %d
",last);
}
void init()
{
//
if((shmId = shmget(IPC_PRIVATE,SHM_SIZE,SHM_MODE)) < 0)
{
perror("create shared memory failed");
exit(1);
}
pSM = (struct ShM *)shmat(shmId,0,0);
pSM->start = 0;
pSM->end = 0;
//
// : , ,
// : , ,
// : ,
if((semSetId = semget(IPC_PRIVATE,3,SEM_MODE)) < 0)
{
perror("create semaphore failed");
exit(1);
}
// , su union semun
su.val = N_BUFFER;//
if(semctl(semSetId,0,SETVAL, su) < 0){
perror("semctl failed");
exit(1);
}
su.val = 0;//
if(semctl(semSetId,1,SETVAL,su) < 0){
perror("semctl failed");
exit(1);
}
su.val = 1;// 1
if(semctl(semSetId,2,SETVAL,su) < 0){
perror("semctl failed");
exit(1);
}
}
int main()
{
int i = 0,child = -1;
init();
// (N_CONSUMER)
for(i = 0; i < N_CONSUMER; i++)
{
if((child = fork()) < 0)// fork
{
perror("the fork failed");
exit(1);
}
else if(child == 0)//
{
printf(" %d ,PID = %d
",i,getpid());
while(1)
{
waitSem(semSetId,1);//
waitSem(semSetId,2);//
consume();// ,
sigSem(semSetId,2);//
sigSem(semSetId,0);// ,
sleep(2);//
}
break;//
}
}
//
if(child > 0)
{
while(1)
{
waitSem(semSetId,0);//
waitSem(semSetId,2);//
produce();
sigSem(semSetId,2);//
sleep(1);//
sigSem(semSetId,1);//
}
}
return 0;
}