文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文读懂Linux延时队列工作原理

2024-12-02 19:19

关注

本文转载自微信公众号「Linux内核那些事」,作者songsong001 。转载本文请联系Linux内核那些事公众号。

当进程要获取某些资源(例如从网卡读取数据)的时候,但资源并没有准备好(例如网卡还没接收到数据),这时候内核必须切换到其他进程运行,直到资源准备好再唤醒进程。

waitqueue (等待队列) 就是内核用于管理等待资源的进程,当某个进程获取的资源没有准备好的时候,可以通过调用 add_wait_queue() 函数把进程添加到 waitqueue 中,然后切换到其他进程继续执行。当资源准备好,由资源提供方通过调用 wake_up() 函数来唤醒等待的进程。

等待队列初始化

要使用 waitqueue 首先需要声明一个 wait_queue_head_t 结构的变量,wait_queue_head_t 结构定义如下:

  1. struct __wait_queue_head { 
  2.     spinlock_t lock; 
  3.     struct list_head task_list; 
  4. }; 

waitqueue 本质上是一个链表,而 wait_queue_head_t 结构是 waitqueue 的头部,lock 字段用于保护等待队列在多核环境下数据被破坏,而 task_list 字段用于保存等待资源的进程列表。

可以通过调用 init_waitqueue_head() 函数来初始化 wait_queue_head_t 结构,其实现如下:

  1. void init_waitqueue_head(wait_queue_head_t *q) 
  2.     spin_lock_init(&q->lock); 
  3.     INIT_LIST_HEAD(&q->task_list); 

初始化过程很简单,首先调用 spin_lock_init() 来初始化自旋锁 lock,然后调用 INIT_LIST_HEAD() 来初始化进程链表。

向等待队列添加等待进程

要向 waitqueue 添加等待进程,首先要声明一个 wait_queue_t 结构的变量,wait_queue_t 结构定义如下:

  1. typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key); 
  2.  
  3. struct __wait_queue { 
  4.     unsigned int flags; 
  5.     void *private; 
  6.     wait_queue_func_t func; 
  7.     struct list_head task_list; 
  8. }; 

下面说明一下各个成员的作用:

flags: 可以设置为 WQ_FLAG_EXCLUSIVE,表示等待的进程应该独占资源(解决惊群现象)。

private: 一般用于保存等待进程的进程描述符 task_struct。

func: 唤醒函数,一般设置为 default_wake_function() 函数,当然也可以设置为自定义的唤醒函数。

task_list: 用于连接其他等待资源的进程。

可以通过调用 init_waitqueue_entry() 函数来初始化 wait_queue_t 结构变量,其实现如下:

  1. static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p) 
  2.     q->flags = 0; 
  3.     q->private = p; 
  4.     q->func = default_wake_function; 

也可以通过调用 init_waitqueue_func_entry() 函数来初始化为自定义的唤醒函数:

  1. static inline void init_waitqueue_func_entry(wait_queue_t *q, wait_queue_func_t func) 
  2.     q->flags = 0; 
  3.     q->private = NULL
  4.     q->func = func; 

初始化完 wait_queue_t 结构变量后,可以通过调用 add_wait_queue() 函数把等待进程添加到等待队列,其实现如下:

  1. void add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait) 
  2.     unsigned long flags; 
  3.  
  4.     wait->flags &= ~WQ_FLAG_EXCLUSIVE; 
  5.     spin_lock_irqsave(&q->lock, flags); 
  6.     __add_wait_queue(q, wait); 
  7.     spin_unlock_irqrestore(&q->lock, flags); 
  8.  
  9. static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new) 
  10.     list_add(&new->task_list, &head->task_list); 

add_wait_queue() 函数的实现很简单,首先通过调用 spin_lock_irqsave() 上锁,然后调用 list_add() 函数把节点添加到等待队列即可。

wait_queue_head_t 结构与 wait_queue_t 结构之间的关系如下图:

waitqueue

休眠等待进程

当把进程添加到等待队列后,就可以休眠当前进程,让出CPU给其他进程运行,要休眠进程可以通过一下方式:

  1. set_current_state(TASK_INTERRUPTIBLE); 
  2. schedule(); 

代码 set_current_state(TASK_INTERRUPTIBLE) 可以把当前进程运行状态设置为 可中断休眠 状态,调用 schedule() 函数可以使当前进程让出CPU,切换到其他进程执行。

唤醒等待队列

当资源准备好后,就可以唤醒等待队列中的进程,可以通过 wake_up() 函数来唤醒等待队列中的进程。wake_up() 最终会调用 __wake_up_common(),其实现如下:

  1. static void __wake_up_common(wait_queue_head_t *q,  
  2.     unsigned int mode, int nr_exclusive, int sync, void *key
  3.     wait_queue_t *curr, *next
  4.  
  5.     list_for_each_entry_safe(curr, next, &q->task_list, task_list) { 
  6.         unsigned flags = curr->flags; 
  7.  
  8.         if (curr->func(curr, mode, sync, key) && 
  9.                 (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive) 
  10.             break; 
  11.     } 

可以看出,唤醒等待队列就是变量等待队列的等待进程,然后调用唤醒函数来唤醒它们。

 

来源:Linux内核那些事内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