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);
        }
        ......
    }
    ......
}

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值