改自原文链接:https://blog.csdn.net/weixin_43166958/article/details/104163221
一、重要资料资料
满时不生产,空时不消耗:
生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要作用是生成一定量的数据放到缓冲区中,然后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。
消费者-------消耗资源中数据-------等数据
生产者-------产生数据存入资源----等资源
1️⃣信号量等于0--------------资源中无数据,无消费者无生产者
2️⃣信号量大于0--------------资源中有数据,消费者与生产者可能在流水作业,也希望是流水作业,拥堵肯定是不好的。拥塞是有生产者睡着等资源存储数据
3️⃣信号量小于0--------------资源中无数据,有消费者在睡着等数据
二、本次实验的基本内容是:
在 Ubuntu 下编写程序,用信号量解决生产者——消费者问题;
在 0.11 中实现信号量,用生产者—消费者程序检验之。
1
用信号量解决生产者—消费者问题
实验要求:pc.c程序需打开一个文件buffer.txt作为共享缓冲区,缓冲区同时最多只能保存 10 个数;创建一个生产者进程和N个消费者进程,其中生产者进程向缓冲区写入连续的整数,0,1,2,……,M,M>=500;消费者进程从缓冲区依次读取数字,每次读一个,并将读出的数字从缓冲区删除,然后将本进程 ID 和数字输出到标准输出。
(1)
信号量相关函数-------根据这些函数编写生产者消费者模型代码 pc.c
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
sem_open() 的功能是创建一个信号量,或打开一个已经存在的信号量。如失败,返回值是 NULL
name 是信号量的名字。不同的进程可以通过提供同样的 name 而共享同一个信号量。
value 是信号量的初值,仅当新建信号量时,此参数才有效,其余情况下它被忽略。
int sem_wait(sem_t *sem);
sem_wait() 就是信号量的 P (生产者)原子操作(硬件原子操作指令与进出栈,中断指令是一样的)。返回 0 表示成功,返回 -1 表示失败。
如果信号量的值比0大,那么进行减一的操作,函数立即返回,往下执行。如果信号量当前为0值,那么调用就会一直阻塞或者直到信号量变得可以进行减一操作,达到阻塞进程的目的.
int sem_post(sem_t *sem);
sem_post() 就是信号量的 V (消费者)原子操作。如果有等待 sem 的进程,它会唤醒其中的一个。返回 0 表示成功,返回 -1 表示失败。
sem_post函数的作用是给信号量的值加上一个“1”,即解锁操作; 它是一个原子操作。
int sem_unlink(const char *name);
sem_unlink() 的功能是删除名为 name 的信号量。返回 0 表示成功,返回 -1 表示失败。
off_t lseek(int filedes, off_t offset, int whence);
lseek() 函数可以改变文件的文件偏移量,所有打开的文件都有一个当前文件偏移量,用于表明文件开始处到文件当前位置的字节数。成功返回新的偏移量,失败返回-1。
SEEK_SET 将读写位置指向文件头后再增加offset个位移量。
SEEK_CUR 以目前的读写位置往后增加offset个位移量。
SEEK_END 将读写位置指向文件尾后再增加offset个位移量。
用lseek创建一个空洞文件
空洞文件就是这个文件有一段是空的;
普通文件中间不能有空,write文件时从前往后移动文件指针,依次写入;
用lseek往后跳过一段,就形成空洞文件;
空洞文件对多线程共同操作文件非常有用。需要创建一个很大文件时,从头开始依次创建时间很长,可以将文件分成多段,多个线程操作每个线程负责其中一段的写入。
ret = lseek(fd, 10, SEEK_SET);
(2)
信号量实现生产者消费者模型代码 pc.c:
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <sys/types.h>
#include <sys/wait.h>
#define M 530 /*打出数字总数*/
#define N 5 /*消费者进程数*/
#define BUFSIZE 10 /*缓冲区大小*/
int main()
{
sem_t *empty, *full, *mutex;/*3个信号量*/
int fd; /*共享缓冲区文件描述符*/
int i,j,k,child;
int data;/*写入的数据*/
pid_t pid;
int buf_out = 0; /*从缓冲区读取位置*/
int buf_in = 0; /*写入缓冲区位置*/
/*打开信号量*/
empty = sem_open("empty", O_CREAT|O_EXCL, 0644, BUFSIZE); /*剩余资源,初始化为size*/
full = sem_open("full", O_CREAT|O_EXCL, 0644, 0); /*已使用资源,初始化为0*/
mutex = sem_open("mutex", O_CREAT|O_EXCL, 0644, 1); /*互斥量,初始化为1*/
fd = open("buffer.txt", O_CREAT|O_TRUNC|O_RDWR,0666);
lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);/*刷新了40个字节的缓冲区,存放10个数字*/
write(fd,(char *)&buf_out,sizeof(int));/*将待读取位置存入buffer后,以便子进程之间通信*/
/*生产者进程*/
if((pid=fork())==0)
{
printf("I'm producer. pid = %d\n", getpid());
/*生产多少个产品就循环几次*/
for( i = 0 ; i < M; i++)
{
/*empty大于0,才能生产*/
sem_wait(empty);
sem_wait(mutex);
/*写入一个字符*/
lseek(fd, buf_in*sizeof(int), SEEK_SET);
write(fd,(char *)&i,sizeof(int));
/*更新写入缓冲区位置,保证在0-9之间*/
/*生产完一轮产品(文件缓冲区只能容纳BUFSIZE个产品编号)后*/
/*将缓冲文件的位置指针重新定位到文件首部。*/
buf_in = (buf_in + 1) % BUFSIZE;
sem_post(mutex);
sem_post(full);/*共享区中已使用资源++,唤醒消费者线程*/
}
printf("producer end.\n");
fflush(stdout);/*确保将输出立刻输出到标准输出。*/
return 0;
}
else if(pid < 0)
{
perror("Fail to fork!\n");
return -1;
}
/*消费者进程*/
for( j = 0; j < N ; j++ )
{
if((pid=fork())==0)
{
for( k = 0; k < M/N; k++ )
{
sem_wait(full);/*共享区中已使用资源--,一开始为0会阻塞此处*/
sem_wait(mutex);
/*获得读取位置*/
lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
read(fd,(char *)&buf_out,sizeof(int));
/*读取数据*/
lseek(fd,buf_out*sizeof(int),SEEK_SET);
read(fd,(char *)&data,sizeof(int));
/*写入读取位置*/
buf_out = (buf_out + 1) % BUFSIZE;
lseek(fd,BUFSIZE*sizeof(int),SEEK_SET);
write(fd,(char *)&buf_out,sizeof(int));
sem_post(mutex);
sem_post(empty);/*共享区中剩余资源++,唤醒生产者进程*/
/*消费资源*/
printf("%d: %d\n",getpid(),data);
fflush(stdout);
}
printf("child-%d: pid = %d end.\n", j, getpid());
return 0;
}
else if(pid<0)
{
perror("Fail to fork!\n");
return -1;
}
}
/*回收线程资源*/
child = N + 1;
while(child--)
wait(NULL);
/*释放信号量*/
sem_unlink("full");
sem_unlink("empty");
sem_unlink("mutex");
/*释放资源*/
close(fd);
return 0;
}
(3)
在ubuntu下执行,结果如下:
gcc pc.c -o pc -lpthread
./pc > 1.txt
cat 1.txt | more
2
在 linux-0.11 中实现信号量