加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

Linux进程互斥——生产者-消费者

(2009-11-28 22:42:11)
标签:

信号量

ipc

my

to

进程互斥

存储区

 Linux进程互斥

(二)模拟生产者-消费者的示例程序

本示例主要体现进程间的直接制约关系,由于使用共享存储区,也存在间接制约关系。进程分为服务进程和客户进程,服务进程只有一个,作为消费者,在每次客户进程改变共享存储区内容时显示其数值。各客户进程作为生产者,如果共享存储区内容已经显示(被消费),可以接收用户从键盘输入的整数,放在共享存储区。

编译后执行,第一个进程实例将作为服务进程,提示:

       ACT CONSUMER!!! To end, try Ctrl+C or use kill.

服务进程一直循环执行,直到用户按Ctrl+C终止执行,或使用kill命令杀死服务进程。

其他进程实例作为客户进程,提示:

       Act as producer. To end, input 0 when prompted.

客户进程一直循环执行,直到用户输入0。

示例程序代码如下:

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

 

#define MY_SHMKEY 10071500          // need to change

#define MY_SEMKEY 10071500           // need to change

 

void sigend(int);

 

int shmid, semid;

 

int main(void)

{

    int *shmptr, semval, local;

    struct sembuf semopbuf;

 

    if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)

                     

        shmid=shmget(MY_SHMKEY, sizeof(int), 0666);

        semid=semget(MY_SEMKEY, 2, 0666);

        shmptr=(int *)shmat(shmid, 0, 0);

        printf("Act as producer. To end, input 0 when prompted.\n\n");

printf("Input a number:\n");

scanf("%d", &local);

        while( local )

{

    semopbuf.sem_num=0;

    semopbuf.sem_op=-1;

    semopbuf.sem_flg=SEM_UNDO;

    semop(semid, &semopbuf, 1);     

    *shmptr = local;

    semopbuf.sem_num=1;

    semopbuf.sem_op=1;

    semopbuf.sem_flg=SEM_UNDO;

    semop(semid, &semopbuf, 1);     

    printf("Input a number:\n");

    scanf("%d", &local);

        }

    }

    else         

    {

        semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);

        shmptr=(int *)shmat(shmid, 0, 0);

semval=1;

semctl(semid, 0, SETVAL, semval);    

semval=0;

semctl(semid, 1, SETVAL, semval);    

        signal(SIGINT, sigend);

        signal(SIGTERM, sigend);

        printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");

        while(1)

        {

    semopbuf.sem_num=1;

    semopbuf.sem_op=-1;

    semopbuf.sem_flg=SEM_UNDO;

    semop(semid, &semopbuf, 1);     

            printf("Shared memory set to %d\n", *shmptr);

    semopbuf.sem_num=0;

    semopbuf.sem_op=1;

    semopbuf.sem_flg=SEM_UNDO;

    semop(semid, &semopbuf, 1);     

        }

    }

}

 

void sigend(int sig)

{

    shmctl(shmid, IPC_RMID, 0);

    semctl(semid, IPC_RMID, 0);

    exit(0);

}

 

 

运行结果:

 

服务端:消费者

 

 

 

客户端:生产者

 

 

二者通过公共存储区进行通信。

 

第一次执行该程序,创建公共存储区成功,

(shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666))<0不成立,则进入else分支,即,创建了一个服务进程:消费者。因公共存储区为空,故消费者进程处于等待状态。

第二次执行该程序,创建公共存储区失败,条件成立,则进入if分支,即:创建一个客户进程:生产者。生产者提示输入一个整数,输入后,生产者将该整数保存到公共存储区,此时消费者会唤醒进行消费,把数据读出并输出。

1、模拟生产者-消费者

实现相应的示例程序功能,记录执行结果;改造该程序,取消所有的同步机制,记录执行结果,看是否能观察到程序出现错误情况;进一步改造程序,使错误情况易于观察到;记录执行情况并进行分析。

 

分析:

书上通过wait(full)  signal(empty) 和 wait(empty)   signal(full) 来实现同步机制。

 

1》改造该程序,取消所有的同步机制:即去掉实现wait(full)  signal(empty) 和 wait(empty)   signal(full)的语句semop(semid, &semopbuf, 1);和semop(semid, &semopbuf, 1);。代码如下:

 

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

 

#define MY_SHMKEY 10071500      // need to change

#define MY_SEMKEY 10071500      // need to change

 

void sigend(int);

int shmid, semid;

 

int main(void)

