信号量实现生产者消费者模型

有界缓冲区实验
本文介绍了一个使用有界缓冲区的生产者-消费者模型实验,其中缓冲区包含3个5字节的单元。通过随机时间间隔模拟生产者与消费者的行为,并利用信号量实现资源的同步与互斥。

本实验的代码中采用的有界缓冲区拥有3个单元,每个单元为5字节。为了尽量体现每个信号量的意义,在程序中生产过程和消费过程是随机(采取0~5s的随机时间间隔)进行的,

而且生产者的速度比比消费者的速度平均快两倍左右(这种关系可以相反)。生产者一次生产一个单元的产品(放入“hello”字符串),消费者一次消费一个单元的产品。

  1  /*本实验的代码中采用的有界缓冲区拥有3个单元,每个单元为5字节。
  2 *为了尽量体现每个信号量的意义,在程序中生产过程和消费过程是随机(采取0~5s的随机时间间隔)进行的,
  3 *而且生产者的速度比比消费者的速度平均快两倍左右(这种关系可以相反)。
  4 *生产者一次生产一个单元的产品(放入“hello”字符串),消费者一次消费一个单元的产品。
  5 */
  6 
  7 #include <stdio.h>
  8 #include <stdlib.h>
  9 #include <string.h>
 10 #include <unistd.h>
 11 #include <fcntl.h>
 12 #include <errno.h>
 13 #include <pthread.h>
 14 #include <semaphore.h>
 15 #include <sys/ipc.h>
 16 #define MYFIFO    "./myfifo"
 17 #define BUFFER_SIZE    3
 18 #define UNIT_SIZE    6
 19 #define RUN_TIME    30
 20 #define DELAY_TIME_LEVELS    5.0
 21 
 22 int fd;    //管道描述符
 23 time_t end_time;    //存放线程的起始时间
 24 sem_t mutex,full,avail;    //信号量描述符
 25 
 26 //生产者线程
 27 void *producer(void *arg)
 28 {
 29     int real_write;        //实际写入字节数
 30     int delay_time=0;
 31 
 32     //time(MULL)返回从公元1970年1月1日的UC时间0时0分0秒算起
 33     //到现在所经过的描述,while()的意思就是说如果在执行生产者线程
 34     //的那一刻没有超过其结束时间,那么执行
 35     while(time(NULL)<end_time){
 36         //阐述0~5s的随机数
 37         delay_time=(int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)/2.0)+1;
 38         sleep(delay_time);
 39         //P操作信号量avail和mutex
 40         sem_wait(&avail);
 41         sem_wait(&mutex);
 42         printf("\nProducer: delay=%d\n", delay_time);
 43         //生产者写入数据
 44         if((real_write=write(fd,"hello",UNIT_SIZE))==-1){
 45             //这个errno==EAGAIN表示的是你的write本来是非阻塞情况
 46             //表示现在没有数据可读,这个时候会置全局变量errno为
 47             //EAGINA,表示可以再次进行读操作;如果是阻塞情况,
 48             //那么被中断的话,errno=EINIR
 49             if(errno==EAGAIN){
 50                 printf("The FIFO has not been read yet. Please try later\n");
 51             }
 52         }
 53         else{
 54             printf("wirte %d to the FIFO\n", real_write);
 55         }
 56 
 57         //V操作信号量full和mutex
 58         sem_post(&full);
 59         sem_post(&mutex);
 60     }
 61     pthread_exit(NULL);
 62 }
 63 
 64 //消费者线程
 65 void *customer(void *arg)
 66 {
 67 //    unsigned int read_buffer[UNIT_SIZE];
 68     char read_buffer[UNIT_SIZE];
 69     int real_read;
 70     int delay_time;
 71 
 72     //time(MULL)返回从公元1970年1月1日的UC时间0时0分0秒算起
 73     //到现在所经过的描述,while()的意思就是说如果在执行生产者线程
 74     //的那一刻没有超过其结束时间,那么执行
 75     while(time(NULL)<end_time){
 76         //阐述0~5s的随机数
 77         delay_time=(int)(rand()*DELAY_TIME_LEVELS/(RAND_MAX)/2.0)+1;
 78         sleep(delay_time);
 79         //P操作信号量full和mutex
 80         sem_wait(&full);
 81         sem_wait(&mutex);
 82         memset(read_buffer,0,UNIT_SIZE);    //用0来初始化
 83         printf("\nCustomer: delay=%d\n", delay_time);
 84         //消费者消费数据
 85         if((real_read=read(fd,read_buffer,UNIT_SIZE))==-1){
 86             //这个errno==EAGAIN表示的是你的write本来是非阻塞情况
 87             //表示现在没有数据可读,这个时候会置全局变量errno为
 88             //EAGINA,表示可以再次进行读操作;如果是阻塞情况,
 89             //那么被中断的话,errno=EINIR
 90             if(errno==EAGAIN){
 91                 printf("No data yet\n");
 92             }
 93         }
 94         else{
 95             printf("read %s from the FIFO\n", read_buffer);
 96         }
 97 
 98         //V操作信号量avail和mutex
 99         sem_post(&avail);
100         sem_post(&mutex);
101     }
102 }
103 
104 int main(int argc, char const *argv[])
105 {
106     pthread_t thrd_pro_id, thrd_cus_id;
107     pthread_t mon_th_id;
108     int ret;
109 
110     srand(time(NULL));    //随机数发生器初始化
111     end_time=time(NULL)+RUN_TIME;
112     //创建有名管道。若文件已存在,则mkfifo()返回errno=EEXIST
113     if((mkfifo(MYFIFO, O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)){
114         printf("Cannot create fifo\n");
115         return errno;
116     }
117     //打开管道
118     fd=open(MYFIFO,O_RDWR,0666);
119     if(fd==-1){
120         printf("Open fifo failed.\n");
121         return fd;
122     }
123 
124     ret=sem_init(&mutex,0,1);
125     ret+=sem_init(&avail,0,BUFFER_SIZE);
126     ret+=sem_init(&full,0,0);
127     if(ret!=0){    //这里ret用的倒也巧妙
128         printf("Any semaphore initialization failed\n");
129         return ret;
130     }
131     
132     //创建两个进程
133     ret=pthread_create(&thrd_pro_id, NULL, (void *)producer, NULL);
134     if(ret!=0){
135         printf("create producer thread failed.\n");
136         return ret;
137     }
138     ret=pthread_create(&thrd_cus_id, NULL, (void *)customer, NULL);
139     if(ret!=0){
140         printf("create customer thread failed.\n");
141         return ret;
142     }
143     printf("wait for producer&customer thread\n");
144     pthread_join(thrd_pro_id,NULL);
145     pthread_join(thrd_cus_id,NULL);
146 //    unlink(MYFIFO);        //所有打开该文件的进程都结束时文件被删除
147     return 0;
148 }

 

编译运行结果:

然后我们看看myfifo文件,果然是权限有问题

 然后把文件的用户读权限加上去 chmod u+r myfifo ,就有结果啦

 

但这终究不是办法呀,要是每次我都改一下权限那岂不是很麻烦,所以就只能该程序喽

把113行 if((mkfifo(MYFIFO, O_CREAT|O_EXCL)<0)&&(errno!=EEXIST)) 改成 if((mkfifo(MYFIFO, O_CREAT|O_EXCL|0666)<0)&&(errno!=EEXIST)) 就可以啦。

我们来看看效果,先删除原来的FIFO文件,重新编译,运行就有结果啦。

我们来看看FIFO文件的权限

好像跟我们设定的0666不太一样诶

 

然后我们取消146行的注释看看

果然运行完后不见myfifo

转载于:https://www.cnblogs.com/fallenmoon/p/6761513.html

### 关于 LiteOS 中信号量实现生产者消费者模型 在 LiteOS 操作系统中,信号量是一种用于进程间同步和互斥访问共享资源的重要工具。通过信号量可以有效解决生产者消费者问题中的资源竞争问题。 以下是基于 LiteOS 的生产者消费者模型的完整代码示例: #### 1. 初始化信号量 LiteOS 提供了 `LOS_SemCreate` 函数来创建信号量[^2]。为了模拟缓冲区的状态,通常会初始化两个信号量: - **emptySem**:表示空闲槽的数量。 - **fullSem**:表示已填充槽的数量。 ```c #include "los_sem.h" #include "los_task.h" #define BUFFER_SIZE 5 UINT32 g_buffer[BUFFER_SIZE]; UINT32 g_in = 0, g_out = 0; UINT32 emptySemID, fullSemID; // 创建信号量函数 VOID CreateSemaphores(VOID) { UINT32 ret; // 创建 emptySem,初始值为缓冲区大小 ret = LOS_SemCreate(BUFFER_SIZE, &emptySemID); if (ret != LOS_OK) { printf("Failed to create empty semaphore\n"); } // 创建 fullSem,初始值为 0 ret = LOS_SemCreate(0, &fullSemID); if (ret != LOS_OK) { printf("Failed to create full semaphore\n"); } } ``` #### 2. 生产者任务 生产者的职责是向缓冲区添加数据,并在每次操作前后调整信号量状态。如果缓冲区已满,则等待有空位后再继续工作。 ```c VOID ProducerTask(VOID) { INT32 item = 0; // 假设生产的物品是一个简单的计数值 while (1) { // 等待有一个空闲位置 LOS_SemPend(emptySemID, LOS_WAIT_FOREVER); // 添加到缓冲区 g_buffer[g_in] = item++; g_in = (g_in + 1) % BUFFER_SIZE; printf("Produced: %d\n", item - 1); // 通知消费者缓冲区中有新项目 LOS_SemPost(fullSemID); // 模拟延迟 LOS_TaskDelay(10); // 单位为 Tick } } ``` #### 3. 消费者任务 消费者的职责是从缓冲区取出数据,并在每次操作前后调整信号量状态。如果没有可消费的数据,则等待直到有新的数据到来。 ```c VOID ConsumerTask(VOID) { while (1) { // 等待缓冲区中有数据 LOS_SemPend(fullSemID, LOS_WAIT_FOREVER); // 取出数据 INT32 consumedItem = g_buffer[g_out]; g_out = (g_out + 1) % BUFFER_SIZE; printf("Consumed: %d\n", consumedItem); // 通知生产者缓冲区有了空位 LOS_SemPost(emptySemID); // 模拟延迟 LOS_TaskDelay(15); // 单位为 Tick } } ``` #### 4. 主程序入口 在主程序中,先初始化信号量并启动生产者消费者线程。 ```c INT32 main(VOID) { TSK_INIT_PARAM_S producerParam = {0}; TSK_INIT_PARAM_S consumerParam = {0}; // 创建信号量 CreateSemaphores(); // 配置生产者任务参数 producerParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ProducerTask; producerParam.pcName = "Producer"; producerParam.uwStackSize = 0x800; producerParam.usTaskPrio = 10; // 配置消费者任务参数 consumerParam.pfnTaskEntry = (TSK_ENTRY_FUNC)ConsumerTask; consumerParam.pcName = "Consumer"; consumerParam.uwStackSize = 0x800; consumerParam.usTaskPrio = 11; // 创建任务 LOS_TaskCreate(&producerParam); LOS_TaskCreate(&consumerParam); return 0; } ``` --- ### 注意事项 1. 如果尝试对已经满了的信号量执行 `LOS_SemPost` 操作,可能会触发错误码 `LOS_ERRNO_SEM_OVERFLOW`。 2. 同样地,当调用 `LOS_SemPend` 而信号量不可用时,可能返回错误码 `LOS_ERRNO_SEM_UNAVAILABLE`。 上述代码展示了如何利用 LiteOS 的信号量机制实现经典的生产者消费者模型,解决了多线程环境下的资源共享与同步问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值