这一节我们讲队列.

从前在乡下的时候是不用排队的,村里的人们都很谦让,而且人本来又不多.后来到了县城里,县城里不大,大家去走亲戚去串门去逛街不用坐车不用排队,除了街上的游戏厅人多一点以外,别的地方人都不是很多,陪妈妈去菜市场买菜也不用排队.后来到了上海,发现去食堂吃饭要排队,开学报道要排队,在人民广场等回复旦的123路公共汽车要排队,考试成绩不好去教务处交重修费要排队,甚至连追求一个女孩子也要排队.每次看见人群排成一条长龙时,才真正意识到自己是龙的传人.

随着子进程进入了我们的视野,我们来看其入口函数,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这边就有用了,当然host controller的驱动程序里也会有,使用链表的目的很明确,因为有很多事情要做,于是就把它放进链表里,一件事一件事的处理,还记得我们当初在usb-storage里面提交urb request了吗?你U盘不停的提交urb request,他usb键盘也提交,我usb鼠标也提交,那usb host controller咋应付得过来呢?很简单,建一个队列,然后你每次提交就是往一个队列里边插入,然后usb host controller再统一去调度,一个一个来执行.那么这里hub,它有什么事件?比如探测到一个设备连进来了,于是就会执行一些代码去初始化设备,所以就建一个队列.所以,再一次证明了,谭浩强大哥的C程序设计是我们学习Linux的有力武器,书中对链表的介绍无疑是英明的,谭大哥,您不是一个人在战斗!关于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分别是指向自己,换言之,我们建立了一个链表,而且是双向链表,并且做了初始化,初始化成一个空队列.而对于这个队列的操作,内核提供了很多函数可以使用,不过在usb hub中,我们将会用到这么几个函数,它们是,

294 /**

295  * list_empty - tests whether a list is empty

296  * @head: the list to test.

297  */

298 static inline int list_empty(const struct list_head *head)

299 {

300         return head->next == head;

301 }

不言自明,判断队列是否为空,我们说了,初始化的时候这个hub_event_list()队列是空的.

有了队列,自然就要操作队列,要往队列里加东西,减东西,就像我们每个人每天都在不停的走进去,又走出来,似是梦境又不是梦境.一切都是不经意的.走进去是一年四季,走出来是春夏秋冬.来看第二个函数,list_add_tail(),这就是往队列里加东西.

76 /**

77  * list_add_tail - add a new entry

78  * @new: new entry to be added

79  * @head: list head to add it before

80  *

81  * Insert a new entry before the specified head.

82  * This is useful for implementing queues.

83  */

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->next = next;

49         new->prev = prev;

50         prev->next = new;

51 }

搞懂谭浩强那本书之后看这些链表的代码那就是小菜一碟.再来看下一个,list_del_init(),队列里的元素不能只加不减,没用了的元素就该删除掉,把空间腾出来给别人.郭敬明说过,我生命里的温暖就那么多,我全部给了你,但是你离开了我,你叫我以后怎么再对别人笑…

250 /**

251  * list_del_init - deletes entry from list and reinitialize it.

252  * @entry: the element to delete from the list.

253  */

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 }

从队列里删除一个元素,并且将该元素做初始化,首先看__list_del(),

148 /*

149  * Delete a list entry by making the prev/next entries

150  * point to each other.

151  *

152  * This is only for internal list manipulation where we know

153  * the prev/next entries already!

154  */

155 static inline void __list_del(struct list_head * prev, struct list_head * next)

