中断处理分为上半部和下半部
       一般来说中断处理的上半部和下半部都是不允许出现睡眠和阻塞的。但是对于下半部,并不是一刀切,下半部的实现方式有软中断和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之工作队列相关推荐

  1. RT-Thread 隐藏的宝藏之工作队列

    1. 工作队列是什么 工作队列:我们可以将不是很紧急处理的事情放到 workqueue 中处理,等待系统空闲时就会去执行 workqueue 里的事情.工作队列的本质就是开一个线程去处理排列好的任务, ...

  2. IOT-OS之RT-Thread(二)--- CPU架构与BSP移植过程

    文章目录 一.RT-Thread内核简介 二.RT-Thread CPU架构移植 2.1 Cortex-M CPU 架构简介 2.2 RT-Thread 中断机制 2.3 CPU 架构移植 三.RT- ...

  3. RT-Thread 隐藏的宝藏之管道

    一. 什么是管道 pipe: 匿名管道. 对于熟悉 linux 开发的人来说,pipe 就很熟悉了.pipe 是一种 IPC 机制,他的作用是用作有血缘进程间完成数据传递,只能从一端写入,从另外一端读 ...

  4. 【火牛STM32F103VC】RT-Thread 蜂鸣器BEEP功能验证

    开发环境 Win10 64位 Keil MDK5 [火牛开发板 STM32F103VCT6] USB 转串口线(CH340),这里使用RS232的串口,注意区分 TTL电平的 5V 直流电源,用于给开 ...

  5. RT-thread内核之进程间通信

    一.进程间通信机制 rt-thread操作系统的IPC(Inter-Process Communication,进程间同步与通信)包含有中断锁.调度器锁.信号量.互斥锁.事件.邮箱.消息队列.其中前5 ...

  6. .net thread操作串口_听说你不知道 RT-Thread 有个 ringbuffer

    在嵌入式开发中,我们经常需要用到 FIFO 数据结构来存储数据,比如任务间的通信.串口数据收发等场合.很多小伙伴不知道 RT-Thread 为我们提供了一个 ringbuffer 数据结构,代码位于: ...

  7. rt-thread端口时钟使能_(2)RTThread启动过程分析

    点击上方蓝字,关注微联智控工作室 可点击右上角的 -,分享这篇文章 在一些不使用操作系统的单片机软件工程里面,除了汇编启动文件之外,普遍认为程序入口就是main函数,很多程序代码都是从main函数开始 ...

  8. rt-thread的定时器管理源码分析

    1 前言 rt-thread可以采用软件定时器或硬件定时器来实现定时器管理的,所谓软件定时器是指由操作系统提供的一类系统接口,它构建在硬件定时器基础之上,使系统能够提供不受数目限制的定时器服务.而硬件 ...

  9. 设计一条简单的等待工作队列之软件模型设计与实现(二)

    上节实现了一条最简单的线程等待工作队列. http://blog.csdn.net/morixinguan/article/details/77758206 但设计还有诸多因素需要考虑和改进,例如以下 ...

最新文章

  1. 贪心 Codeforces Round #300 A Cutting Banner
  2. 理解并演示:思科的netflow功能(200-120新增考点)
  3. Spring Cloud Stream 与 Kafka 整合
  4. vue eslint 代码自动格式化
  5. mysql数据库replace写入_MySQL数据库replace into 用法(insert into 的增强版)
  6. 2010年5月22日 网络规划师下午II 试题预测和论文预测
  7. hive函数参考手册
  8. OpenSSL之自签名证书认证
  9. 设计一个小型的物联网应用系统_点赞 | 面向能源物联网的智能传感芯片设计与应用...
  10. JMeter中文使用手册
  11. 华为初面+综合面试(Java技术面)附上面试题
  12. 【大学物理·静止电荷的电场】静电场的能量
  13. Urchin.exe使用说明
  14. 家用无线路由器哪个品牌好?程序员分享值得推荐的无线路由器
  15. 通过 Dockerfile 搭建标注工具 brat 的镜像
  16. 撸了一次 Js 代码
  17. tomcat启动报错,找不到对应的 queue,从而引发内存泄漏
  18. IOS 上传IPA到AppStore
  19. cordova NFC读卡(javascript)
  20. fseek, _fseeki64 函数应用

热门文章

  1. 41 01背包 记录方案
  2. 计算机专业需要用独显吗,集显 or 独显 我的程序用哪个必须由我定
  3. 解决H5 audio自动播放无效问题(应用于一切环境的一切浏览器)
  4. python使用多线程读写数据到文件2
  5. 百城价格房价周期和郑州、武汉房价比较分析
  6. 大数据分析AI和机器学习在医疗行业的应用
  7. 武汉上海知名互联网公司面试心得体会
  8. 信息泄露防护案例分享
  9. android switch 未定义,源生Switch控件在Android4.4无法显示?
  10. 算法导论 练习5.4-4