2:中断中用work_queue 即 wait_queue 协作(简单工作中使用的示例)

1. 基本介绍

1.1:work_queue(相关接口在linux/kernel/workqueue.c和 linux/include/linux/workqueue.h中)

内核驱动中,一般的小型任务(work)都不会自己起一个线程来处理,而是扔到workqueu中处理。workqueue的主要工作就是用进程上下文来处理内核中大量的小任务(本质也是调用了)。

 工作队列是一种将工作推后执行的形式(中断下半部),它和tasklet有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势。最重要的就是工作队列允许被重新调度甚至是睡眠。
      那么,什么情况下使用工作队列,什么情况下使用tasklet。如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要用一个可以重新调度的实体来执行你的下半部处理,也应该使用工作队列。它是唯一能在进程上下文运行的下半部实现的机制,也只有它才可以睡眠。这意味着在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,它都会非常有用。如果不需要用一个内核线程来推后执行工作,那么就考虑使用tasklet.

所以workqueue的主要设计思想:一个是并行,多个work不要相互阻塞;另外一个是节省资源,多个work尽量共享资源(进程、调度、内存),不要造成系统过多的资源浪费。workqueue有如下多种优先级,常用system_wq(工作者)即可,每个工作者即一个线程.

工作放入工作队列之后,由管理此工作队列的工作者来执行这些work,通过alloc_workqueue或create_singlethread_workqueue来创建工作者线程,它最后调用kthread_create创建线程,其线程名为alloc_workqueue中指定的name,system_wq的线程名字即为"events" 其举例如下:

static int __init init_workqueues(void)  
{  
    unsigned int cpu;  
    int i;  
        ...  
    system_wq = alloc_workqueue("events", 0, 0);  
    system_long_wq = alloc_workqueue("events_long", 0, 0);  
    system_nrt_wq = alloc_workqueue("events_nrt", WQ_NON_REENTRANT, 0);  
    system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,  
                        WQ_UNBOUND_MAX_ACTIVE);  
    system_freezable_wq = alloc_workqueue("events_freezable",  
                          WQ_FREEZABLE, 0);  
    BUG_ON(!system_wq || !system_long_wq || !system_nrt_wq ||  
           !system_unbound_wq || !system_freezable_wq);  
    return 0;  

 workqueue是对内核线程封装的用于处理各种工作项的一种处理方法, 由于处理对象是用链表拼接一个个工作项, 依次取出来处理, 然后从链表删除,就像一个队列排好队依次处理一样, 所以也称工作队列,所谓封装可以简单理解一个中转站, 一边指向“合适”的内核线程, 一边接受你丢过来的工作项, 用结构体 workqueue_srtuct表示, 而所谓工作项也是个结构体 --  work_struct, 里面有个成员指针, 指向你最终要实现的函数,

struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
};

当然使用者在实现自己函数功能后可以直接调用,或者通过kthread_create()把函数当做新线程的主代码, 或者add_timer添加到一个定时器延时处理, 

那为何要弄个work_struct工作项先封装函数, 然后再丢到workqueue_srtuct处理呢? 这就看使用场景了, 如果是一个大函数, 处理事项比较多, 且需要重复处理, 可以单独开辟一个内核线程处理; 对延时敏感的可以用定时器; 

如果只是简单的一个函数功能,  且函数里面有延时动作的, 就适合放到工作队列来处理了, 毕竟定时器处理的函数是在中断上下文,不能delay或者引发进程切换的API,  而且开辟一个内核线程是耗时且耗费资源的, 一般用于函数需要while(1) 不断循环处理的,

不然处理一次函数后退出,线程又被销毁, 简直就是浪费!

将自己的工作项挂到已有的工作队列需要注意的是由于这些队列是共享的, 各个驱动都有可能将自己的工作项放到同个队列, 会导致队列的项拥挤, 当有些项写的代码耗时久或者调用delay()延时特别久, 你的项将会迟迟得不到执行! 

所以早期很多驱动开发人员都是自己创建workqueue, 添加自己的work。 在Linux-2.XXX时代, 创建workqueue时会创建属于workqueue自己的内核线程, 这些线程是“私有的”, 虽然是方便了驱动开发人员, 但每个驱动都“一言不合”就

创建workqueue导致太多线程, 严重占用系统资源和效率, 所以在Linux-3.XXX时代, 社区开发人员将workqueue和内核线程剥离! 内核会自己事先创建相应数量的线程(后面详解), 被所有驱动共享使用。  用户调用alloc_workqueue();

1.2 :wait_queue 

等待队列在linux内核中有着举足轻重的作用,很多linux驱动都或多或少涉及到了等待队列。因此,对于linux内核及驱动开发者来说,掌握等待队列是必须课之一。 Linux内核的等待队列是以双循环链表为基础数据结构,与进程调度机制紧密结合,能够用于实现核心的异步事件通知机制。它有两种数据结构:等待队列头(wait_queue_head_t)和等待队列项(wait_queue_t)。等待队列头和等待队列项中都包含一个list_head类型的域作为”连接件”。它通过一个双链表和把等待task的头,和等待的进程列表链接起来,在内核里面,等待队列是有很多用处的,尤其是在中断处理、进程同步、定时等场合。可以使用等待队列在实现阻塞进程的唤醒。它以队列为基础数据结构,与进程调度机制紧密结合,能够用于实现内核中的异步事件通知机制,同步对系统资源的访问等。

