5.那些队列,那些队列操作函数(2)
从队列里删除一个元素,并且将该元素做初始化,首先看__list_del():
- 155 static inline void __list_del(struct list_head * prev, struct list_head * next)
- 156 {
- 157 next->prevprev = prev;
- 158 prev->nextnext = next;
- 159 }
INIT_LIST_HEAD()其实就是初始化为最初的状态,即一个空链表。如下:
- 30 static inline void INIT_LIST_HEAD(struct list_head *list)
- 31 {
- 32 list->next = list;
- 33 list->prev = list;
- 34 }
当然了,还有一个超级经典的list_entry(),和以上所有list方面的宏一样,也是来自include/linux/list.h:
- 425 #define list_entry(ptr, type, member) \
- 426 container_of(ptr, type, member)
我相信,list_entry()这个宏在Linux内核代码中的地位都是耳熟能详的,如果你说你不知道list_entry(),那你千万别跟人说你懂Linux内核。
list_entry这个宏应该说还是有一定技术含量的。
关于list_entry(),让我们结合实例来看,在hub的故事里会接触到的一个重要的数据结构就是struct usb_hub,来自drivers/usb/core/hub.c:
- 34 struct usb_hub {
- 35 struct device *intfdev; /* the "interface" device */
- 36 struct usb_device *hdev;
- 37 struct urb *urb; /* for interrupt polling pipe */
- 38
- 39 /* buffer for urb ... with extra space in case of babble */
- 40 char (*buffer)[8];
- 41 dma_addr_t buffer_dma; /* DMA address for buffer */
- 42 union {
- 43 struct usb_hub_status hub;
- 44 struct usb_port_status port;
- 45 } *status; /* buffer for status reports */
- 46 struct mutex status_mutex; /* for the status buffer */
- 47
- 48 int error; /* last reported error */
- 49 int nerrors; /* track consecutive errors */
- 50
- 51 struct list_head event_list; /* hubs w/data or errs ready */
- 52 unsigned long event_bits[1]; /* status change bitmask */
- 53 unsigned long change_bits[1]; /* ports with logical connect
- 54 status change */
- 55 unsigned long busy_bits[1]; /* ports being reset or
- 56 resumed */
- 57 #if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
- 58 #error event_bits[] is too short!
- 59 #endif
- 60
- 61 struct usb_hub_descriptor *descriptor; /* class descriptor */
- 62 struct usb_tt tt; /* Transaction Translator */
- 63
- 64 unsigned mA_per_port; /* current for each child */
- 65
- 66 unsigned limited_power:1;
- 67 unsigned quiescing:1;
- 68 unsigned activating:1;
- 69
- 70 unsigned has_indicators:1;
- 71 u8 indicator[USB_MAXCHILDREN];
- 72 struct delayed_work leds;
- 73 };
看到了吗,51行,struct list_head event_list,这个结构体变量将为我们组建一个队列,或者说组建一个链表。我们知道,一个struct usb_hub代表一个Hub,每一个struct usb_hub有一个event_list,即每一个Hub都有它自己的一个事件列表。要知道Hub可以有一个或者多个,而Hub驱动只需要一个,或者说khubd这个精灵进程永远都只有一个。而我们的做法是,不管实际上有多少个Hub,我们最终都会将其event_list挂入到全局链表hub_event_list中来统一处理(hub_event_list是对于整个USB系统来说是全局的,但对于整个内核来说当然是局部的,毕竟它前面有一个static)。
因为最终处理所有Hub事务的都是这一个精灵进程khubd,它只需要判断hub_event_list是否为空,不为空就去处理。或者说就去触发hub_events()函数,但当我们真的要处理Hub的事件时,当然要知道具体是哪个Hub触发了这起事件。而list_entry的作用就是,从struct list_head event_list得到它所对应的struct usb_hub结构体变量。比如以下的四行代码:
- struct list_head *tmp;
- struct usb_hub *hub;
- tmp=hub_event_list.next;
- hub=list_entry(tmp,struct usb_hub,event_list);
从全局链表hub_event_list中取出一个来,叫做tmp,然后通过tmp获得它所对应的struct usb_hub。
最后是总结,中学我们学习写议论文时,老师教过这样几种结构:总分总式结构、对照式结构、层进式结构和并列式结构。而总分总式结构就是先提出中心论点,然后围绕中心,以不同角度提出分论点,展开论述,最后进行总结。而总分总具体来说又有总分、分总、总分总三种形式。
以前我以为Linus只是技术比我强,现在我算是看明白了,语文学得也比我好。看出来了吗?这里采用的就是我们议论文中的总分总结构,先设置一个链表hub_event_list,设置一个总的函数hub_events(),这是"总";然后每一个Hub都有一个event_list,每当有一个hub的event_list出现了变化,就把它的event_list插入到hub_event_list中来,这是"分";然后触发总函数hub_events(),这又是"总";然后在hub_events()里又根据event_list来确定是哪个struct usb_hub,或者说是哪个Hub有问题,又针对该Hub进行具体处理,这又是"分"。这就是Linux中Hub驱动的中心思想。
最后,提醒各位读者,struct usb_hub在这里介绍了,稍后讲到这个结构体时就不会再介绍了。