驱动开发中的常用数据结构和 API

本贴最后更新于 456 天前,其中的信息可能已经天翻地覆

数据结构及其关系

paltform 与字符设备驱动

  ts/

API reference

likely 和 unlikely(分支预测,提高程序效率)

  "likely" 和 "unlikely" 是一种宏定义,用于在代码中标记分支的预期执行路径,以便优化器可以更好地优化代码。这些宏通常用于条件语句,以指示哪个分支更可能执行。这对于提高代码的性能非常有用,因为它允许编译器和优化器更好地利用分支预测和 CPU 预取等硬件特性。

if (likely(some_condition)) {
    // 在这里编写预期执行频率较高的代码
} else {
    // 在这里编写预期执行频率较低的代码
}

  ​image

  在上面的示例中,likely​ 宏表示 some_condition​ 预计为真,因此编译器可以针对这个情况进行优化。相反,你可以使用 unlikely​ 宏来表示某些条件预计为假。

wait_queue(等待同步)

wait.h 中包含许多 wait queue 相关的方法和定义

  Linux 内核中用于等待事件的函数之一,它允许进程或线程等待某个条件成立或者等待一段时间后超时返回。通常,它用于等待在并发编程中某个条件满足,例如等待某个设备准备好、等待某个队列非空或等待某个信号。

unsigned long wait_event_timeout(wait_queue_head_t *q, int condition, unsigned long timeout);
  • q​ 是等待队列头(wait queue head),这是一个与条件相关的等待队列,通常是由 DECLARE_WAIT_QUEUE_HEAD​ 定义的,或者使用 init_wait_queue_head​初始化等待队列。

    image

  • condition​​ 是一个表示等待条件的函数或表达式。当这个条件为真时,等待结束,函数返回。如果条件为假,将会一直等待,直到超时。也可以使用 wake_up​唤醒等待队列。

    image

  • timeout​​ 是等待的超时时间,以 jiffies 为单位。Jiffies 是 Linux 内核的时间单位,通常是系统启动后的时钟滴答数。你可以使用 msecs_to_jiffies​​ 等函数来将毫秒转换为 jiffies。

  ​image

workqueue(并发执行而不阻塞当前进程)

  Linux 内核中用于处理延迟工作的数据结构之一。它通常与工作队列(work queue)相关联,用于在后台执行一些需要时间的工作,而不会阻塞主要的内核或用户进程。

  数据结构:workqueue_struct​:工作队列。work_struct​:实际的工作。

  1. 创建工作队列
    首先,你需要创建一个工作队列。通常,你可以使用 create_workqueue​ 函数来创建一个工作队列。例如:

    struct workqueue_struct *my_workqueue;
    my_workqueue = create_workqueue("my_workqueue");
    

    这将创建一个名为 "my_workqueue" 的工作队列。

  2. 定义工作函数
    接下来,你需要定义一个函数,这个函数将会在工作队列中执行。这个函数通常以如下方式定义:

    void my_work_function(struct work_struct *work) {
        // 执行需要延迟处理的工作
        // 这个函数可以包含你的具体工作逻辑
    }
    
  3. 初始化工作
    在执行工作之前,你需要初始化一个工作项(work item),并将其与工作函数关联起来。这通常是通过 INIT_WORK​ 宏来完成的:

    struct work_struct my_work;
    INIT_WORK(&my_work, my_work_function);
    

    image

  4. 将工作项提交到工作队列
    一旦工作项准备就绪,你可以将其提交到工作队列,以便在后台执行。这是通过 queue_work​ 函数来完成的:

    queue_work(my_workqueue, &my_work);
    

    这将触发执行 my_work_function​ 函数。

    image

  5. 销毁工作队列
    当不再需要工作队列时,通常需要销毁它以释放资源。这可以通过 destroy_workqueue​ 函数来完成:

    destroy_workqueue(my_workqueue);
    

   ​image

