生產者消費者問題

一、實驗目的

1、對於不同系統的平臺學會使用調用系統函數,掌握進程創建的方法和步驟。

2、理解不同進程之間的通信機制,掌握生產者消費者問題。

二、實驗內容

       生產者消費者問題(需要Windows版本和Linux版本)

•   一個大小爲3的緩衝區,初始爲空

•   2個生產者

– 隨機等待一段時間,往緩衝區添加數據

– 若緩衝區已滿,等待消費者取走數據後再添加

– 重複6次

•   3個消費者

– 隨機等待一段時間,從緩衝區讀取數據

– 若緩衝區爲空,等待生產者添加數據後再讀取

– 重複4次

說明:

1、顯示每次添加和讀取數據的時間及緩衝區的狀態

2、生產者和消費者用進程模擬。

三、實驗環境

1、Windows環境下使用Dev-c++和Notepad++;

2、Linux環境是在虛擬機中裝Ubuntu15.10;如圖1所示

圖1

3、在Linux中使用Linux自帶的文本編輯器(gedit)進行編輯,在終端進行編譯和運行。

四、實驗方法與步驟

1、在Windows下實現

(1)、首先理解在理論中學習的生產者消費者問題,然後根據本實驗內容的要求,將已知條件轉化爲編譯器能識別的宏或者變量,在此我定義爲宏;(如圖2所示)

圖2

(2)、定義緩衝區的結構,此緩衝區用共享內存實現的。(如圖3所示)

圖3

(3)、生產者與消費者需要用進程模擬,就需要考慮進程之間的通信;同時生產者往緩衝區寫入數據、消費者從緩衝區讀取數據,不同進程之間同步進行,牽涉到進程通信和共享內存,解決這個問題的方案比較多,此處我才用的是文件映射。(如圖4所示) 


圖4

(4)、編寫通用函數:本實驗中的隨機等待時間函數get_random()、生產者往緩衝區寫入數據的函數get_letter()、通過參數傳遞進程的ID創建當前進程的克隆進程函數StartClone()、創建共享內存並返回創建後的文件句柄的函數MakeSharedFile();

(5)、MakeShareFile()函數思路:首先利用CreateFileMapping()函數創建臨時的文件映射對象;如果文件映射成功,利用MapViewOfFile()函數把文件映射的一個視圖映射到當前進程的地址空間、並返回文件映射的起始地址;若起始地址有效,將前一步的地址所指向的存儲空間清0;在當前進程的地址空間中解除對一個文件映射對象的映射;最終返回臨時的文件映射對象句柄。(如圖5所示)

圖5

(6)、編寫主函數

對於主進程:

①、利用上述所寫的MakeSharedFile()函數創建數據文件;

②、利用OpenFileMapping()函數打開文件映射,利用MapViewOfFile()函數把打開的文件映射到主進程的地址空間;

③、如果第②步映射成功,對主進程進行處理,將緩衝區的頭尾指針都指向0,然後創建信號量,利用UnmapViewOfFile()函數在當前進程的地址空間中解除對緩衝區文件的映射;

④、如果第②步映射失敗,輸出提示信息;

⑤、利用CloseHandle()函數關閉文件映射對象句柄;

⑥、利用StartClone()函數創建2個生產者進程和3個消費者進程;

⑦、利用WaitForSingleObject()函數和CloseHandle()函數等待5個子進程運行結束,並關閉各個子進程的句柄。

對於2個生產者進程:

①、利用OpenFileMapping()函數打開文件映射,利用MapViewOfFile()函數把打開的文件映射到此進程的地址空間;

②、如果第①步映射成功,對此進程進行處理,利用OpenSemaphore()函數打開信號量、並將打開的信號量賦值給共享內存中的信號量;每個生產者各工作6次,利用WaitForSingleObject()函數等待一個子進程結束並睡眠一個隨機的時間,利用get_letter()函數隨機產生一個字母放入環形緩衝區中,並將當前位置記錄下來,此處從0開始;取當前的系統時間,並將每一次寫入後的環形緩衝區狀態輸出,並顯示進程的操作;利用UnmapViewOfFile()函數將當前進程句柄關閉;

③、如果第①步映射失敗,輸出提示信息;

④、利用CloseHandle()函數將第①步打開的文件映射對象句柄關閉。

對於3個消費者進程:

①、利用OpenFileMapping()函數打開文件映射,利用MapViewOfFile()函數把打開的文件映射到此進程的地址空間;

