设为首页 加入收藏

TOP

生产者与消费者模型Linux下C语言的实现(一)
2014-11-23 21:42:01 来源: 作者: 【 】 浏览:23
Tags:生产者 消费者 模型 Linux 语言 实现

作者:武特
学习了信号量以及共享内存后,我们就可以实现进程的同步与互斥了。说到这里,最经典的例子莫过于生产者和消费者模型。下面就和大家一起分析,如何一步步实现这个经典模型。完整代码可以在这里下载
下面程序,实现的是多个生产者和多个消费者对N个缓冲区(N个货架)进行访问的例子。现在先想想我们以前的伪代码是怎么写的?是不是这样:
//生产者:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
while(1)
{
p(semid,1);
sleep(3);
p(semid,0);
//producer is producing a product
goods=rand()%10;//product a goods
shmaddr[indexaddr[0]]=goods;//The goods is placed on a shelf
printf("producer:%d produces a product[%d]:%d ",getpid(),indexaddr[0],goods);
indexaddr[0]=(indexaddr[0]+1)%10;
v(semid,0);
sleep(3);
v(semid,2);
}

//消费者:

1
2
3
4
5
6
7
8
9
10
11
12
13
while(1)
{
p(semid,2);
sleep(1);
p(semid,0);
//consumer is consuming a product
goods=shmaddr[indexaddr[1]];//The goods on the shelf is taken down
printf("consumer:%d consumes a product[%d]:%d ",getpid(),indexaddr[1],goods);
indexaddr[1]=(indexaddr[1]+1)%num;
v(semid,0);
sleep(1);
v(semid,1);
}

可能上面的代码你有些眼熟,又有些困惑,因为它和课本上的代码不完全一样,其实上面的代码就是伪代码的linuxC语言具体实现。我们从上面的代码中慢慢寻找伪代码的踪迹:p(semid,0)和v(semid,0)的作用是让进程互斥访问临界区。临界区中包含的数据indexaddr[0],indexaddr[1],以及shmaddr数组分别对应伪代码中的in,out,buffer。p(semid,1)和v(semid,2)以及p(semid,2)和v(semid,1)实现的是同步作用。
并且,在生产者中,生产者生产了一个货物(goods=rand()%10;),然后将这个货物放上货架(shmaddr[indexaddr[0]]=goods;)。在消费者中,消费和从货架上取下货物(goods=shmaddr[indexaddr[1]];)。
好了,现在再看一边上面的代码,我想你的思路就清晰了。
了解了核心代码,并不能算就完成了生产者和消费者模型,因为生产者和消费者核心代码前还得做一些些准备工作,具体要准备些什么,我们具体来分析。
首先申请一块共享内存,这块共享内存用于存放生产者所生产的货物。同时我们可以看到这块共享内存大小为10字节。这里需要注意,每个生产着或消费者运行后,都要去试着分配这样的一块共享内存。如果在当前进程运行前已经有某个进程已经创建了这块共享内存,那么这个进程就不再创建(此时createshm会返回-1并且错误代码为EEXIST),只是打开这块共享内存。创建后,再将这块共享内存添加到当前进程的地址空间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
num=10;
//create a shared memory as goods buffer
if((shmid_goods=createshm(".",s,num))==-1)
{
if(errno==EEXIST)
{
if((shmid_goods=openshm(".",s))==-1)
{
exit(1);
}
}
else
{
perror("create shared memory failed ");
exit(1);
}
}
//attach the shared memory to the current process
if((shmaddr=shmat(shmid_goods,(char*)0,0))==(char*)-1)
{
perror("attach shared memory error ");
exit(1);
}

接下来还要再申请一块共享内存,用于存放两个整形变量in和out(其实就是申请一个含有2个整形变量的数组而已)。他们记录的是生产和消费货物时“货架”的索引。与上面情况相同,如果已经有其他进程创建了此块共享内存,那么当前进程只是打开它而已。
注意这里对两个整形变量的初始化时的值均为0。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
//create a shared memory as index
if((shmid_index=createshm(".",z,8))==-1)
{
if(errno==EEXIST)
{
if((shmid_index=openshm(".",z))==-1)
{
exit(1);
}
}
else
{
perror("create shared memory failed ");
exit(1);
}
}
else
{
is_noexist=1;
}
//attach the shared memory to the current process
if((indexaddr=shmat(shmid_index,(int*)0,0))==(int*)-1)
{
perror("attach shared memory error ");
exit(1);
}
if(is_noexist)
{
indexaddr[0]=0;
indexaddr[1]=0;
}

接下来就是创建一个信号量集,这个信号量集中包含三个信号量。第一个信号量实现的互斥作用,即进程对临界区的互斥访问。剩下两个均实现的是同步作用,协调生产者和消费者的合理运行,即货架上没有空位时候生产者不再生产,货架上无商品时消费者不再消费。
注意下面对每个信号量的赋值情况。互斥信号量当然初值为1。而同步信号量两者之和不能大于num的值。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
//create a semaphore set including 3 semaphores
if((semid=createsem(".",t,3,0))==-1)
{
if(errno==EEXIST)
{
if((semid=opensem(".",t))==-1)
{
exit(1);
}
}
else
{
perror("semget error:");
exit(1);
}
}
els

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C 随机函数rand()与srand()的用法 下一篇实例介绍C语言结构的用法

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: