5.那些队列,那些队列操作函数(1)
这一节我们讲队列。
随着子进程进入了我们的视野,我们来看其入口函数hub_thread(),这是一个令你大跌隐形眼镜的函数。
- 2817 static int hub_thread(void *__unused)
- 2818 {
- 2819 do {
- 2820 hub_events();
- 2821 wait_event_interruptible(khubd_wait,
- 2822 !list_empty(&hub_event_list) ||
- 2823 kthread_should_stop());
- 2824 try_to_freeze();
- 2825 } while (!kthread_should_stop() || !list_empty(&hub_event_list));
- 2826
- 2827 pr_debug("%s: khubd exiting\n", usbcore_name);
- 2828 return 0;
- 2829 }
这就是Hub驱动中最精华的代码。这几乎是一个死循环,但是关于Hub的所有故事都发生在这里,没错,就在这短短几行代码中。
而这其中,最核心的函数自然是hub_events()。我们先不看hub_events(),先把外面这几个函数看明白了。kthread_should_stop()的意思很明显,就是字面意思--是不是该停掉。如果是,那么这里循环就结束了,hub_thread()返回0,而要让kthread_should_stop()为真,就是当我们调用kthread_stop()时。这种情况,这个进程就该结束了。
再看hub_event_list,同样来自drivers/usb/core/hub.c:
- 83 static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */
我们来看LIST_HEAD吧,当你越接近那些核心的代码,你就会发现关于链表的定义就会越多。其实在usb-storage里面,我们也提到过一些链表,但却并没有自己用LIST_HEAD来定义过链表,因为我们用不着。可是Hub这边就有用了,当然主机控制器的驱动程序里也会有。
使用链表的目的很明确,因为有很多事情要做,于是就把它放进链表里,一件事一件事地处理。还记得我们当初在usb-storage里面提交urb请求了吗?你的U盘不停地提交urb请求,USB键盘也提交,USB鼠标也提交,那USB主机控制器怎么能应付得过来呢?很简单,建立一个队列,然后你每次提交就是往一个队列里边插入,然后USB主机控制器再统一去调度,一个一个来执行。
那么这里Hub它有什么事件?比如探测到一个设备连进来了,于是就会执行一些代码去初始化设备,所以就建一个队列。关于Linux内核中的链表,可以专门写一篇文章了,我们简单介绍,来看include/linux/list.h:
- 21 struct list_head {
- 22 struct list_head *next, *prev;
- 23 };
- 24
- 25 #define LIST_HEAD_INIT(name) { &(name), &(name) }
- 26
- 27 #define LIST_HEAD(name) \
- 28 struct list_head name = LIST_HEAD_INIT(name)
可以看出,我们无非就是定义了一个struct list_head的结构体hub_event_list,而且其两个指针next和prev分别是指向自己。换而言之,我们建立了一个链表,而且是双向链表,并且做了初始化,初始化成一个空队列。而对于这个队列的操作,内核提供了很多函数可以使用,不过在Hub中,我们将会用到这么几个函数。
- 298 static inline int list_empty(const struct list_head *head)
- 299 {
- 300 return head->next == head;
- 301 }
不言自明,判断队列是否为空。我们说了,初始化时这个hub_event_list队列是空的。
有了队列,自然就要操作队列,要往队列里加东西、减东西。就像我们每个人每天都在不停地走进去,又走出来。来看第二个函数list_add_tail(),这就是往队列里加东西:
- 84 static inline void list_add_tail(struct list_head *new, struct list_head *head)
- 85 {
- 86 __list_add(new, head->prev, head);
- 87 }
继续跟着__list_add()看就会发现其实就是往队列的末尾加一个元素:
- 43 static inline void __list_add(struct list_head *new,
- 44 struct list_head *prev,
- 45 struct list_head *next)
- 46 {
- 47 next->prev = new;
- 48 new->nextnext = next;
- 49 new->prevprev = prev;
- 50 prev->next = new;
- 51 }
再来看下一个list_del_init(),队列里的元素不能只加不减,没用了的元素就该删除掉,把空间腾出来给别人:
- 254 static inline void list_del_init(struct list_head *entry)
- 255 {
- 256 __list_del(entry->prev, entry->next);
- 257 INIT_LIST_HEAD(entry);
- 258 }