②、如果第①步映射成功,對此進程進行處理,利用OpenSemaphore()函數打開信號量、並將打開的信號量賦值給共享內存中的信號量;每個消費者各工作4次,利用WaitForSingleObject()函數等待一個子進程結束並睡眠一個隨機的時間,取出環形緩衝區中的數據,若緩衝區爲空(頭尾指針指向同一個位置),並將當前位置記錄下來,並賦值給緩衝區結構體中的is_empty成員,取當前的系統時間,並將每一次讀取後的環形緩衝區狀態輸出,並顯示進程的操作;利用ReleaseSemaphore()函數釋放此進程的信號量,並利用UnmapViewOfFile()函數將當前進程句柄關閉;

③、如果第①步映射失敗,輸出提示信息;

④、利用CloseHandle()函數將第①步打開的文件映射對象句柄關閉。

(7)、各個子進程運行結束後,關閉主進程的句柄。

2、在Linux下實現

(1)、首先理解在理論中學習的生產者消費者問題,然後根據本實驗內容的要求,將已知條件轉化爲編譯器能識別的宏或者變量,在此我定義爲宏;如下所示:

//2個生產者,每個生產者工作6次

#defineNeed_Producer 2

#defineWorks_Producer 6

 

//3個消費者,每個消費者工作4次

#defineNeed_Customer 3

#defineWorks_Customer 4

 

//緩衝區爲3

#definebuffer_len 3

#defineMYBUF_LEN (sizeof(struct mybuffer))

 

#define SHM_MODE0600

#defineSEM_ALL_KEY 1234

#defineSEM_EMPTY 0

#define SEM_FULL1

(2)、定義緩衝區的結構(如圖6所示)

圖6

(3)、編寫通用函數:本實驗中的隨機等待時間函數get_random()、生產者往緩衝區寫入數據的函數get_letter()、生產者與消費者的PV操作(如圖7所示)

圖7

(4)、編寫主函數:

對於主函數:

①、創建一個信號量集合,若返回的信號量集合的標識號不小於0,輸出提示;

②、對信號量進行控制操作;

③、申請一個共享內存區,成功返回共享內存區的標識,若此標識小於0,則申請共享內存區失敗並輸出相應提示;

④、將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1,若返回-1則輸出相應提示;

⑤、初始化環形緩衝區中的數據成員。

對於2個生產者進程:

①、創建新的進程,若所創建的進程標識符小於0,則創建進程失敗,並輸出相應提示;

②、若此進程爲子進程,將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1,並輸出相應提示;

③、2個生產者進程各執行6次,利用上面的P操作,然後睡眠一段隨機時間;利用get_letter()函數得到一個隨機的字母並寫入環形緩衝區,將is_empty設置爲0;取當前的系統時間,對每一次寫入後、輸出當前緩衝區的狀態,並顯示進程的操作;將第①步創建的信號量執行V操作;

④、將共享段與進程之間解除連接。

對於3個消費者進程:

①、創建新的進程,若所創建的進程標識符小於0,則創建進程失敗,並輸出相應提示;

②、若此進程爲子進程,將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1,並輸出相應提示;

③、3個消費者進程各執行4次,利用上面的P操作,然後睡眠一段隨機時間;讀取環形緩衝區中的數據,如頭尾指針指向同一單元,則將當前單元索引賦值給is_empty;取當前的系統時間,對每一次讀取後、輸出當前緩衝區的狀態,並顯示進程的操作;將第①步創建的信號量執行V操作;

④、將共享段與進程之間解除連接。

(5)、主進程等待所有子進程運行結束,將共享段與進程之間解除連接。

五、實驗結果

1、Windows下的實驗截圖(如圖8所示)


圖8 

2、Linux下的實驗截圖(如圖9所示)



圖9 

六、實驗分析與總結

Windows內存管理器使用局部對象來實現共享內存。文件中的字節一對一映射到虛地址空間。讀內存的時候實際上是從文件獲取數據,修改內存最終將寫回到文件。進程間通信、共享數據有很多種方法,文件映射是常用的一種方法。因爲mapping對象在系統中是全局的,一個進程創建的Mapping對象可以從另外一個進程打開,映射視圖就是進程間共享的內存了。一般在共享的內存數據量比較大時,選擇使用文件映射進行共享。使用CreateFileMapping()創建文件映射,OpenFileMapping()打開文件映射。調用 MapViewOfFile()將文件映射到進程的地址空間。