{

    int *shmptr, semval, local;

    struct sembuf semopbuf;

 

 

    if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)

            

       

 

        //获得共享存储区首地址

        shmid=shmget(MY_SHMKEY, sizeof(int), 0666);

 

        //跟信号量集,建立联系

        semid=semget(MY_SEMKEY, 2, 0666);

 

        //把一个共享存储区附接到进程内存空间;

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //输出提示

        printf("Act as producer. To end, input 0 when prompted.\n\n");

 

        //输入整数

        printf("Input a number:\n");

        scanf("%d", &local);

 

        //当输入整数为时结束循环

        while( local )

        {

            semopbuf.sem_num=0;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作,实现生产者、消费者的同步

            //semop(semid, &semopbuf, 1);  

 

            *shmptr = local;

            semopbuf.sem_num=1;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作;

            //semop(semid, &semopbuf, 1);  

 

            //输入整数

            printf("Input a number:\n");

            scanf("%d", &local);

        }

    }

    else       

    

       

        //建立一个信号量集

        semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);

 

        //把一个共享存储区附接到进程内存空间

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //操纵一个信号量集,包括赋初值

        semval=1;

        semctl(semid, 0, SETVAL, semval);  

        semval=0;

        semctl(semid, 1, SETVAL, semval);  

 

        //设置对信号的处理方式或处理过程

        signal(SIGINT, sigend);

        signal(SIGTERM, sigend);

 

        //输出提示

        printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");

        while(1)

        {

            semopbuf.sem_num=1;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            //semop(semid, &semopbuf, 1);  

 

            printf("Shared memory set to %d\n", *shmptr);

            semopbuf.sem_num=0;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            //semop(semid, &semopbuf, 1);  

        }

    }

}

 

void sigend(int sig)

{

    shmctl(shmid, IPC_RMID, 0);

    semctl(semid, IPC_RMID, 0);

    exit(0);

}

运行结果:

观察到程序出现错误情况:

消费者会一直从共享存储区中读数据local。

Local在共享存储区,系统默认给予赋值为0.

第一次执行,创建共享存储区成功,进入else分支,打开了一个消费者进程,因为同步控制,消费者会一直进行消费,即读共享存储区数据local的值然后一直输出“Shared memory set to 0”。

第二次执行该程序,创建共享存储区失败,进入if分支,打开一个生产者进程,因无同步控制,当输入整数1时,local=1,则消费者进程会一直输出“Shared memory set to 1”。

即,生产者没生产时,消费者已经开始消费了。并且生产者生产一个,而消费者消费多了。故,程序出现错误。

 

 

2》进一步改造程序,使错误情况易于观察到;记录执行情况并进行分析?

 

进一步改造程序,即在消费者进程中添加“sleep(5);”代码如下:

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

 

#define MY_SHMKEY 10071500      // need to change

#define MY_SEMKEY 10071500      // need to change

 

void sigend(int);

int shmid, semid;

 

int main(void)

{

    int *shmptr, semval, local;

    struct sembuf semopbuf;

 

    //创建一个共享存储区名为MY_SHMKEY,长度为sizeof(int)

    if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)

            

       

 

       

        shmid=shmget(MY_SHMKEY, sizeof(int), 0666);

 

        //跟信号量集,建立联系

        semid=semget(MY_SEMKEY, 2, 0666);

 

        //共享存储区(Share  Memory)通信机制

        //可使若干进程共享主存中的某一个区域,且使该区域出现(映射)在多个进程的虚地址空间中。

        //当进程间欲利用共享存储区进行通信时,必须先在主存中建立一共享存储区,

        //然后将它附接到自己的虚地址空间上。

 

        //故,从逻辑上将一个共享存储区附接到进程的虚拟地址空间上

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //输出提示

        printf("Act as producer. To end, input 0 when prompted.\n\n");

 

        //输入整数

        printf("Input a number:\n");

        scanf("%d", &local);

 

        //当输入整数为时结束循环

        while( local )

        {

            semopbuf.sem_num=0;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作,实现生产者、消费者的同步

            //semop(semid, &semopbuf, 1);  

 

            *shmptr = local;

            semopbuf.sem_num=1;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作;

            //semop(semid, &semopbuf, 1);  

 

            //输入整数

            printf("Input a number:\n");

            scanf("%d", &local);

        }

    }

    else       

    

       

        //建立一个信号量集

        semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);

 

        //把一个共享存储区附接到进程内存空间

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //操纵一个信号量集,包括赋初值

        semval=1;

        semctl(semid, 0, SETVAL, semval);  

        semval=0;

        semctl(semid, 1, SETVAL, semval);  

 

        //设置对信号的处理方式或处理过程

        signal(SIGINT, sigend);

        signal(SIGTERM, sigend);

 

        //输出提示

        printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");

        while(1)

        {

            semopbuf.sem_num=1;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            //semop(semid, &semopbuf, 1);  

 

            sleep(5);

            printf("Shared memory set to %d\n", *shmptr);

            semopbuf.sem_num=0;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            //semop(semid, &semopbuf, 1);  

        }

    }

}

 