time_list(定时执行任务)

  Linux 内核中用于定时器管理的数据结构,它允许你创建和管理定时器,以便在一定时间后执行某些操作。

  1. 初始化定时器

    image

  2. 启动定时器

    指定的到期时间触发回调函数

    add_timer(&my_timer);
    
  3. 修改定时器值

    mod_timer​ 函数用于在运行时更改或更新一个已经配置的定时器的到期时间。这是在 Linux 内核编程中非常有用的函数,因为它允许你动态地调整定时器的触发时间而无需重新配置整个定时器。

    int mod_timer(struct timer_list *timer, unsigned long expires);
    

   ​image

  1. 停止和删除定时器
    如果需要,在定时器到期之前,你可以使用 del_timer​ 函数停止和删除定时器。

    del_timer(&my_timer);
    

   ​image

  1. 模块卸载时销毁定时器
    最后,如果你的内核模块需要卸载,确保在模块退出时销毁定时器以释放资源。

    del_timer_sync(&my_timer); // 停止和销毁定时器
    

   ​image

msecs_to_jiffies(const unsigned int m)​转换 ms 到 jiffies。

delay_work(延迟任务)

  ​delayed_work​ 是一种延迟工作(delayed work)的机制,它允许你在一定时间后执行一些工作,类似于定时器,但使用更高级别的抽象。delayed_work​ 是基于工作队列(workqueue)的延迟工作。

  1. 定义和初始化 ​​**delayed_work​ 结构**:
    在你的内核模块或代码中,首先需要定义一个 delayed_work​ 结构并初始化它。例如:

    struct delayed_work my_delayed_work;
    INIT_DELAYED_WORK(&my_delayed_work, my_delayed_work_callback);
    

    这将创建一个 delayed_work​ 结构并将其与回调函数 my_delayed_work_callback​ 关联起来。

  2. 定义工作队列
    delayed_work​ 使用工作队列来执行工作。你需要创建一个工作队列并将 delayed_work​ 关联到该队列。例如:

    struct workqueue_struct *my_workqueue;
    my_workqueue = create_workqueue("my_workqueue");
    

    也可以使用系统预定义的 workqueue,在 workqueue.h​:

    image

  3. 安排延迟工作
    使用 queue_delayed_work​ 函数来安排延迟工作,设置延迟的时间以及工作队列。例如:

    这将在 1 秒钟后执行 my_delayed_work_callback​ 函数。

    unsigned long delay_in_jiffies = msecs_to_jiffies(1000); // 延迟1秒钟
    queue_delayed_work(my_workqueue, &my_delayed_work, delay_in_jiffies);
    

    image

  4. 取消延迟任务:

    int result = cancel_delayed_work_sync(&my_delayed_work);
    if (result) {
        // 取消成功
    } else {
        // 延迟工作已经在执行中或已经取消
    }
    

    该函数将取消 my_delayed_work​,并等待它完成执行(如果它正在执行)。如果延迟工作已经在执行中,函数将等待工作完成后返回。

  5. 销毁工作队列
    当不再需要工作队列时,记得销毁它以释放资源。例如:

    destroy_workqueue(my_workqueue);
    

completion(完成量)

  用于同步和协调线程或进程之间的操作的机制。它通常用于等待某些事件完成或通知其他线程或进程某个任务已经完成。在 Linux 内核中,completion 通常由 struct completion​表示。

  ​image

  1. 初始化 completion:在使用 completion 之前,您需要先初始化它。通常在定义 completion 变量时进行初始化,可以使用 DECLARE_COMPLETION​宏或 init_completion​函数。

    DECLARE_COMPLETION(my_completion);
    // 或者
    struct completion my_completion;
    init_completion(&my_completion);
    

    image

  2. 等待 completion 完成:一个线程或进程可以使用 wait_for_completion​函数来等待 completion 变为完成状态。

    image

    当调用 wait_for_completion​时,线程将阻塞,直到 completion 变为完成状态。

  3. 标记 completion 为完成状态:另一个线程或进程可以使用 complete​函数来标记 completion 为完成状态。

    image

    当调用 complete​时,任何等待 completion 的线程将被唤醒。

  4. 通常,可以在等待 completion 的线程中执行某些操作,然后在另一个线程中使用 complete​来通知它们任务已经完成。

  5. 有时候,您可能还需要使用 complete_all​函数,以确保多个等待 completion 的线程都被唤醒。

  在 completion.c​和 completion.h​中还有一些常用 API。

  image

  ‍

  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    85 引用 • 165 回帖 • 2 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...