共享主存段爲進程提供了直接通過主存進行通信的有效手段。使用shmget()系統調用實現共享主存段的創建,shmget()返回共享內存區的 ID。對於已經申請到的共享段,進程需把它附加到自己的虛擬空間中才能對其進行讀寫。使用shmat()將共享內存附加到進程的地址空間。程序退出時調用 shmdt()將共享存儲區從本地進程中解除連接,但它不刪除共享存儲區。shmctl()調用可實現多種共享存儲區操作,包括刪除和獲取信息。在UNIX系統中,一個或多個信號量構成一個信號量集合。使用信號量機制可以實現進程之間的同步和互斥,允許併發進程一次對一組信號量進行相同或不同的操作。每個P、V操作不限於減1或加1,而是可以加減任何整數。在進程終止時,系統可根據需要自動消除所有被進程操作過的信號量的影響。 semget()調用建立一個信號量集合,semctl()調用對信號量執行控制操作,進而實現P、V操作。

七、實驗源代碼

Windows版本
// 名稱:ProducerAndCustomer.h
// 描述:生產者消費者問題
// 作者:野狼
// 日期:2017.3.27

#ifndef __PRODUCERANDCUSTOMER_H
#define __PRODUCERANDCUSTOMER_H
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#include <unistd.h>

//1個主進程序號記爲0
#define mainNum 0
//2個生產者序號(1~2)
#define Producer_Num_from 1
#define Producer_Num_to 2
//3個消費者序號(3~5)
#define Customer_Num_from 3
#define Customer_Num_to 5

//2個生產者,每個生產者工作6次
#define Need_Producer 2
#define Works_Producer 6

//3個消費者,每個消費者工作4次
#define Need_Customer 3
#define Works_Customer 4

//緩衝區爲3
#define buffer_len 3

#define SHM_NAME "BUFFER"

//文件映射對象句柄
static HANDLE handleFileMapping;

//子進程句柄數組
static HANDLE subProHandleArray[5+1];

//緩衝區的結構
struct mybuffer
{
	char str[buffer_len];
	int head;
	int tail;
	int is_empty;
};

//共享主存區的結構
struct shareMemory
{
	struct mybuffer data;
	HANDLE semEmpty;
	HANDLE semFull;
};

//得到1000以內的一個隨機數
int get_random()
{
	int digit;
	srand((unsigned)(GetCurrentProcessId() + time(NULL)));
	digit = rand() % 1000;
	return digit;
}

//得到A~Z的一個隨機字母
char get_letter()
{
	char letter;
	srand((unsigned)(getpid() + time(NULL)));
	letter = (char)((rand() % 26) + 'A');
	return letter;
}

//通過參數傳遞進程的ID創建當前進程的克隆進程
void StartClone(int subProID)
{
	char szFilename[MAX_PATH];
	char szCmdLine[MAX_PATH];
	STARTUPINFO si;
	PROCESS_INFORMATION pi;
	//獲得當前可執行文件名,hModule爲NULL返回當前可執行文件的路徑名;存放給定模塊的路徑和文件名;緩衝區大小
	GetModuleFileName(NULL, szFilename, MAX_PATH);
	sprintf(szCmdLine, "\"%s\" %d",szFilename, subProID);
	
	memset(&si, 0, sizeof(STARTUPINFO));
	si.cb = sizeof(STARTUPINFO);
	
	//創建子進程
	BOOL bCreateOK = CreateProcess(szFilename, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
	//將新創建進程的句柄賦值給進程ID爲subProID的子進程
	subProHandleArray[subProID] = pi.hProcess;	
	return;
}

//創建共享內存
HANDLE MakeSharedFile()
{
	//創建臨時的文件映射對象(用INVALID_HANDLE_VALUE代替真正的文件句柄)
	HANDLE handleFileMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(struct shareMemory), SHM_NAME);
    if (handleFileMapping == NULL || handleFileMapping == INVALID_HANDLE_VALUE)
	{
		printf("創建文件映射失敗:%d\n",GetLastError());
		return;
	}
	if (handleFileMapping != INVALID_HANDLE_VALUE)
	{
		//把文件映射對象的一個視圖映射到當前進程的地址空間,返回值爲文件映射的起始地址
		LPVOID pData = MapViewOfFile(handleFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);//高32位,低32位,整個文件映射
		if (pData == NULL)
		{
			printf("創建文件映射視圖失敗:%d\n",GetLastError());
			return;
		}
		if (pData != NULL)
		{
			//將指定的存儲空間清0
			ZeroMemory(pData, sizeof(struct shareMemory));
		}
		//在當前進程的地址空間中解除對一個文件映射對象的映射
		UnmapViewOfFile(pData);
	}
	return handleFileMapping;
}

