rtthread之工作队列
中断处理分为上半部和下半部
一般来说中断处理的上半部和下半部都是不允许出现睡眠和阻塞的。但是对于下半部,并不是一刀切,下半部的实现方式有软中断和tasklet(不允许睡眠和阻塞)以及工作队列(允许睡眠和阻塞)。
上半部:一般中断的中断处理函数为上半部,要求做耗时少的动作,尽量迅速,一定不能休眠和阻塞。
下半部:由于上半部只能执行耗时少的操作,所以耗时长的操作就放在下半部,两个的界限并不是很明显,取决于我们要将哪个操作放在上半部还是下半部。
下面讨论一下工作队列的原理及实现
实现原理:工作队列(workqueue)是一种将工作推后执行的形式,工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。工作队列的本质是创建一个一个普通的内核线程,我们称为工作者线程。
以下是几个工作队列抽象出来的数据结构
1:struct rt_workqueue表示一个工作队列对象(包含一个工作者线程)。
2:struct rt_work为工作队列上的任务。当我们将一个任务加入一个工作队列时候,会将该任务挂接到struct rt_workqueue的任务链表上,当工作线程未就绪的时候,这个工作者线程就会唤醒,去遍历这个链表上的所有任务,执行完后,将该任务从链表上拿走,工作者线程继续休眠。
3:双向链表操作原理请查看文章(数据结构-------双向链表),内存申请释放请查看文章(rtthread之小内存算法)。
数据结构定义
//内存申请,实现及原理请参考rtthread之小内存算法
#ifndef RT_KERNEL_MALLOC
#define RT_KERNEL_MALLOC(sz) rt_malloc(sz)
#endif
//内存释放
#ifndef RT_KERNEL_FREE
#define RT_KERNEL_FREE(ptr) rt_free(ptr)
#endif//工作队列对象,包含工作者线程
struct rt_workqueue
{rt_list_t work_list;rt_thread_t work_thread;
};//任务对象,包含工作任务及数据
struct rt_work
{rt_list_t list;void (*work_func)(struct rt_work* work, void* work_data);void *work_data;
};
原理实现
1.工作者线程
//工作者线程,处理挂接到该工作队列上的任务或者工作
static void _workqueue_thread_entry(void* parameter)
{struct rt_work* work;struct rt_workqueue* queue;uint32_t level = 0;uint8_t workqueue_empty = 0;queue = (struct rt_workqueue*) parameter;RT_ASSERT(queue != RT_NULL);while (1){level = rt_hw_interrupt_disable();//判断工作队列上是否有任务,没有则挂起工作者线程workqueue_empty = rt_list_isempty(&(queue->work_list));rt_hw_interrupt_enable(level);if (workqueue_empty){//没有任务,则挂起工作者线程rt_thread_suspend(rt_thread_self());rt_schedule();}level = rt_hw_interrupt_disable();//有任务,则获取对应任务的信息work = rt_list_entry(queue->work_list.next, struct rt_work, list);//从工作队列任务表中删除当前任务rt_list_remove(&(work->list));rt_hw_interrupt_enable(level);//执行任务work->work_func(work, work->work_data);}
}
2.工作队列初始化及创建工作者线程
//创建及初始化工作队列对象,创建工作者线程,返回工作队列对象
struct rt_workqueue *rt_workqueue_create(const char* name, rt_uint16_t stack_size, rt_uint8_t priority)
{struct rt_workqueue *queue = RT_NULL;//申请一个工作队列对象句柄queue = (struct rt_workqueue*)RT_KERNEL_MALLOC(sizeof(struct rt_workqueue));if (queue != RT_NULL){//初始化工作队列任务链表,指向自己rt_list_init(&(queue->work_list));//创建工作者处理线程queue->work_thread = rt_thread_create(name, _workqueue_thread_entry, queue, stack_size, priority, 10);if (queue->work_thread == RT_NULL){RT_KERNEL_FREE(queue);return RT_NULL;}//开启工作者线程rt_thread_startup(queue->work_thread);}return queue;
}
3.向工作队列添加任务
//将任务加入到工作队列中
rt_err_t rt_workqueue_dowork(struct rt_workqueue* queue, struct rt_work* work)
{uint32_t level = 0;RT_ASSERT(queue != RT_NULL);RT_ASSERT(work != RT_NULL);level = rt_hw_interrupt_disable();//将任务加入到工作队列任务链表中rt_list_remove(&(work->list));rt_list_insert_after(queue->work_list.prev, &(work->list));//如果当前工作者线程处于挂起态,则唤醒该工作者线程if (queue->work_thread->stat != RT_THREAD_READY){rt_hw_interrupt_enable(level);rt_thread_resume(queue->work_thread);rt_schedule();}else rt_hw_interrupt_enable(level);return RT_EOK;
}
4.删除工作队列
//删除工作队列
rt_err_t rt_workqueue_destroy(struct rt_workqueue* queue)
{RT_ASSERT(queue != RT_NULL);//删除工作者线程rt_thread_delete(queue->work_thread);//释放工作队列对象空间RT_KERNEL_FREE(queue);return RT_EOK;
}
5.将任务从工作队列中删除
//将任务从工作队列中删除
rt_err_t rt_workqueue_cancel_work(struct rt_workqueue* queue, struct rt_work* work)
{uint32_t level = 0;RT_ASSERT(queue != RT_NULL);RT_ASSERT(work != RT_NULL);level = rt_hw_interrupt_disable();//从工作队列中移除任务rt_list_remove(&(work->list));rt_hw_interrupt_enable(level);return RT_EOK;
}
应用:
在使用spi flash模拟u盘开发过程中,由于spi flash读写速度操作比较慢耗时,且在flash操作中封装了互斥操作,对于操作系统不能在中断中使用互斥量等操作,需要使用中断下半部的思想,将读取操作添加到工作队列中,从而实现模拟u盘的操作。
修改步骤:
1.创建工作队列
struct rt_workqueue *usb_irq_workqueue = NULL;
void USB_CDC_VCP_Thread(void* parameter)
{.......if(usb_irq_workqueue == NULL){usb_irq_workqueue = rt_workqueue_create("usb_workqueue", 4096, 1);rt_kprintf("[USB] usb irq workqueque creat %s\r\n",(usb_irq_workqueue != NULL)?"ok":"failed");}.......
}
2.修改usb中断操作,将任务添加到工作队列
struct usb_work_data
{USB_OTG_CORE_HANDLE *pdev;uint8_t epnum;
};//修改Usb输入端点处理任务函数
void usb_msc_data_in(struct rt_work* work, void* work_data)
{struct usb_work_data *usb_data = NULL;usb_data = (struct usb_work_data *)work_data;USBD_DCD_INT_fops->DataInStage(usb_data->pdev , usb_data->epnum);
}
//修改Usb输出端点处理任务函数
void usb_msc_data_out(struct rt_work* work, void* work_data)
{struct usb_work_data *usb_data = NULL;usb_data = (struct usb_work_data *)work_data;USBD_DCD_INT_fops->DataOutStage(usb_data->pdev , usb_data->epnum);
}extern struct rt_workqueue *usb_irq_workqueue;
static struct usb_work_data usb_data;
static struct rt_work usb_msc_work;
static uint32_t DCD_HandleInEP_ISR(USB_OTG_CORE_HANDLE *pdev)
{......if(doepint.b.xfercompl){......if(usb_irq_workqueue != NULL){//将任务参数及任务函数加入到工作队列中usb_data.pdev = pdev;usb_data.epnum = epnum;usb_msc_work.work_data = (void*)&usb_data;usb_msc_work.work_func = usb_msc_data_in;rt_workqueue_dowork(usb_irq_workqueue,&usb_msc_work);}......}......
}static uint32_t DCD_HandleOutEP_ISR(USB_OTG_CORE_HANDLE *pdev)
{......if(doepint.b.xfercompl){......if(usb_irq_workqueue != NULL){//将任务参数及任务函数加入到工作队列中usb_data.pdev = pdev;usb_data.epnum = epnum;usb_msc_work.work_data = (void*)&usb_data;usb_msc_work.work_func = usb_msc_data_out;rt_workqueue_dowork(usb_irq_workqueue,&usb_msc_work);}......}......
}
rtthread之工作队列相关推荐
- RT-Thread 隐藏的宝藏之工作队列
1. 工作队列是什么 工作队列:我们可以将不是很紧急处理的事情放到 workqueue 中处理,等待系统空闲时就会去执行 workqueue 里的事情.工作队列的本质就是开一个线程去处理排列好的任务, ...
- IOT-OS之RT-Thread(二)--- CPU架构与BSP移植过程
文章目录 一.RT-Thread内核简介 二.RT-Thread CPU架构移植 2.1 Cortex-M CPU 架构简介 2.2 RT-Thread 中断机制 2.3 CPU 架构移植 三.RT- ...
- RT-Thread 隐藏的宝藏之管道
一. 什么是管道 pipe: 匿名管道. 对于熟悉 linux 开发的人来说,pipe 就很熟悉了.pipe 是一种 IPC 机制,他的作用是用作有血缘进程间完成数据传递,只能从一端写入,从另外一端读 ...
- 【火牛STM32F103VC】RT-Thread 蜂鸣器BEEP功能验证
开发环境 Win10 64位 Keil MDK5 [火牛开发板 STM32F103VCT6] USB 转串口线(CH340),这里使用RS232的串口,注意区分 TTL电平的 5V 直流电源,用于给开 ...
- RT-thread内核之进程间通信
一.进程间通信机制 rt-thread操作系统的IPC(Inter-Process Communication,进程间同步与通信)包含有中断锁.调度器锁.信号量.互斥锁.事件.邮箱.消息队列.其中前5 ...
- .net thread操作串口_听说你不知道 RT-Thread 有个 ringbuffer
在嵌入式开发中,我们经常需要用到 FIFO 数据结构来存储数据,比如任务间的通信.串口数据收发等场合.很多小伙伴不知道 RT-Thread 为我们提供了一个 ringbuffer 数据结构,代码位于: ...
- rt-thread端口时钟使能_(2)RTThread启动过程分析
点击上方蓝字,关注微联智控工作室 可点击右上角的 -,分享这篇文章 在一些不使用操作系统的单片机软件工程里面,除了汇编启动文件之外,普遍认为程序入口就是main函数,很多程序代码都是从main函数开始 ...
- rt-thread的定时器管理源码分析
1 前言 rt-thread可以采用软件定时器或硬件定时器来实现定时器管理的,所谓软件定时器是指由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务.而硬件 ...
- 设计一条简单的等待工作队列之软件模型设计与实现(二)
上节实现了一条最简单的线程等待工作队列. http://blog.csdn.net/morixinguan/article/details/77758206 但设计还有诸多因素需要考虑和改进,例如以下 ...
最新文章
- 贪心 Codeforces Round #300 A Cutting Banner
- 理解并演示:思科的netflow功能(200-120新增考点)
- Spring Cloud Stream 与 Kafka 整合
- vue eslint 代码自动格式化
- mysql数据库replace写入_MySQL数据库replace into 用法(insert into 的增强版)
- 2010年5月22日 网络规划师下午II 试题预测和论文预测
- hive函数参考手册
- OpenSSL之自签名证书认证
- 设计一个小型的物联网应用系统_点赞 | 面向能源物联网的智能传感芯片设计与应用...
- JMeter中文使用手册
- 华为初面+综合面试(Java技术面)附上面试题
- 【大学物理·静止电荷的电场】静电场的能量
- Urchin.exe使用说明
- 家用无线路由器哪个品牌好?程序员分享值得推荐的无线路由器
- 通过 Dockerfile 搭建标注工具 brat 的镜像
- 撸了一次 Js 代码
- tomcat启动报错,找不到对应的 queue,从而引发内存泄漏
- IOS 上传IPA到AppStore
- cordova NFC读卡(javascript)
- fseek, _fseeki64 函数应用