156 {

157         next->prev = prev;

158         prev->next = 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:

419 /**

420  * list_entry - get the struct for this entry

421  * @ptr:        the &struct list_head pointer.

422  * @type:       the type of the struct this is embedded in.

423  * @member:     the name of the list_struct within the struct.

424  */

425 #define list_entry(ptr, type, member) \

426         container_of(ptr, type, member)

我相信,list_entry()这个宏在Linux内核代码中的地位,就相当于广告词中的任静付笛生的洗洗更健康,相当于大美女关之琳的一分钟轻松做女人,这都是耳熟能详妇孺皆知的,是经典中的经典.如果你说你不知道list_entry(),那你千万别跟人说你懂Linux内核,就好比你不知道陈文登不知道任汝芬你就根本不好意思跟人说你考过研,要知道每个考研人都是左手一本陈文登右手一本任汝芬.

可惜,关于list_entry,这个谭浩强老师的书里就没有了,当然你不能指责谭浩强的书不行,再好的书也不可能包罗万象.这个宏应该说还是有一定技术含量的.我印象中外企面试笔试中,有三次比较经典的链表考题,其中对list_entry的解释就是其中一次,威盛的笔试,而另外两次是摩托罗拉中国研发中心和IBM.摩托罗拉那次是在中信泰富广场,问了我一道骨灰级的问题,给定一个链表,如何判断链表中是否有环,而IBM那次也是问的一道化石级的问题,说给定一个单向链表,请设计一个比较快的算法来找到该链表中的倒数第m个元素.像我这种低智商+不学无术的人怎么可能回答得出这样的问题.也许只有错过IBM这个我当时最心仪的公司才能给我留下刻骨铭心的伤痛吧.每一段文字都有一段刻骨铭心的经历,人们能看见我在屏幕上打的字,却看不见我流在键盘上的泪.我记得当年有一个房地产公司来我们学校招聘,是瑞安房地产,当时我一边投简历一边对同学说,要就别让我就了瑞安,进了瑞安我第一件事就是把IBM赶出瑞安广场,哼.算了,有些东西,注定和我们无缘,以前是,现在是,将来也是.

关于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驱动的中心思想.Linus,I服了U!从前我只佩服复旦大学中文系系主任陈思和,他早上8点的公选课学生7点去就没有了座位,堪称复旦的奇迹,而Linus你和你的那些伙伴们让我们知道了真正的文学泰斗是从来不写文学著作的!是从来不讲论语心得,从来不去品三国的!

最后的最后,提醒一下,struct usb_hub这里贴出来了,稍后讲到这个结构体的时候就不会再贴了

Linux那些事儿之我是Hub(4)相关推荐

  1. Linux那些事儿之我是Hub(9)While You Were Sleeping(二)

    老实说,从函数一个开始的598行直到627行都没有什么可说的.其中需要一提的是,606行,调用usb_buffer_alloc()申请内存,赋给hub->buffer.614行,调用kmallo ...

  2. 【转】Linux那些事儿之我是Hub(7)蝴蝶效应

    朋友,你相信,一只蝴蝶在北京拍拍翅膀,将使得纽约几个月后出现比狂风还厉害的龙卷风吗?看过那部经典的影片蝴蝶效应的朋友们一定会说,这不就是蝴蝶效应吗.没错.蝴蝶效应其实是混沌学理论中的一个概念.它是指对 ...

  3. Linux那些事儿之我是Hub(1)跟我走吧,现在就出发

    最早知道hub是在大学里,复旦的4人间宿舍,条件真好,每个人一张书桌,书桌下面一个网口,但是有时候网口坏了,那可急死人了,要知道当初我们买电脑初衷虽说是为了学习C语言,可是买了之后,C倒是没学,先学了 ...

  4. Linux那些事儿之我是Hub(25)不说代码说理论

    当女作家们越来越多的使用下半身来告诉我什么是文学的时候,当模特们越来越多的使用裸体来告诉我什么是人体艺术的时候,我开始对这个社会困惑了,当行为艺术家们越来越多的使用垃圾堆砌来告诉我什么是波谱的时候,当 ...

  5. Linux那些事儿之我是Hub(2)

    莎士比亚曾经说过,不懂hub是怎么工作的就等于不知道usb设备驱动是怎么工作的.这句话一点没错,因为usb设备的初始化都是hub这边发起的,通常我们写usb设备驱动程序都是在已经得到了一个struct ...

  6. Linux那些事儿之我是Hub(19)八大重量级函数闪亮登场(三)

    在开始第三个函数前,2492行至2494行还有三行代码,对udev中的speed,bus_mA,level进行赋值. 先说一下,bus_mA,struct usb_device中的成员,unsigne ...

  7. Linux设备之我是usb,linux那些事儿之我是usb

    linux那些事儿之我是usb,复旦大学教授肖林甫先生给学生们解说的linux操作系统的一些硬件驱动开发的事儿. 内核说明: 我是U盘 说的是2.6.10的内核 我是Sysfs 说的是2.6.10的内 ...

  8. Linux系统USB驱动目录,Linux那些事儿之我是USB 目录

    目录 第1篇  Linux那些事儿之我是USB Core 1.引子 2 2.它从哪里来 2 3.PK 3 4.漫漫辛酸路 3 5.我型我秀 4 6.我是一棵树 5 7.我是谁 9 8.好戏开始了 11 ...

  9. 读书笔记《Linux那些事儿之我是USB》

    第一篇:Linux那些事儿之我是USB Core USB诞生于inel 产生是为了解决前期计算机并口串口的问题,实现一种解决速度,扩展性,易用性的通信方式. 速度:usb2.0高速模式,480MB/s ...

最新文章

  1. ORA-19502: write error on file xxxxx, block number xxxx
  2. 在一个执行力极差的团队工作是一种怎样的体验?
  3. 1071 mysql_mysql 出现1071错误怎么办
  4. stl文件 python_STL文件,一种前处理网格划分技术??
  5. where is path tag generated
  6. Linux 命令之 ifconfig -- 配置和显示网卡的网络参数
  7. 下一代SQL 产品发布会,诚邀您的参加!!包含 Azure数据服务、高级分析和SQL Server(其中包括支持Linux的SQL Server vNext)。
  8. 浅谈数据库发展史和 OceanBase 的诞生
  9. 【推荐实践】强化学习算法在京东广告序列推荐场景的应用
  10. nginx配置文件简单理解(自用)
  11. 360路由器v2刷第三方固件_路由器不拆闪存,编程器刷机技巧,恢复出厂固件办法...
  12. spring5.0学习笔记1
  13. JS 在火狐浏览器下关闭弹窗
  14. 分享8个超酷的HTML5相册动画应用
  15. 雨过天晴电脑保护系统专业版
  16. Bootstrap基础九辅助类
  17. Pytorch深度学习(二):反馈神经网络(BPNN)
  18. deepin允许root登录_[多图|需要root]在安卓手机上运行 UOS v20 arm64 版
  19. fetchMetadata: sill resolveWithNewModule raw-loader@0.5.1 checking installable status
  20. 光纤专线与普通宽带区别(ASDL)

热门文章

  1. 82岁高龄的高德纳仍在写《计算机程序设计艺术》,那是他未完成的人生目标...
  2. mysql安装完后不能配置_安装完 MySQL 后必须调整的 10 项配置
  3. HTML基础知识汇总
  4. Linux永久修改主机名
  5. 【大学生课程】《Python数据分析》课程大作业要求
  6. 【Semantic Segmentation】语义分割综述
  7. 鸿蒙-实践课程六 android、HarmonyOS 权限对比
  8. 出门问问又出音箱了,除了造型大变样,「工作地点」开始从家里转移到你的裤兜里
  9. 一张通往计算机世界的地图
  10. python错误解决TypeError: () must be callable