等待队列

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的地址。

Linux源码-等待队列注释的更多相关文章

  1. linux源码分析2

    linux源码分析 这里使用的linux版本是4.8,x86体系. 这篇是 http://home.ustc.edu.cn/~boj/courses/linux_kernel/1_boot.html  ...

  2. 从linux源码看socket的阻塞和非阻塞

    从linux源码看socket的阻塞和非阻塞 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 大部分高性能网络框架采用的是非阻塞模式.笔者这次就从linux ...

  3. 从linux源码看epoll

    从linux源码看epoll 前言 在linux的高性能网络编程中,绕不开的就是epoll.和select.poll等系统调用相比,epoll在需要监视大量文件描述符并且其中只有少数活跃的时候,表现出 ...

  4. 从Linux源码看Socket(TCP)的bind

    从Linux源码看Socket(TCP)的bind 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看下Server ...

  5. linux源码阅读笔记 数组定义

    在阅读linux源码的过程中遇到了下面的略显奇怪的结构体数组定义. static struct hd_struct{ long start_sect; long nr_sects; }hd[10]={ ...

  6. linux源码阅读笔记 asm函数

    在linux源码中经常遇到__asm__函数.它其实是函数asm的宏定义 #define __asm__ asm,asm函数让系统执行汇编语句. __asm__常常与__volatile__一起出现. ...

  7. 如何从Linux源码获知版本信息

    /*************************************************************************** * 如何从Linux源码获知版本信息 * 声明 ...

  8. robotlegs2.0框架实例源码带注释

    robotlegs2.0框架实例源码带注释 Robotlegs2的Starling扩展 有个老外写了robotleges2的starling扩展,地址是 https://github.com/brea ...

  9. Linux 源码编译Python 3.6

    Linux 源码编译Python 3.6 1.操作系统以及版本显示 # uname -sr Linux 3.10.0-514.el7.x86_64 # uname -sr Linux 3.10.0-5 ...

随机推荐

  1. HDU - 1043 A* + 康托 [kuangbin带你飞]专题二

    这题我第一次用的bfs + ELFhash,直接TLE,又换成bfs + 康托还是TLE,5000ms都过不了!!我一直调试,还是TLE,我才发觉应该是方法的问题. 今天早上起床怒学了一波A*算法,因 ...

  2. JSP的内置对象以及作用域。

    最近在面试,一些基础的问题总是会被问到,虽然是基础,但是有些东西在工作中用的少,所以就有些记不清了,在面试的时候更因为紧张很容易造成原先知道的知识也会突然忘了的情况发生.所以在重新组织一下jsp的内置 ...

  3. Hive:子查询

    Hive只支持在FROM子句中使用子查询,子查询必须有名字,并且列必须唯一:SELECT ... FROM(subquery) name ...

  4. 工业级GBDT算法︱微软开源 的LightGBM(R包正在开发....)

    看完一篇介绍文章后,第一个直觉就是这算法已经配得上工业级属性.日前看到微软已经公开了这一算法,而且已经发开python版本,本人觉得等hadoop+Spark这些平台配齐之后,就可以大规模宣传啦~如果 ...

  5. mysql常用基础操作语法(十)~~子查询【命令行模式】

    mysql中虽然有连接查询实现多表连接查询,但是连接查询的性能很差,因此便出现了子查询. 1.理论上,子查询可以出现在查询语句的任何位置,但实际应用中多出现在from后和where后.出现在from后 ...

  6. Linux 系统裁剪笔记 3

    说到裁减Linux,无非是为了减小磁盘占用或者是为了某些特定场合的应用(如嵌入式系统).以RedHat 7.3为例,其最小安装仍然达到了300M,这不得不让人对一直号称小而全的Linux系统感到疑惑. ...

  7. 从1.5K到18K,一个程序员的5年成长之路

    原文地址:点击打开链接 168楼朋友批评的很有道理, 虚心接受. 我自己是开始学的时候已经错过了基础课的学习, 现在也是深受其苦的, 面临技术上的瓶颈, 需要花更多的时间补充这些知识. 希望看到此文的 ...

  8. Error Code: 1305. FUNCTION student.rand_string does not exist

    1.错误描述 13:52:42 call new_procedure Error Code: 1305. FUNCTION student.rand_string does not exist 0.0 ...

  9. but the supplied types were (flex.messaging.io.amf.ASObject) and converted to (null)."

    1.错误描述 [RPC Fault faultString="Cannot invoke method  'saveOrUpdate'. " faultCode="Ser ...

  10. 芝麻HTTP:批量部署Splash负载集群

    安装Ansible: 看官方文档去:http://www.ansible.com.cn/index.html 好像这个主控端不支持Windows? 大家虚拟机装个Ubuntu吧. 闲话少扯直接上干货: ...