2. 使用示例

.h中:

    struct work_struct retreive_frame_work;

driver init中:

    INIT_WORK(&retreive_frame_work, retrieve_desc_task_callback);

    init_waitqueue_head(&frame_wq);

中断ISR中:
    {
        queue_work(system_wq, &retreive_frame_work);

    }

void retrieve_desc_task_callback(struct work_struct *work){
    add_desc(iav, 0); //生产frame_desc
    wake_up_interruptible_all(&frame_wq); //通知消费者,frame_desc已经生产完毕,可以去取了
}

消费由 retrieve_desc_task_callback 生产的 frame_desc(blocking 的方式) 

wait_event_interruptible(frame_wq,
            (desc = iav_find_frame_desc()));

参考链接如下:作者如需要删除请联系我,谢谢!

1. https://www.cnblogs.com/vedic/p/11069249.html

2. https://blog.youkuaiyun.com/morixinguan/article/details/69666642 ,包含诸如断tasklet, workqueue,softirq,中断下半部(bottom half)等概念

<think>嗯,用户遇到了一个编译错误,说'sc_core::sc_event_queue'类没有名为'num_available'的成员。我需要先确认这个错误的原因。 首先,用户可能是在SystemC的代码中调用了sc_event_queue的num_available()方法。但根据我的知识,SystemC的标准库中,sc_event_queue类确实没有这个成员函数。可能用户是参考了某个文档或者示例代码,但那个方法并不存在。 接下来,我应该考虑用户为什么会使用这个方法。可能他们想查询事件队列中待处理事件的数量。这时候,我需要检查SystemC的文档,看看是否有替代的方法或者属性。比如,是否有size()或者类似的函数,或者是否需要通过其他方式获取事件数量。 另外,用户可能混淆了sc_event_queue和其他类似的类,比如sc_fifo,后者确实有num_available()方法。如果是这种情况,需要指出两者的区别,并建议用户检查他们是否正确使用了类名。 然后,我需要建议用户检查他们的SystemC版本。如果使用的是旧版本,可能某些方法被弃用或不存在。建议升级到最新版本,并查阅对应版本的文档确认。 最后,如果确实没有这个方法,可能需要用户自己实现统计事件数量的功能,比如在事件被通知时维护一个计数器。这时候需要给出示例代码,帮助用户理解如何实现。</think>你遇到的编译错误`error: 'class sc_core::sc_event_queue' has no member named 'num_available'`表示在SystemC的`sc_event_queue`类中找不到`num_available()`方法。以下是逐步分析及解决方案: --- ### **1. 错误原因分析** - **SystemC官方文档验证** `sc_event_queue`是SystemC中用于管理事件队列的类,但根据IEEE 1666-2011标准,其公开成员函数中确实**没有**`num_available()`方法。 - **可能的混淆来源** 此错误通常是因为开发者混淆了`sc_event_queue`与其他SystemC类(如`sc_fifo`)的API。例如: - ✅ `sc_fifo`有`num_available()`方法(查询可用数据数量) - ❌ `sc_event_queue`没有此方法 --- ### **2. 解决方案** #### **方案一:检查API调用意图** - **如果目标是查询事件队列中的事件数量** SystemC未直接提供该功能。需通过自定义计数器实现: ```cpp class CustomEventQueue { public: void notify(double delay) { event_queue.notify(delay); event_count++; // 自定义计数器 } size_t num_available() const { return event_count; } private: sc_core::sc_event_queue event_queue; size_t event_count = 0; }; ``` #### **方案二:检查类是否误用** - **如果实际需要使用`sc_fifo`** 直接替换类名并验证API: ```cpp sc_core::sc_fifo<int> fifo; std::cout << fifo.num_available(); // 合法调用 ``` #### **方案三:检查SystemC版本** - 通过`systemc.h`头文件确认版本: ```cpp #define SYSTEMC_VERSION // 例如:SYSTEMC_VERSION=201710 ``` 较新版本可能新增了API,需查阅对应版本的[官方文档](https://www.accellera.org/downloads/standards/systemc)。 --- ### **3. 验证步骤** 1. **最小化复现代码** ```cpp #include <systemc.h> int main() { sc_core::sc_event_queue eq; eq.num_available(); // 触发编译错误 return 0; } ``` 2. **编译时添加-I参数** 确保编译器能找到正确的SystemC头文件路径: ```bash g++ -I/path/to/systemc/include ... ``` --- ### **4. 扩展建议** - **调试事件队列** 可通过`sc_event_queue::get_interface()`获取底层实现,但需注意平台兼容性。 - **替代方案** 使用`std::queue<sc_event>`自行管理事件队列,配合`sc_time`实现延时触发。 若问题仍未解决,请提供更多上下文代码,我会进一步分析。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值