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

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

#include

#include

#include

#include

#include

#include

#include

#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

#include

#include

#include

#include

#include

#include

#include

#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

#include

#include

#include

#include

#include

#include

#include

#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

#include

#include

#include

#include

#include

#include

#include

#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,故消费者一直处理等待状态,消费者进程出了输出提示该进程为消费者进程语句外,不进行任何操作。

你可能感兴趣的:(linux生产者消费者进程)