• Linux源码-等待队列注释


    等待队列

    Linux中了等待队列的毒,代码中充斥着等待队列。不信你翻翻代码。

    等待队列的唤醒我们这里叫激活。免得和线程唤醒混淆。

    转载注明出处哦:http://www.cnblogs.com/stonehat/p/8627302.html

    数据结构

    1. 头结点wait_queue_head_t的结构
    struct __wait_queue_head {
        
        // 自旋锁,用来做同步
    	spinlock_t lock;
        
    	// 链表,
    	struct list_head task_list;
    };
    
    // 熟悉的wait_queue_head_t实际上是struct __wait_queue_head
    typedef struct __wait_queue_head wait_queue_head_t;
    
    
    1. 普通节点wait_queue_t的结构
    
       typedef struct __wait_queue wait_queue_t;
    
       //wait_queue_func_t的定义
       typedef int (*wait_queue_func_t)(wait_queue_t *wait, unsigned mode, int sync, void *key);
    
       //__wait_queue的定义
       struct __wait_queue {
           // 激活后是否继续激活下一个entry。候选值为WQ_FLAG_EXCLUSIVE。一般设置为0。
           // 当等待队列所有entry的flags==0时,等待队列所有entry都会被激活。所以就会有惊群现象。
       	unsigned int flags;
           // 排他性标志,调用wake_up时,可以传入参数,控制激活多少个排他性的entry就停止。
       #define WQ_FLAG_EXCLUSIVE	0x01
           
           //线程结构
       	struct task_struct * task;
           
           // 函数指针。被激活时调用。
       	wait_queue_func_t func;
           
           // listItem。内核链表如何做通用化的。就是靠特殊的宏操作。
       	struct list_head task_list;
       };
    

    函数

    一、初始化

    1. 头节点初始化

    #define INIT_LIST_HEAD(ptr) do { 
    	(ptr)->next = (ptr); (ptr)->prev = (ptr); 
    } while (0)
    
    static inline void init_waitqueue_head(wait_queue_head_t *q)
    {
        // 初始化自旋锁
    	q->lock = SPIN_LOCK_UNLOCKED;
        // 初始化链表
    	INIT_LIST_HEAD(&q->task_list);
    }
    
    
    

    2. entry节点初始化

    // 初始化一个等待队列entry
    // 这个entry在激活的时候直接会唤醒task_struct线程
    static inline void init_waitqueue_entry(wait_queue_t *q, struct task_struct *p)
       {
       	//表示这个不是排他性的entry
        q->flags = 0;
       	
        q->task = p;
        
        // 默认给一个唤醒q->task的函数指针。
       	q->func = default_wake_function;
       }
    
    // 初始化一个等待队列entry
    // 这个entry在激活的时候仅仅调用func.
    
    static inline void init_waitqueue_func_entry(wait_queue_t *q,
       					wait_queue_func_t func)
       {
       	q->flags = 0;
       	q->task = NULL;
       	q->func = func;
       }
    

    二、添加

    
    extern void FASTCALL(add_wait_queue(wait_queue_head_t *q, wait_queue_t * wait));
    extern void FASTCALL(add_wait_queue_exclusive(wait_queue_head_t *q, wait_queue_t * wait));
    
    

    代码实现

    void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    {
    	unsigned long flags;
    
    	wait->flags &= ~WQ_FLAG_EXCLUSIVE;
    	spin_lock_irqsave(&q->lock, flags);
    	__add_wait_queue(q, wait);
    	spin_unlock_irqrestore(&q->lock, flags);
    }
    static inline void __add_wait_queue(wait_queue_head_t *head, wait_queue_t *new)
    {
    	list_add(&new->task_list, &head->task_list);
    }
    static inline void list_add(struct list_head *new, struct list_head *head)
    {
    	__list_add(new, head, head->next);
    }
    
    
    

    简化代码看

    void fastcall add_wait_queue(wait_queue_head_t *q, wait_queue_t *wait)
    {
    	unsigned long flags;
    
    	wait->flags &= ~WQ_FLAG_EXCLUSIVE;
        //加锁保护,保存中断
    	spin_lock_irqsave(&q->lock, flags);
        
        q->task_list->pre=wait->task_list;
        wait->task_list->next=q->task_list;
        wait->task_list->pre=q->task_list->next;
        q->task_list->next = wait->task_list;
        
    	__add_wait_queue(q, wait);
        //解锁。
    	spin_unlock_irqrestore(&q->lock, flags);
    }
    

    三、删除

    extern void FASTCALL(remove_wait_queue(wait_queue_head_t *q, wait_queue_t * wait));
    

    //忽略

    四、队列激活

    #define wake_up(x)			__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 1, NULL)
    #define wake_up_nr(x, nr)		__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, nr, NULL)
    #define wake_up_all(x)			__wake_up(x, TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE, 0, NULL)
    #define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
    #define wake_up_interruptible_nr(x, nr)	__wake_up(x, TASK_INTERRUPTIBLE, nr, NULL)
    #define wake_up_interruptible_all(x)	__wake_up(x, TASK_INTERRUPTIBLE, 0, NULL)
    #define	wake_up_locked(x)		__wake_up_locked((x), TASK_UNINTERRUPTIBLE | TASK_INTERRUPTIBLE)
    #define wake_up_interruptible_sync(x)   __wake_up_sync((x),TASK_INTERRUPTIBLE, 1)
    
    /**
     * 激活等待队列.
     * @q: the waitqueue
     * @mode: which threads
     * @nr_exclusive: 最多激活多少个WQ_FLAG_EXCLUSIVE属性的entry。0表示都不限制。
     */
    void fastcall __wake_up(wait_queue_head_t *q, unsigned int mode,
    				int nr_exclusive, void *key)
    {
    	unsigned long flags;
    
    	spin_lock_irqsave(&q->lock, flags);
    	__wake_up_common(q, mode, nr_exclusive, 0, key);
    	spin_unlock_irqrestore(&q->lock, flags);
    }
    
    
    /*
     * 激活核心代码。遍历所有task_list,取出wait_queue_t结构(宏操作取出),执行里面的func。
     * nr_exclusive表示要执行多少个WQ_FLAG_EXCLUSIVE属性的entry。
     */
    static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
    			     int nr_exclusive, int sync, void *key)
    {
    	struct list_head *tmp, *next;
    
    	list_for_each_safe(tmp, next, &q->task_list) {
    		wait_queue_t *curr;
    		unsigned flags;
    		curr = list_entry(tmp, wait_queue_t, task_list);
    		flags = curr->flags;
    		if (curr->func(curr, mode, sync, key) &&
    		    (flags & WQ_FLAG_EXCLUSIVE) &&
    		    !--nr_exclusive)
    			break;
    	}
    }
    
    

    巧妙的宏

    在等待队列中,队列其实是由list_head构成的,而在遍历激活entry的时候,可以取出对应的wait_queue_t结构体。如何做到的?

    看下wait_queue_t结构。

    struct __wait_queue {
       
    	unsigned int flags;
    #define WQ_FLAG_EXCLUSIVE	0x01
        
    	struct task_struct * task;
        
    	wait_queue_func_t func;
        
        // list_head在结构体内部。
    	struct list_head task_list;
    };
    

    我们一般是从外部去取内部成员,而内核链表是通过内部成员去取外部结构体指针。有什么好处?这样可以做通用的链表结构,而不用担心内部单元类型。

    那如何从内部成员获得外部结构体指针呢?以wait_queue_t 的变量a为例,内部task_list地址记为b。

    b- &( ( (wait_queue_t *) 0 )->task_list)可以获得wait_queue_t a的地址。

  • 相关阅读:
    .NET 多线程 Task async await
    .NET5 MVC 文件目录
    Html 问题记录
    vue学习笔记(记录知识点)
    vue调试工具vue-devtools安装及使用
    node.js入坑记录
    vue从0开始笔记
    前端样式css问题记录
    谷歌浏览器chrome console 发送POST/GET请求
    jQuery的请求数据方式
  • 原文地址:https://www.cnblogs.com/stonehat/p/8627302.html
Copyright © 2020-2023  润新知