验证码: 看不清楚,换一张 查询 注册会员,免验证
  • {{ basic.site_slogan }}
  • 打开微信扫一扫,
    您还可以在这里找到我们哟

    关注我们

Android内核wake_up源码分析

阅读:1140 来源:乙速云 作者:代码code

Android内核wake_up源码分析

      内核中通常用法:

      内核有个函数 wake_up 和 wake_up_interruptible 通常来说看到这俩函数调用就是唤醒等待队列上的线程。

      直到看了epoll的源码,发现并非如此。

          bool wakeup_condition;
          wait_queue_head_t wait_queue;
          init_waitqueue_head(&wait_queue);
          wait_queue_entry_t wq_entry
      // wait 
          wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop());
      // 唤醒
      // 设置等待条件为true,并唤醒
          wakeup_condition = true;
          wake_up(&wait_queue);

      wake_up 的源码:

      // common/include/linux/wait.h
      #define TASK_NORMAL			(TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE)
      #define wake_up(x)			        __wake_up(x, TASK_NORMAL, 1, NULL)
      #define wake_up_interruptible(x)	__wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
      // common/kernel/sched/wait.c
      // wake_up 是个宏,展开后调用的是 __wake_up 函数
      // __wake_up(x, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, NULL)
      int __wake_up(struct wait_queue_head *wq_head, unsigned int mode, int nr_exclusive, void *key)
      {
      	return __wake_up_common_lock(wq_head, mode, nr_exclusive, 0, key);
      }
      EXPORT_SYMBOL(__wake_up);
      // __wake_up_common_lock(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL)
      static int __wake_up_common_lock(struct wait_queue_head *wq_head, unsigned int mode,
      			int nr_exclusive, int wake_flags, void *key)
      {
      	unsigned long flags;
      	wait_queue_entry_t bookmark;
      	int remaining = nr_exclusive;
      	bookmark.flags = 0;
      	bookmark.private = NULL;
      	bookmark.func = NULL;
      	INIT_LIST_HEAD(&bookmark.entry);//初始化链表: 链表的next和prev指针都指向链表自身地址
      	do {
      		spin_lock_irqsave(&wq_head->lock, flags);//自旋锁上锁,对队列上锁
      		remaining = __wake_up_common(wq_head, mode, remaining, wake_flags, key, &bookmark);
      		spin_unlock_irqrestore(&wq_head->lock, flags);//自旋锁解锁
      	} while (bookmark.flags & WQ_FLAG_BOOKMARK);
      	return nr_exclusive - remaining;//队列为空时,remaining=nr_exclusive ,此时 return 0;
      }
      // __wake_up_common(wq_head, TASK_INTERRUPTIBLE | TASK_UNINTERRUPTIBLE, 1, 0, NULL, &bookmark);
      static int __wake_up_common(struct wait_queue_head *wq_head, unsigned int mode,
      			int nr_exclusive, int wake_flags, void *key,
      			wait_queue_entry_t *bookmark)
      {
      	wait_queue_entry_t *curr, *next;
      	int cnt = 0;
      	lockdep_assert_held(&wq_head->lock);
          // bookmark.flags = 0;  WQ_FLAG_BOOKMARK = 0x04;
      	if (bookmark && (bookmark->flags & WQ_FLAG_BOOKMARK)) {//不会进入此分支
      		curr = list_next_entry(bookmark, entry);
      		list_del(&bookmark->entry);
      		bookmark->flags = 0;
      	} else
      		curr = list_first_entry(&wq_head->head, wait_queue_entry_t, entry);//获取wq_head队列的第一个元素
      	if (&curr->entry == &wq_head->head)//队列为空时,直接返回传入的 nr_exclusive
      		return nr_exclusive;
      	list_for_each_entry_safe_from(curr, next, &wq_head->head, entry) {//遍历链表
      		unsigned flags = curr->flags;
      		int ret;
      		if (flags & WQ_FLAG_BOOKMARK)
      			continue;
      /*
      调用 wait_queue_entry_t 中的回调函数 func
      //   这里依据func的类型会出现不同的结果。
      使用 init_waitqueue_entry 初始化的 wait_queue_entry_t ,func = default_wake_function,这个函数会唤醒 curr->private 上的线程。
      使用 init_waitqueue_func_entry 初始化的 wait_queue_entry_t,仅仅是做普通的函数调用。
      */
      		ret = curr->func(curr, mode, wake_flags, key);
      		if (ret < 0)
      			break;
      		if (ret && (flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
      			break;
      		if (bookmark && (++cnt > WAITQUEUE_WALK_BREAK_CNT) &&
      				(&next->entry != &wq_head->head)) {
      			bookmark->flags = WQ_FLAG_BOOKMARK;
      			list_add_tail(&bookmark->entry, &next->entry);
      			break;
      		}
      	}
      	return nr_exclusive;
      }

      func 赋值过程

      wait_queue_head 和 wait_queue_entry 数据结构

      //内核4.14以后
      // common/include/linux/wait.h
      struct wait_queue_head {// wait队列
      	spinlock_t		lock;     // 自旋锁
      	struct list_head	head; // 添加到 wait 队列时,就是把wait_queue_entry.entry 加入这个 head 链表
      };
      /*
       * A single wait-queue entry structure:
       */
      struct wait_queue_entry {// wait队列的一个项
      	unsigned int		flags;
      	void			*private;   // 私有数据,在init_waitqueue_entry中代表线程,在init_waitqueue_func_entry中为null
      	wait_queue_func_t	func;   // 回调函数
      	struct list_head	entry;  // 添加到 wait 队列时,就是把这个 entry 加入到 wait_queue_head.head 的链表
      };
      typedef struct wait_queue_head wait_queue_head_t;   // wait_queue_head_t  同 wait_queue_head
      typedef struct wait_queue_entry wait_queue_entry_t; // wait_queue_entry_t 同 wait_queue_entry

      对于 wait_queue_entry 有两种常用的初始化方法 init_waitqueue_entryinit_waitqueue_func_entry

      两种等待任务 wait_queue_entry:线程 和 函数

      // common/include/linux/wait.h
      static inline void init_waitqueue_entry(struct wait_queue_entry *wq_entry, struct task_struct *p)
      {
      	wq_entry->flags		= 0;
      	wq_entry->private	= p; // 把需要唤醒的线程存储到 private 数据中
          // func 赋值为 default_wake_function 函数
          // 这个函数的作用是 唤醒等待队列上的线程
      	wq_entry->func		= default_wake_function; // 这函数作用是:唤醒线程 p
      }
      static inline void init_waitqueue_func_entry(struct wait_queue_entry *wq_entry, wait_queue_func_t func)
      {
      	wq_entry->flags		= 0;
      	wq_entry->private	= NULL;
      	wq_entry->func		= func; // 直接把传入的回调函数赋值给 wq_entry->func
      }

      default_wake_function 函数

      这个函数的作用基本等效于 wake_up_process 函数。

      int default_wake_function(wait_queue_entry_t *curr, unsigned mode, int wake_flags,
      			  void *key)
      {
      	WARN_ON_ONCE(IS_ENABLED(CONFIG_SCHED_DEBUG) && wake_flags & ~WF_SYNC);
          //try_to_wake_up函数通过把进程状态设置为TASK_RUNNING, 并把该进程插入本地CPU运行队列rq来达到唤醒睡眠和停止的进程的目的.
          // curr->private 存储了需要唤醒的线程
      	return try_to_wake_up(curr->private, mode, wake_flags);
      }
      EXPORT_SYMBOL(default_wake_function);

      综上:

      • wake_up ,可能是唤醒队列上的线程,也可能仅仅是触发一个回调而已

      wake_up的两种用法:

          bool wakeup_condition;
          wait_queue_head_t wait_queue;
          init_waitqueue_head(&wait_queue);
          wait_queue_entry_t wq_entry
      // wait
      第一种用法:线程等待
          wait_event_interruptible(&wait_queue, wakeup_condition || kthread_should_stop());
      第二种用法:添加一个回调到等待队列上
          init_waitqueue_func_entry(&wq_entry, callback);
          add_wait_queue(&wait_queue, &wq_entry);
      // 唤醒
        设置等待条件为true,并唤醒
          wakeup_condition = true;
      // 内部遍历队列,调用每个 wait_queue_entry 的 func 函数,根据func不同为产生不同效果
          wake_up(&wait_queue);
    分享到:
    *特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: hlamps#outlook.com (#换成@)。
    相关文章
    {{ v.title }}
    {{ v.description||(cleanHtml(v.content)).substr(0,100)+'···' }}
    你可能感兴趣
    推荐阅读 更多>