#endif
// 名稱:ProducerAndCustomer.c
// 描述:生產者消費者問題
// 作者:野狼
// 日期:2017.3.27

#include "ProducerAndCustomer.h"

int main(int argc, char * argv[])
{
	int i, j, k;
	int nextIndex = 1;	//下一個要執行的進程序號 
	int curProNum = mainNum;
	char lt;
	SYSTEMTIME time;
	
	//printf("緩衝區大小爲:%d.\n",buffer_len);
	//printf("%d個生產者,分別寫入%d次.\n",Need_Producer, Works_Producer);
	//printf("%d個消費者,分別讀取%d次.\n",Need_Customer, Works_Customer);
	
	//如果有參數,就作爲子進程ID
	if (argc > 1)
	{
		sscanf(argv[1], "%d", &curProNum);
	}
	
	//對於主進程
	if (curProNum == mainNum)
	{
		printf("主進程開始運行!\n");
		
		//創建共享內存 
		handleFileMapping = MakeSharedFile();
		//映射視圖
		HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
		if (hFileMapping == NULL)
		{
			printf("打開文件映射失敗:%d\n",GetLastError());
			return;
		}
		LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if (pFile == NULL)
		{
			printf("文件映射視圖失敗:%d\n",GetLastError());
			return;
		}
		else
		{
			struct shareMemory *sham = (struct shareMemory*)(pFile);
			sham->data.head = 0;
			sham->data.tail = 0;
			sham->semEmpty = CreateSemaphore(NULL, buffer_len, buffer_len, "SEM_EMPTY");
			sham->semFull = CreateSemaphore(NULL, 0, buffer_len, "SEM_FULL");
			//取消文件映射
			UnmapViewOfFile(pFile);
			pFile = NULL;
		}
		CloseHandle(hFileMapping);
		
		//創建5個子進程
		while (nextIndex <= 5)
		{
			StartClone(nextIndex++);
		}
		
		//等待子進程運行結束
		for (k=1; k<6; k++)
		{
			WaitForSingleObject(subProHandleArray[k], INFINITE);
			CloseHandle(subProHandleArray[k]);
		}
		
		//輸出結束信息
		printf("主進程運行結束!\n");
	}
	
	//2個生產者進程
	else if (curProNum >= Producer_Num_from && curProNum <= Producer_Num_to)
	{
		//映射視圖
		HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
		if (hFileMapping == NULL)
		
		{
			printf("打開文件映射失敗:%d\n",GetLastError());
			return;
		}
		LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if (pFile == NULL)
		{
			printf("文件映射視圖失敗:%d\n",GetLastError());
			return;
		}
		else
		{
			struct shareMemory *sham = (struct shareMemory*)(pFile);
			sham->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
			sham->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
			
			for (i=0; i<Works_Producer; i++)
			{
				WaitForSingleObject(sham->semEmpty, INFINITE);
				Sleep(get_random());
				sham->data.str[sham->data.tail] = lt = get_letter();
				sham->data.tail = (sham->data.tail + 1) % buffer_len;
				sham->data.is_empty = 0;

				GetSystemTime(&time);
				printf("%04d:%02d:%02d-%02d:%02d:%02d\t",time.wYear,time.wMonth,time.wDay,time.wHour+8,time.wMinute,time.wSecond);
				
				j = (sham->data.tail-1 >= sham->data.head) ? (sham->data.tail - 1) : (sham->data.tail -1 + buffer_len);
				
				for (j; !(sham->data.is_empty)&&(j>=sham->data.head); j--)
				{
					printf("%c", sham->data.str[j % buffer_len]);
				}
				printf("\t 生產者%d進程 寫入 '%c'.\n",curProNum-mainNum, lt);
				ReleaseSemaphore(sham->semFull, 1, NULL);
			}
			UnmapViewOfFile(pFile);
			pFile = NULL;
		}
		CloseHandle(hFileMapping);
	}
	
	//3個消費者進程
	else if (curProNum >= Customer_Num_from && curProNum <= Customer_Num_to)
	{
		HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
		if (hFileMapping == NULL)
		{
			printf("打開文件映射失敗:%d\n",GetLastError());
			return;
		}
		LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
		if (pFile == NULL)
		{
			printf("文件映射視圖失敗:%d\n",GetLastError());
			return;
		}
		else
		{
			struct shareMemory *sham = (struct shareMemory*)(pFile);
			sham->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
			sham->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
			
			for (i=0; i<Works_Customer; i++)
			{
				WaitForSingleObject(sham->semFull, INFINITE);
				Sleep(get_random());
				lt = sham->data.str[sham->data.head];
				sham->data.head = (sham->data.head + 1) % buffer_len;
				sham->data.is_empty = (sham->data.head == sham->data.tail);
				GetSystemTime(&time);
				printf("%04d:%02d:%02d-%02d:%02d:%02d\t",time.wYear,time.wMonth,time.wDay,time.wHour+8,time.wMinute,time.wSecond);
				
				j = (sham->data.tail-1 >= sham->data.head) ? (sham->data.tail - 1) : (sham->data.tail -1 + buffer_len);
				
				for (j; !(sham->data.is_empty)&&(j>=sham->data.head); j--)
				{
					printf("%c", sham->data.str[j % buffer_len]);
				}
				printf("\t 消費者%d進程 讀取 '%c'. \n",curProNum-Producer_Num_to, lt);
				ReleaseSemaphore(sham->semEmpty, 1, NULL);
			}
			UnmapViewOfFile(pFile);
			pFile = NULL;
		}
		CloseHandle(hFileMapping);
	}
	
	//關閉主進程的句柄
	CloseHandle(handleFileMapping);
	handleFileMapping = INVALID_HANDLE_VALUE;
	return 0;
}