void sigend(int sig)

{

    //共享存储区的控制,对其状态信息进行读取和修改

    shmctl(shmid, IPC_RMID, 0);

    semctl(semid, IPC_RMID, 0);

    exit(0);

}

结果:

消费者进程:

 

 

生产者进程:

 

分析:

消费者与生产者并发执行,会出现错误:生产者,生产出2,3,4,5,即共享存储区中变量shmid值依次为2,3,4,5。由于在消费者进程读该变量值之前加了sleep(5),消费者睡醒后,shmid=5,故产品2,3,4丢失了。可见,消费者-生产者中的同步机制非常重要,不能去掉。

 

 

自己补充:

3》只把生产者中的semop(semid, &semopbuf, 1);和semop(semid, &semopbuf, 1);去掉。代码如下:

#include <sys/types.h>

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

#include <string.h>

#include <sys/ipc.h>

#include <sys/shm.h>

#include <sys/sem.h>

 

#define MY_SHMKEY 10071500      // need to change

#define MY_SEMKEY 10071500      // need to change

 

void sigend(int);

int shmid, semid;

 

int main(void)

{

    int *shmptr, semval, local;

    struct sembuf semopbuf;

 

 

    if((shmid=shmget(MY_SHMKEY, sizeof(int), IPC_CREAT|IPC_EXCL|0666)) < 0)

            

       

 

        //获得共享存储区首地址

        shmid=shmget(MY_SHMKEY, sizeof(int), 0666);

 

        //跟信号量集,建立联系

        semid=semget(MY_SEMKEY, 2, 0666);

 

        //把一个共享存储区附接到进程内存空间;

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //输出提示

        printf("Act as producer. To end, input 0 when prompted.\n\n");

 

        //输入整数

        printf("Input a number:\n");

        scanf("%d", &local);

 

        //当输入整数为时结束循环

        while( local )

        {

            semopbuf.sem_num=0;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作,实现生产者、消费者的同步

            //semop(semid, &semopbuf, 1);  

 

            *shmptr = local;

            semopbuf.sem_num=1;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作;

            //semop(semid, &semopbuf, 1);  

 

            //输入整数

            printf("Input a number:\n");

            scanf("%d", &local);

        }

    }

    else       

    

       

        //建立一个信号量集

        semid=semget(MY_SEMKEY, 2, IPC_CREAT|0666);

 

        //把一个共享存储区附接到进程内存空间

        shmptr=(int *)shmat(shmid, 0, 0);

 

        //操纵一个信号量集,包括赋初值

        semval=1;

        semctl(semid, 0, SETVAL, semval);  

        semval=0;

        semctl(semid, 1, SETVAL, semval);  

 

        //设置对信号的处理方式或处理过程

        signal(SIGINT, sigend);

        signal(SIGTERM, sigend);

 

        //输出提示

        printf("ACT CONSUMER!!! To end, try Ctrl+C or use kill.\n\n");

        while(1)

        {

            semopbuf.sem_num=1;

            semopbuf.sem_op=-1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            semop(semid, &semopbuf, 1);

 

            printf("Shared memory set to %d\n", *shmptr);

            semopbuf.sem_num=0;

            semopbuf.sem_op=1;

            semopbuf.sem_flg=SEM_UNDO;

 

            //对信号量集进行wait和signal操作

            semop(semid, &semopbuf, 1);

        }

    }

}

 

void sigend(int sig)

{

    shmctl(shmid, IPC_RMID, 0);

    semctl(semid, IPC_RMID, 0);

    exit(0);

}

 

分析:

则也出现错误:生产者生产,而消费者却一直不消费。

 

因为生产者进程中的semop(semid, &semopbuf, 1);和semop(semid, &semopbuf, 1);被去掉后,信号量的变化在消费者看来一直没变化,即共享存储区可用资源数量一直为0,故消费者一直处理等待状态,消费者进程出了输出提示该进程为消费者进程语句外,不进行任何操作。

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有