转:基于进程与共享主存实现的生产者消费者问题(Windows/Linux)
应同学需求,在考试之后帖出这个实验程序。与网络上可以下载到的大多数生产者消费者问题模拟程序不同,本程序使用进程(而非线程)模拟,基于共享内存读写“产品”。同时给出了Windowst和Linux下实现的源代码,使用gcc编译通过。
本程序仅供参考,请勿挪做他用。
源代码下载: 这里 (Linux 版)或者 这里 (Windows 版)
linux代码:
View Code
#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define NEED_P 2
#define NEED_C 3
#define WORKS_P 6
#define WORKS_C 4
#define BUF_LENGTH (sizeof(struct mybuffer))
#define LETTER_NUM 3
#define SHM_MODE 0600
#define SEM_ALL_KEY 1234
#define SEM_EMPTY 0
#define SEM_FULL 1
//缓冲区结构(循环队列)
struct mybuffer
{
char letter[LETTER_NUM];
int head;
int tail;
int is_empty;
};
//得到5以内的一个随机数
int get_random()
{
int t;
srand((unsigned)(getpid() + time(NULL)));
t = rand() % 5;
return t;
}
//得到A~Z的一个随机字母
char get_letter()
{
char a;
srand((unsigned)(getpid() + time(NULL)));
a = (char)((char)(rand() % 26) + 'A');
return a;
}
//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);
}
//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);
}
//主函数
int main(int argc, char * argv[])
{
int i, j;
int shm_id, sem_id;
int num_p = 0, num_c = 0;
struct mybuffer * shmptr;
char lt;
time_t now;
pid_t pid_p, pid_c;
sem_id = semget(SEM_ALL_KEY, 2, IPC_CREAT | 0660);
if (sem_id >= 0)
{
printf("Main process starts. Semaphore created.\n");
}
semctl(sem_id, SEM_EMPTY, SETVAL, LETTER_NUM);
semctl(sem_id, SEM_FULL, SETVAL, 0);
if ((shm_id = shmget(IPC_PRIVATE, BUF_LENGTH, SHM_MODE)) < 0)
{
printf("Error on shmget.\n");
exit(1);
}
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
shmptr->head = 0;
shmptr->tail = 0;
shmptr->is_empty = 1;
while ((num_p++) < NEED_P)
{
if ((pid_p = fork()) < 0)
{
printf("Error on fork.\n");
exit(1);
}
//如果是子进程,开始创建生产者
if (pid_p == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_P; i++)
{
p(sem_id, SEM_EMPTY);
sleep(get_random());
shmptr->letter[shmptr->tail] = lt = get_letter();
shmptr->tail = (shmptr->tail + 1) % LETTER_NUM;
shmptr->is_empty = 0;
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % LETTER_NUM]);
}
printf("\tProducer %d puts '%c'.\n", num_p, lt);
fflush(stdout);
v(sem_id, SEM_FULL);
}
shmdt(shmptr);
exit(0);
}
}
while (num_c++ < NEED_C)
{
if ((pid_c = fork()) < 0)
{
printf("Error on fork.\n");
exit(1);
}
//如果是子进程,开始创建消费者
if (pid_c == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_C; i++)
{
p(sem_id, SEM_FULL);
sleep(get_random());
lt = shmptr->letter[shmptr->head];
shmptr->head = (shmptr->head + 1) % LETTER_NUM;
shmptr->is_empty = (shmptr->head == shmptr->tail);
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % LETTER_NUM]);
}
printf("\tConsumer %d gets '%c'.\n", num_c, lt);
fflush(stdout);
v(sem_id, SEM_EMPTY);
}
shmdt(shmptr);
exit(0);
}
}
//主控程序最后退出
while(wait(0) != -1);
shmdt(shmptr);
shmctl(shm_id, IPC_RMID, 0);
semctl(sem_id, IPC_RMID, 0);
printf("Main process ends.\n");
fflush(stdout);
exit(0);
}
#include <time.h>
#include <unistd.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/sem.h>
#define NEED_P 2
#define NEED_C 3
#define WORKS_P 6
#define WORKS_C 4
#define BUF_LENGTH (sizeof(struct mybuffer))
#define LETTER_NUM 3
#define SHM_MODE 0600
#define SEM_ALL_KEY 1234
#define SEM_EMPTY 0
#define SEM_FULL 1
//缓冲区结构(循环队列)
struct mybuffer
{
char letter[LETTER_NUM];
int head;
int tail;
int is_empty;
};
//得到5以内的一个随机数
int get_random()
{
int t;
srand((unsigned)(getpid() + time(NULL)));
t = rand() % 5;
return t;
}
//得到A~Z的一个随机字母
char get_letter()
{
char a;
srand((unsigned)(getpid() + time(NULL)));
a = (char)((char)(rand() % 26) + 'A');
return a;
}
//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);
}
//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);
}
//主函数
int main(int argc, char * argv[])
{
int i, j;
int shm_id, sem_id;
int num_p = 0, num_c = 0;
struct mybuffer * shmptr;
char lt;
time_t now;
pid_t pid_p, pid_c;
sem_id = semget(SEM_ALL_KEY, 2, IPC_CREAT | 0660);
if (sem_id >= 0)
{
printf("Main process starts. Semaphore created.\n");
}
semctl(sem_id, SEM_EMPTY, SETVAL, LETTER_NUM);
semctl(sem_id, SEM_FULL, SETVAL, 0);
if ((shm_id = shmget(IPC_PRIVATE, BUF_LENGTH, SHM_MODE)) < 0)
{
printf("Error on shmget.\n");
exit(1);
}
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
shmptr->head = 0;
shmptr->tail = 0;
shmptr->is_empty = 1;
while ((num_p++) < NEED_P)
{
if ((pid_p = fork()) < 0)
{
printf("Error on fork.\n");
exit(1);
}
//如果是子进程,开始创建生产者
if (pid_p == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_P; i++)
{
p(sem_id, SEM_EMPTY);
sleep(get_random());
shmptr->letter[shmptr->tail] = lt = get_letter();
shmptr->tail = (shmptr->tail + 1) % LETTER_NUM;
shmptr->is_empty = 0;
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % LETTER_NUM]);
}
printf("\tProducer %d puts '%c'.\n", num_p, lt);
fflush(stdout);
v(sem_id, SEM_FULL);
}
shmdt(shmptr);
exit(0);
}
}
while (num_c++ < NEED_C)
{
if ((pid_c = fork()) < 0)
{
printf("Error on fork.\n");
exit(1);
}
//如果是子进程,开始创建消费者
if (pid_c == 0)
{
if ((shmptr = shmat(shm_id, 0, 0)) == (void *)-1)
{
printf("Error on shmat.\n");
exit(1);
}
for (i = 0; i < WORKS_C; i++)
{
p(sem_id, SEM_FULL);
sleep(get_random());
lt = shmptr->letter[shmptr->head];
shmptr->head = (shmptr->head + 1) % LETTER_NUM;
shmptr->is_empty = (shmptr->head == shmptr->tail);
now = time(NULL);
printf("%02d:%02d:%02d\t", localtime(&now)->tm_hour, localtime(&now)->tm_min, localtime(&now)->tm_sec);
for (j = (shmptr->tail - 1 >= shmptr->head) ? (shmptr->tail - 1) : (shmptr->tail - 1 + LETTER_NUM); !(shmptr->is_empty) && j >= shmptr->head; j--)
{
printf("%c", shmptr->letter[j % LETTER_NUM]);
}
printf("\tConsumer %d gets '%c'.\n", num_c, lt);
fflush(stdout);
v(sem_id, SEM_EMPTY);
}
shmdt(shmptr);
exit(0);
}
}
//主控程序最后退出
while(wait(0) != -1);
shmdt(shmptr);
shmctl(shm_id, IPC_RMID, 0);
semctl(sem_id, IPC_RMID, 0);
printf("Main process ends.\n");
fflush(stdout);
exit(0);
}
windows代码:
View Code
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define ID_M 0
#define ID_P_FROM 1
#define ID_P_TO 2
#define ID_C_FROM 3
#define ID_C_TO 5
#define PROCESS_NUM 5
#define WORKS_P 6
#define WORKS_C 4
#define LETTER_NUM 3
#define SHM_NAME "BUFFER"
//缓冲区结构(循环队列)
struct mybuffer
{
char letter[LETTER_NUM];
int head;
int tail;
int is_empty;
};
//共享主存区结构
struct sharemem
{
struct mybuffer data;
int index;
HANDLE semEmpty;
HANDLE semFull;
};
//文件映射对象句柄
static HANDLE hMapping;
//子进程句柄数组
static HANDLE hs[PROCESS_NUM + 1];
//得到5000以内的一个随机数
int get_random()
{
int t;
srand((unsigned)(GetCurrentProcessId() + time(NULL)));
t = rand() % 5000;
return t;
}
//得到A~Z的一个随机字母
char get_letter()
{
char a;
srand((unsigned)(getpid() + time(NULL)));
a = (char)((char)(rand() % 26) + 'A');
return a;
}
//进程克隆(通过参数传递进程的序列号)
void StartClone(int nCloneID)
{
char szFilename[MAX_PATH];
char szCmdLine[MAX_PATH];
STARTUPINFO si;
PROCESS_INFORMATION pi;
GetModuleFileName(NULL, szFilename, MAX_PATH);
sprintf(szCmdLine, "\"%s\" %d", szFilename, nCloneID);
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
//创建子进程
BOOL bCreateOK = CreateProcess(szFilename, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
hs[nCloneID] = pi.hProcess;
return;
}
//创建共享主存区
HANDLE MakeSharedFile()
{
//创建文件映射对象
HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(struct sharemem), SHM_NAME);
if (hMapping != INVALID_HANDLE_VALUE)
{
//在文件映射上创建视图
LPVOID pData = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pData != NULL)
{
ZeroMemory(pData, sizeof(struct sharemem));
}
//关闭文件视图
UnmapViewOfFile(pData);
}
return (hMapping);
}
//主函数
int main(int argc, char * argv[])
{
int i, j, k;
int pindex = 1; //下一个要创建的子进程的序列号
int nClone = ID_M; //本进程序列号
char lt;
SYSTEMTIME systime;
//如果有参数,就作为子进程的序列号
if (argc > 1)
{
sscanf(argv[1], "%d", &nClone);
}
//对于所有进程
printf("Process ID: %d , Serial No: %d\n", GetCurrentProcessId(), nClone);
//对于主控进程
if (nClone == ID_M)
{
printf("Main process starts.\n");
//创建数据文件
hMapping = MakeSharedFile();
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->data.head = 0;
shm->data.tail = 0;
shm->index = 0;
shm->semEmpty = CreateSemaphore(NULL, LETTER_NUM, LETTER_NUM, "SEM_EMPTY");
shm->semFull = CreateSemaphore(NULL, 0, LETTER_NUM, "SEM_FULL");
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
//依次创建子进程
while (pindex <= PROCESS_NUM)
{
StartClone(pindex++);
}
//等待子进程完成
for (k = 1; k < PROCESS_NUM + 1; k++)
{
WaitForSingleObject(hs[k], INFINITE);
CloseHandle(hs[k]);
}
printf("Main process ends.\n");
}
//对于生产者进程
else if (nClone >= ID_P_FROM && nClone <= ID_P_TO)
{
printf("Producer %d process starts.\n", nClone - ID_M);
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
shm->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
for (i = 0; i < WORKS_P; i++)
{
WaitForSingleObject(shm->semEmpty, INFINITE);
sleep(get_random());
shm->index++;
shm->data.letter[shm->data.tail] = lt = get_letter();
shm->data.tail = (shm->data.tail + 1) % LETTER_NUM;
shm->data.is_empty = 0;
GetLocalTime(&systime);
printf("[%02d]\t", shm->index);
printf("%02d:%02d:%02d\t", systime.wHour, systime.wMinute, systime.wSecond);
for (j = (shm->data.tail - 1 >= shm->data.head) ? (shm->data.tail - 1) : (shm->data.tail - 1 + LETTER_NUM); !(shm->data.is_empty) && j >= shm->data.head; j--)
{
printf("%c", shm->data.letter[j % LETTER_NUM]);
}
printf("\tProducer %d puts '%c'.\n", nClone - ID_M, lt);
ReleaseSemaphore(shm->semFull, 1, NULL);
}
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
printf("Producer %d process ends.\n", nClone - ID_M);
}
//对于消费者进程
else if (nClone >= ID_C_FROM && nClone <= ID_C_TO)
{
printf("Consumer %d process starts.\n", nClone - ID_P_TO);
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
shm->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
for (i = 0; i < WORKS_C; i++)
{
WaitForSingleObject(shm->semFull, INFINITE);
sleep(get_random());
shm->index++;
lt = shm->data.letter[shm->data.head];
shm->data.head = (shm->data.head + 1) % LETTER_NUM;
shm->data.is_empty = (shm->data.head == shm->data.tail);
GetLocalTime(&systime);
printf("[%02d]\t", shm->index);
printf("%02d:%02d:%02d\t", systime.wHour, systime.wMinute, systime.wSecond);
for (j = (shm->data.tail - 1 >= shm->data.head) ? (shm->data.tail - 1) : (shm->data.tail - 1 + LETTER_NUM); !(shm->data.is_empty) && j >= shm->data.head; j--)
{
printf("%c", shm->data.letter[j % LETTER_NUM]);
}
printf("\tConsumer %d gets '%c'.\n", nClone - ID_P_TO, lt);
ReleaseSemaphore(shm->semEmpty, 1, NULL);
}
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
printf("Consumer %d process ends.\n", nClone - ID_P_TO);
}
CloseHandle(hMapping);
hMapping = INVALID_HANDLE_VALUE;
return 0;
}
#include <stdlib.h>
#include <time.h>
#include <windows.h>
#define ID_M 0
#define ID_P_FROM 1
#define ID_P_TO 2
#define ID_C_FROM 3
#define ID_C_TO 5
#define PROCESS_NUM 5
#define WORKS_P 6
#define WORKS_C 4
#define LETTER_NUM 3
#define SHM_NAME "BUFFER"
//缓冲区结构(循环队列)
struct mybuffer
{
char letter[LETTER_NUM];
int head;
int tail;
int is_empty;
};
//共享主存区结构
struct sharemem
{
struct mybuffer data;
int index;
HANDLE semEmpty;
HANDLE semFull;
};
//文件映射对象句柄
static HANDLE hMapping;
//子进程句柄数组
static HANDLE hs[PROCESS_NUM + 1];
//得到5000以内的一个随机数
int get_random()
{
int t;
srand((unsigned)(GetCurrentProcessId() + time(NULL)));
t = rand() % 5000;
return t;
}
//得到A~Z的一个随机字母
char get_letter()
{
char a;
srand((unsigned)(getpid() + time(NULL)));
a = (char)((char)(rand() % 26) + 'A');
return a;
}
//进程克隆(通过参数传递进程的序列号)
void StartClone(int nCloneID)
{
char szFilename[MAX_PATH];
char szCmdLine[MAX_PATH];
STARTUPINFO si;
PROCESS_INFORMATION pi;
GetModuleFileName(NULL, szFilename, MAX_PATH);
sprintf(szCmdLine, "\"%s\" %d", szFilename, nCloneID);
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
//创建子进程
BOOL bCreateOK = CreateProcess(szFilename, szCmdLine, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
hs[nCloneID] = pi.hProcess;
return;
}
//创建共享主存区
HANDLE MakeSharedFile()
{
//创建文件映射对象
HANDLE hMapping = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(struct sharemem), SHM_NAME);
if (hMapping != INVALID_HANDLE_VALUE)
{
//在文件映射上创建视图
LPVOID pData = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pData != NULL)
{
ZeroMemory(pData, sizeof(struct sharemem));
}
//关闭文件视图
UnmapViewOfFile(pData);
}
return (hMapping);
}
//主函数
int main(int argc, char * argv[])
{
int i, j, k;
int pindex = 1; //下一个要创建的子进程的序列号
int nClone = ID_M; //本进程序列号
char lt;
SYSTEMTIME systime;
//如果有参数,就作为子进程的序列号
if (argc > 1)
{
sscanf(argv[1], "%d", &nClone);
}
//对于所有进程
printf("Process ID: %d , Serial No: %d\n", GetCurrentProcessId(), nClone);
//对于主控进程
if (nClone == ID_M)
{
printf("Main process starts.\n");
//创建数据文件
hMapping = MakeSharedFile();
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->data.head = 0;
shm->data.tail = 0;
shm->index = 0;
shm->semEmpty = CreateSemaphore(NULL, LETTER_NUM, LETTER_NUM, "SEM_EMPTY");
shm->semFull = CreateSemaphore(NULL, 0, LETTER_NUM, "SEM_FULL");
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
//依次创建子进程
while (pindex <= PROCESS_NUM)
{
StartClone(pindex++);
}
//等待子进程完成
for (k = 1; k < PROCESS_NUM + 1; k++)
{
WaitForSingleObject(hs[k], INFINITE);
CloseHandle(hs[k]);
}
printf("Main process ends.\n");
}
//对于生产者进程
else if (nClone >= ID_P_FROM && nClone <= ID_P_TO)
{
printf("Producer %d process starts.\n", nClone - ID_M);
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
shm->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
for (i = 0; i < WORKS_P; i++)
{
WaitForSingleObject(shm->semEmpty, INFINITE);
sleep(get_random());
shm->index++;
shm->data.letter[shm->data.tail] = lt = get_letter();
shm->data.tail = (shm->data.tail + 1) % LETTER_NUM;
shm->data.is_empty = 0;
GetLocalTime(&systime);
printf("[%02d]\t", shm->index);
printf("%02d:%02d:%02d\t", systime.wHour, systime.wMinute, systime.wSecond);
for (j = (shm->data.tail - 1 >= shm->data.head) ? (shm->data.tail - 1) : (shm->data.tail - 1 + LETTER_NUM); !(shm->data.is_empty) && j >= shm->data.head; j--)
{
printf("%c", shm->data.letter[j % LETTER_NUM]);
}
printf("\tProducer %d puts '%c'.\n", nClone - ID_M, lt);
ReleaseSemaphore(shm->semFull, 1, NULL);
}
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
printf("Producer %d process ends.\n", nClone - ID_M);
}
//对于消费者进程
else if (nClone >= ID_C_FROM && nClone <= ID_C_TO)
{
printf("Consumer %d process starts.\n", nClone - ID_P_TO);
//映射视图
HANDLE hFileMapping = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, SHM_NAME);
LPVOID pFile = MapViewOfFile(hFileMapping, FILE_MAP_ALL_ACCESS, 0, 0, 0);
if (pFile != NULL)
{
struct sharemem * shm = (struct sharemem *)(pFile);
shm->semEmpty = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_EMPTY");
shm->semFull = OpenSemaphore(SEMAPHORE_ALL_ACCESS,FALSE,"SEM_FULL");
for (i = 0; i < WORKS_C; i++)
{
WaitForSingleObject(shm->semFull, INFINITE);
sleep(get_random());
shm->index++;
lt = shm->data.letter[shm->data.head];
shm->data.head = (shm->data.head + 1) % LETTER_NUM;
shm->data.is_empty = (shm->data.head == shm->data.tail);
GetLocalTime(&systime);
printf("[%02d]\t", shm->index);
printf("%02d:%02d:%02d\t", systime.wHour, systime.wMinute, systime.wSecond);
for (j = (shm->data.tail - 1 >= shm->data.head) ? (shm->data.tail - 1) : (shm->data.tail - 1 + LETTER_NUM); !(shm->data.is_empty) && j >= shm->data.head; j--)
{
printf("%c", shm->data.letter[j % LETTER_NUM]);
}
printf("\tConsumer %d gets '%c'.\n", nClone - ID_P_TO, lt);
ReleaseSemaphore(shm->semEmpty, 1, NULL);
}
UnmapViewOfFile(pFile);
pFile = NULL;
}
else
{
printf("Error on OpenFileMapping.\n");
}
CloseHandle(hFileMapping);
printf("Consumer %d process ends.\n", nClone - ID_P_TO);
}
CloseHandle(hMapping);
hMapping = INVALID_HANDLE_VALUE;
return 0;
}
- 实验名称
Linux/Windows系统API实验二:生产者消费者问题
- 实验目的
实现生产者消费者问题模拟,显示每次添加和读取数据的时间及缓冲区的状态,生产者和消费者用进程模拟,缓冲区用共享内存来实现。
- 实验要求
-
一个大小为3的缓冲区,初始为空
2个生产者
随机等待一段时间,往缓冲区添加数据,
若缓冲区已满,等待消费者取走数据后再添加
重复6次
3个消费者
随机等待一段时间,从缓冲区读取数据
若缓冲区为空,等待生产者添加数据后再读取
重复4次
- 实验内容
(1)Linux版本:
共享主存段为进程提供了直接通过主存进行通信的有效手段。使用shmget()系统调用实现共享主存段的创建,shmget()返回共享内存区的 ID。对于已经申请到的共享段,进程需把它附加到自己的虚拟空间中才能对其进行读写。使用shmat()将共享内存附加到进程的地址空间。程序退出时调用 shmdt()将共享存储区从本地进程中解除连接,但它不删除共享存储区。shmctl()调用可实现多种共享存储区操作,包括删除和获取信息。
在UNIX系统中,一个或多个信号量构成一个信号量集合。使用信号量机制可以实现进程之间的同步和互斥,允许并发进程一次对一组信号量进行相同或不同 的操作。每个P、V操作不限于减1或加1,而是可以加减任何整数。在进程终止时,系统可根据需要自动消除所有被进程操作过的信号量的影响。 semget()调用建立一个信号量集合,semctl()调用对信号量执行控制操作,进而实现P、V操作。
(2)Windows版本:
信号量是可命名的、安全的线程同步对象。信号量启动时带有可访问资源的最大数目和当前空闲资源数。使用CreateSemaphore() API创建信号量对象,它需要标准的安全性和命名,以及最大值和初始计数。当资源使用完成后,必须调用ReleaseSemaphore() API。一旦内核对象使用信号量挂起等待访问资源的线程,那么可保证下一个获得访问的是等待队列中的第一个线程。信号量使用计数器实现同步。每次取信号量 时,信号量计数器递减。计数永远不会小于0或大于在参数lMaximumCount中定义的值。
Windows内存管理器使用局部对象来实现共享内存。文件中的字节一对一映射到虚地址空间。读内存的时候实际上是从文件获取数据,修改内存最终将写 回到文件。使用CreateFileMapping()创建文件映射,OpenFileMapping()打开文件映射。调用 MapViewOfFile()将文件映射到进程的地址空间。
- 实验结果
(1)Linux版本:
lj@ossg-ssh:~/dev/linux/p_c$ ./p_c
Main process starts. Semaphore created.
14:00:35 C Producer 1 puts 'C'.
14:00:35 XC Producer 2 puts 'X'.
14:00:36 XXC Producer 1 puts 'X'.
14:00:36 XX Consumer 3 gets 'C'.
14:00:36 X Consumer 2 gets 'X'.
14:00:39 Consumer 1 gets 'X'.
14:00:39 P Producer 1 puts 'P'.
14:00:40 SP Producer 2 puts 'S'.
14:00:42 CSP Producer 1 puts 'C'.
14:00:43 CS Consumer 3 gets 'P'.
14:00:44 C Consumer 2 gets 'S'.
14:00:44 Consumer 1 gets 'C'.
14:00:45 R Producer 2 puts 'R'.
14:00:46 RR Producer 1 puts 'R'.
14:00:48 VRR Producer 2 puts 'V'.
14:00:49 VR Consumer 3 gets 'R'.
14:00:50 V Consumer 2 gets 'R'.
14:00:50 Consumer 1 gets 'V'.
14:00:53 E Producer 2 puts 'E'.
14:00:53 Consumer 3 gets 'E'.
14:00:53 S Producer 1 puts 'S'.
14:00:55 Consumer 2 gets 'S'.
14:00:57 E Producer 2 puts 'E'.
14:00:59 Consumer 1 gets 'E'.
Main process ends.
(2)Windows版本:
E:\Temp\Ex2\windows\p_c>p_c
Process ID: 3120 , Serial No: 0
Main process starts.
Process ID: 7376 , Serial No: 1
Producer 1 process starts.
Process ID: 7260 , Serial No: 2
Producer 2 process starts.
Process ID: 4424 , Serial No: 3
Consumer 1 process starts.
Process ID: 6444 , Serial No: 4
Consumer 2 process starts.
Process ID: 6492 , Serial No: 5
Consumer 3 process starts.
[01] 13:57:32 N Producer 2 puts 'N'.
[02] 13:57:33 RN Producer 2 puts 'R'.
[03] 13:57:33 FRN Producer 1 puts 'F'.
[04] 13:57:33 FR Consumer 1 gets 'N'.
[05] 13:57:33 RFR Producer 2 puts 'R'.
[06] 13:57:34 RF Consumer 1 gets 'R'.
[07] 13:57:35 MRF Producer 1 puts 'M'.
[08] 13:57:35 MR Consumer 2 gets 'F'.
[09] 13:57:35 XMR Producer 2 puts 'X'.
[10] 13:57:35 XM Consumer 3 gets 'R'.
[11] 13:57:36 X Consumer 1 gets 'M'.
[12] 13:57:36 PX Producer 1 puts 'P'.
[13] 13:57:36 APX Producer 2 puts 'A'.
[14] 13:57:37 AP Consumer 1 gets 'X'.
Consumer 1 process ends.
[15] 13:57:38 WAP Producer 1 puts 'W'.
[16] 13:57:38 WA Consumer 2 gets 'P'.
[17] 13:57:38 HWA Producer 2 puts 'H'.
Producer 2 process ends.
[18] 13:57:39 HW Consumer 3 gets 'A'.
[19] 13:57:39 ZHW Producer 1 puts 'Z'.
[20] 13:57:40 ZH Consumer 2 gets 'W'.
[21] 13:57:41 GZH Producer 1 puts 'G'.
Producer 1 process ends.
[22] 13:57:41 GZ Consumer 3 gets 'H'.
[23] 13:57:43 G Consumer 2 gets 'Z'.
Consumer 2 process ends.
[24] 13:57:44 Consumer 3 gets 'G'.
Consumer 3 process ends.
Main process ends.