Linux版本
/********************************************/
/*名稱:ProducerAndCustomer.h
/*描述:生產者與消費者的子函數定義和聲明
/*作者:野狼
/*日期:2017-03-26
/********************************************/

#ifndef __PRODUCERANDCUSTOMER_H
#define __PRODUCERANDCUSTOMER_H
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>

//2個生產者,每個生產者工作6次
#define Need_Producer 2
#define Works_Producer 6

//3個消費者,每個消費者工作4次
#define Need_Customer 3
#define Works_Customer 4

//緩衝區爲3
#define buffer_len 3

#define MYBUF_LEN (sizeof(struct mybuffer))

#define SHM_MODE 0600//可讀可寫
#define SEM_ALL_KEY 1234
#define SEM_EMPTY 0
#define SEM_FULL 1

//緩衝區的結構
struct mybuffer
{
	char str[buffer_len];
	int head;
	int tail;
	int is_empty;
};

//得到10以內的一個隨機數
int get_random()
{
	int digit;
	srand((unsigned)(getpid() + time(NULL)));
	digit = rand() % 10;
	return digit;
}

//得到A~Z的一個隨機字母
char get_letter()
{
	char letter;
	srand((unsigned)(getpid() + time(NULL)));
	letter = (char)((rand() % 26) + 'A');
	return letter;
}

//P操作
void P(int sem_id, int sem_num)
{
	struct sembuf xx;
	xx.sem_num = sem_num;//信號量的索引
	xx.sem_op = -1;//信號量的操作值
	xx.sem_flg = 0;//訪問標誌
 	semop(sem_id,&xx,1);//一次需進行的操作的數組sembuf中的元素數爲1
}

//V操作
void V(int sem_id, int sem_num)
{
	struct sembuf xx;
	xx.sem_num = sem_num;
	xx.sem_op = 1;
	xx.sem_flg = 0;
	semop(sem_id,&xx,1);
}

#endif
/********************************************/
/*名稱:ProducerAndCustomer.c
/*描述:生產者與消費者的主函數
/*作者:野狼
/*日期:2017-03-28
/********************************************/

#include "ProducerAndCustomer.h"

