转:基于进程与共享主存实现的生产者消费者问题(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, 00)) == (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, 00)) == (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, 00)) == (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, 0sizeof(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, 0sizeof(struct sharemem), SHM_NAME);
    if (hMapping != INVALID_HANDLE_VALUE)
    {
        //在文件映射上创建视图
        LPVOID pData = MapViewOfFile(hMapping, FILE_MAP_ALL_ACCESS, 000);
        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, 000);
        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, 000);
        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, 000);
        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.
posted @ 2012-06-27 22:28  Mr.Rico  阅读(...)  评论(...编辑  收藏