本次實(shí)驗(yàn)比較簡(jiǎn)單,相較于前幾屆需要在linux0.11 底下實(shí)現(xiàn)信號(hào)量,這次只需要在linux下寫(xiě)個(gè)利用信號(hào)量解決生產(chǎn)者消費(fèi)者問(wèn)題已經(jīng)很簡(jiǎn)單了.
實(shí)驗(yàn)要求:
在Ubuntu上編寫(xiě)應(yīng)用程序“pc.c”拗窃,解決經(jīng)典的生產(chǎn)者—消費(fèi)者問(wèn)題,完成下面的功能:
- 建立一個(gè)生產(chǎn)者進(jìn)程黎做,N個(gè)消費(fèi)者進(jìn)程(N>1);
- 用文件建立一個(gè)共享緩沖區(qū)松忍;
- 生產(chǎn)者進(jìn)程依次向緩沖區(qū)寫(xiě)入整數(shù)0,1,2,...,M蒸殿,M>=500;
- 消費(fèi)者進(jìn)程從緩沖區(qū)讀數(shù)鸣峭,每次讀一個(gè)宏所,并將讀出的數(shù)字從緩沖區(qū)刪除,然后將本進(jìn)程ID和數(shù)字輸出到標(biāo)準(zhǔn)輸出摊溶;
緩沖區(qū)同時(shí)最多只能保存10個(gè)數(shù)爬骤。
要求用信號(hào)量來(lái)解決問(wèn)題:
pc.c中將會(huì)用到sem_open()、sem_close()莫换、sem_wait()和sem_post()等信號(hào)量相關(guān)的系統(tǒng)調(diào)用
要用到的函數(shù)說(shuō)明:
int fseek(FILE *stream, long offset, int fromwhere);
函數(shù)設(shè)置文件指針stream的位置霞玄。
如果執(zhí)行成功,stream將指向以fromwhere為基準(zhǔn)拉岁,偏移offset(指針偏移量)個(gè)字節(jié)的位置溃列,函數(shù)返回0。
如果執(zhí)行失敗(比如offset超過(guò)文件自身大小)膛薛,則不改變stream指向的位置,函數(shù)返回一個(gè)非0值补鼻。
size_t fread(void *buffer,size_t size,size_t count, FILE *stream );
buffer 是讀取的數(shù)據(jù)存放的內(nèi)存的指針
size 是每次讀取的字節(jié)數(shù)
count 是讀取次數(shù)
stream 是要讀取的文件的指針
從一個(gè)文件流中讀數(shù)據(jù)哄啄,最多讀取count個(gè)元素雅任,每個(gè)元素size字節(jié),如果調(diào)用成功返回實(shí)際讀取到的元素個(gè)數(shù)咨跌,如果不成功或讀到文件末尾返回 0沪么。
size_t fwrite(const void* buffer, size_t size, size_t count, FILE* stream);
(1)buffer:是一個(gè)指針,對(duì)fwrite來(lái)說(shuō)锌半,是要獲取數(shù)據(jù)的地址禽车;
(2)size:要寫(xiě)入內(nèi)容的單字節(jié)數(shù);
(3)count:要進(jìn)行寫(xiě)入size字節(jié)的數(shù)據(jù)項(xiàng)的個(gè)數(shù)刊殉;
(4)stream:目標(biāo)文件指針殉摔;
(5)返回實(shí)際寫(xiě)入的數(shù)據(jù)項(xiàng)個(gè)數(shù)count。
思路:建立文件緩沖區(qū)
0-9位存生產(chǎn)的數(shù)據(jù),第10位存儲(chǔ)當(dāng)前讀到的位置.
消費(fèi)者讀取的位置的時(shí)候要執(zhí)行兩次,一次讀出當(dāng)前所讀位置,第二次根據(jù)此位置計(jì)算位偏移;
fseek( fp, 10*sizeof(int), SEEK_SET );
fread( &Outpos, sizeof(int), 1, fp);
fseek( fp, Outpos*sizeof(int), SEEK_SET );
fread( &costnum, sizeof(int), 1, fp);
下面是實(shí)驗(yàn)所需文件pc.c:
#define __LIBRARY__
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#define ALLNUM 550
#define CUSTOMERNUM 5
#define BUFFERSIZE 10
void Producters(pid_t pid,FILE *fp);
void Customer(pid_t pid,FILE *fp);
sem_t *empty,*full,*mutex;
FILE *fp=NULL;
int Inpos=0;
int Outpos=0;
int main()
{
int i,j,k;
pid_t producter;
pid_t customer;
empty=(sem_t *)sem_open("empty",O_CREAT,0064,10);
full=(sem_t *)sem_open("full",O_CREAT,0064,0);
mutex=(sem_t*)sem_open("mutex",O_CREAT,0064,1);
//開(kāi)啟三個(gè)信號(hào)量
fp=fopen("products.txt","wb+");
fseek(fp,10*sizeof(int),SEEK_SET);
fwrite(&Outpos,sizeof(int),1,fp);
fflush(fp);
producter=fork();
if(producter==0)
{
Producters(producter,fp);
}
for (i=0;i<CUSTOMERNUM;i++)
{
customer=fork();
if(customer==0)
{
Customer(customer,fp);
}
}
wait(NULL);
wait(NULL);
wait(NULL);
wait(NULL);
wait(NULL);
wait(NULL);
//開(kāi)了6個(gè)進(jìn)程
sem_unlink("empty");
sem_unlink("full");
sem_unlink("mutex");
fclose(fp);
return 0;
}
void Producters(pid_t pid,FILE *fp)
{
int i=0;
for (i=0;i<ALLNUM;i++)
{
sem_wait(empty);
sem_wait(mutex);
fseek( fp, Inpos * sizeof(int), SEEK_SET );
fwrite(&i,sizeof(int),1,fp);
fflush(fp);
Inpos=(Inpos +1) % BUFFERSIZE;
sem_post(mutex);
sem_post(full);
}
return;
}
void Customer(pid_t pid,FILE *fp)
{
int j,productid;
for (j=0;j<ALLNUM/CUSTOMERNUM;j++)
{
sem_wait(full);
sem_wait(mutex);
fflush(stdout);
fseek(fp,10*sizeof(int),SEEK_SET);
fread(&Outpos,sizeof(int),1,fp);
fseek(fp,Outpos*sizeof(int),SEEK_SET);
fread(&productid,sizeof(int),1,fp);
printf("%d: %d\n",getpid(),productid);
fflush(stdout);
Outpos=(Outpos+1)% BUFFERSIZE;
fseek(fp,10*sizeof(int),SEEK_SET);
fwrite(&Outpos,sizeof(int),1,fp);
fflush(fp);
sem_post(mutex);
sem_post(empty);
}
return;
}
報(bào)告:
(1)在pc.c中去掉所有與信號(hào)量有關(guān)的代碼记焊,再運(yùn)行程序逸月,執(zhí)行效果有變化嗎?為什么會(huì)這樣遍膜?
執(zhí)行結(jié)果Customer的消費(fèi)數(shù)據(jù)沒(méi)有按遞增的順序輸出,而且且fread()函數(shù)將產(chǎn)生錯(cuò)誤;
原因:
因?yàn)闆](méi)有信號(hào)量P(S)控制碗硬,導(dǎo)致生產(chǎn)者可能在緩沖區(qū)滿(mǎn)后繼續(xù)生產(chǎn),導(dǎo)致沒(méi)有被消費(fèi)的數(shù)據(jù)被覆蓋瓢颅,使得消費(fèi)者消費(fèi)的數(shù)據(jù)不是遞增序列恩尾。
同時(shí),沒(méi)有信號(hào)量V(S)控制挽懦,導(dǎo)致消費(fèi)者可能在讀取所有數(shù)據(jù)后仍然繼續(xù)讀取翰意,導(dǎo)致讀取的數(shù)據(jù)無(wú)效。
沒(méi)有mutex信號(hào)量控制導(dǎo)致出現(xiàn)多進(jìn)程并發(fā)訪問(wèn)緩沖區(qū)巾兆,導(dǎo)致出現(xiàn)fread()錯(cuò)誤猎物。
(2)實(shí)驗(yàn)的設(shè)計(jì)者在第一次編寫(xiě)生產(chǎn)者——消費(fèi)者程序的時(shí)候,是這么做的:
Producer()
{ P(Mutex); //互斥信號(hào)量
生產(chǎn)一個(gè)產(chǎn)品item;
P(Empty); //空閑緩存資源
將item放到空閑緩存中;
V(Full); //產(chǎn)品資源
V(Mutex);
}
Consumer()
{ P(Mutex);
P(Full);
從緩存區(qū)取出一個(gè)賦值給item;
V(Empty);
消費(fèi)產(chǎn)品item;
V(Mutex);
}
這樣可行嗎角塑?如果可行蔫磨,那么它和標(biāo)準(zhǔn)解法在執(zhí)行效果上會(huì)有什么不同?如果不可行圃伶,那么它有什么問(wèn)題使它不可行堤如?
這樣做不可行,只有當(dāng)緩沖區(qū)可寫(xiě)或者可讀時(shí)窒朋,才能鎖定該臨界資源搀罢,否則容易出現(xiàn)緩沖區(qū)未鎖定(mutex=1),consumer鎖定該緩沖區(qū)侥猩,卻發(fā)現(xiàn)empty=10榔至,full=0,等待緩沖區(qū)有字符信號(hào)量欺劳,這樣程序會(huì)進(jìn)入死鎖狀態(tài)唧取。