int main(int argc, char *argv[])
{
	int i, j;
	int shm_id, sem_id;
	int num_Producer = 0, num_Customer = 0;
	struct mybuffer *shmptr;
	char lt;

	time_t now;
	struct tm *timenow;

	pid_t pid_p, pid_c;

	//創建一個信號量集合(信號量數爲2),返回值爲信號量集合的標識號(關鍵字,信號量數,創建或打開的標誌)
	sem_id = semget(SEM_ALL_KEY,2,IPC_CREAT|0660);
	if (sem_id >= 0)
	{
		printf("主進程開始運行! \n");
	}
	//對信號量執行控制操作(信號量集合標識,信號量的索引,要執行的操作命令,設置或返回信號量的參數)
	semctl(sem_id, SEM_EMPTY, SETVAL, buffer_len);
	semctl(sem_id, SEM_FULL, SETVAL, 0);

	//申請一個共享內存區,成功返回爲共享內存區的標識
	shm_id = shmget(IPC_PRIVATE, MYBUF_LEN, SHM_MODE);
	if (shm_id < 0)
	{
		printf("申請共享內存區失敗!\n");
		exit(1);
	}

	//將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1
	shmptr = shmat(shm_id, 0, 0);
	if (shmptr == (void *)-1)
	{
		printf("將共享段附加到申請通信的進程空間失敗!\n");
		exit(1);
	}

	shmptr->head = 0;
	shmptr->tail = 0;
	shmptr->is_empty = 1;
	
	//2個生產者進程
	while ((num_Producer++) < Need_Producer)
	{
		pid_p = fork();
		if (pid_p < 0)
		{
			printf("創建進程失敗!\n");
			exit(1);
		}
		//如果是生產者子進程,開始創建生產者
		if (pid_p == 0)
		{
			//將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1
			shmptr = shmat(shm_id, 0, 0);
			if (shmptr == (void *)-1)
			{
				printf("將共享段附加到申請通信的進程空間失敗!\n");
				exit(1);
			}
		 	for (i=0; i<Works_Producer; i++)
			{
				P(sem_id, SEM_EMPTY);
				sleep(get_random());
				shmptr->str[shmptr->tail] = lt = get_letter();
				shmptr->tail = (shmptr->tail + 1) % buffer_len;
				shmptr->is_empty = 0;
				time(&now);
				timenow = localtime(&now);
				now = time(NULL);
				printf("%s ",asctime(timenow));
				
				j = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1+buffer_len);
				for (j; !(shmptr->is_empty) && j >= shmptr->head; j--)
				{
					printf("%c", shmptr->str[j%buffer_len]);
				}
				printf("\t 生產者 %d  放入 '%c'. \n",num_Producer,lt);
				fflush(stdout);
				V(sem_id,SEM_FULL);
			}
			//將共享段與進程之間解除連接
			shmdt(shmptr);
			exit(0);
		}
	}
	//3個消費者進程
	while ((num_Customer++) < Need_Customer)
	{
		pid_c = fork();
		if (pid_c < 0)
		{
			printf("創建進程失敗!\n");
			exit(1);
		}
		//如果是消費者子進程,開始創建消費者
		if (pid_c == 0)
		{
			//將共享段附加到申請通信的進程空間;成功時返回共享內存附加到進程空間的虛地址,失敗時返回-1
			shmptr = shmat(shm_id, 0, 0);
			if (shmptr == (void *)-1)
			{
				printf("將共享段附加到申請通信的進程空間失敗!\n");
				exit(1);
			}
		 	for (i=0; i<Works_Customer; i++)
			{
				P(sem_id, SEM_FULL);
				sleep(get_random());
				lt = shmptr->str[shmptr->head];
				shmptr->head = (shmptr->head + 1) % buffer_len;
				shmptr->is_empty = (shmptr->head == shmptr->tail);

				time(&now);
				timenow = localtime(&now);
				now = time(NULL);
				printf("%s ",asctime(timenow));

				j = (shmptr->tail-1 >= shmptr->head) ? (shmptr->tail-1) : (shmptr->tail-1+buffer_len);
				for (j; !(shmptr->is_empty) && j >= shmptr->head; j--)
				{
					printf("%c", shmptr->str[j%buffer_len]);
				}
				printf("\t 消費者 %d  取出 '%c'. \n",num_Customer,lt);
				fflush(stdout);
				V(sem_id,SEM_EMPTY);
			}
			//將共享段與進程之間解除連接
			shmdt(shmptr);
			exit(0);
		}
	}

	//主進程最後退出
	while (wait(0) != -1);
	//將共享段與進程之間解除連接
	shmdt(shmptr);
	//對共享內存區執行控制操作
	shmctl(shm_id,IPC_RMID,0);//當cmd爲IPC_RMID時,刪除該共享段
	shmctl(sem_id,IPC_RMID,0);
	printf("主進程運行結束!\n");
	fflush(stdout);
	exit(0);
}


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