`
javatome
  • 浏览: 863503 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux那些事儿之我是Hub(4)那些队列,那些队列操作函数

 
阅读更多

这一节我们讲队列.

从前在乡下的时候是不用排队的,村里的人们都很谦让,而且人本来又不多.后来到了县城里,县城里不大,大家去走亲戚去串门去逛街不用坐车不用排队,除了街上的游戏厅人多一点以外,别的地方人都不是很多,陪妈妈去菜市场买菜也不用排队.后来到了上海,发现去食堂吃饭要排队,开学报道要排队,在人民广场等回复旦的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,而且其两个指针nextprev分别是指向自己,换言之,我们建立了一个链表,而且是双向链表,并且做了初始化,初始化成一个空队列.而对于这个队列的操作,内核提供了很多函数可以使用,不过在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,每当有一个hubevent_list出现了变化,就把它的event_list插入到hub_event_list中来,这是分,然后触发总函数hub_events(),这又是总,然后在hub_events()里又根据event_list来确定是哪个struct usb_hub,或者说是哪个hub有事情,又针对该hub进行具体处理,这又是分.这就是Linuxhub驱动的中心思想.Linus,I服了U!从前我只佩服复旦大学中文系系主任陈思和,他早上8点的公选课学生7点去就没有了座位,堪称复旦的奇迹,Linus你和你的那些伙伴们让我们知道了真正的文学泰斗是从来不写文学著作的!是从来不讲论语心得,从来不去品三国的!

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

分享到:
评论

相关推荐

    Linux那些事儿之我是EHCI主机控制器.pdf

    ### Linux那些事儿之我是EHCI主机控制器 #### 引言 在深入了解EHCI(Enhanced Host Controller Interface,增强型主机控制器接口)之前,让我们先简要回顾一下EHCI的背景及其在Linux系统中的作用。EHCI是USB 2.0...

    USB-HUB驱动分析 .docx

    本文旨在深入探讨USB Hub驱动的设计原理及其实现机制,尤其针对Linux操作系统环境下的USB Hub驱动进行详细分析。通过解读代码逻辑及调用流程,帮助初学者更好地理解USB Hub的工作原理及其实现细节。 #### USB Hub...

    linux下usb热插拔处理

    而在Linux操作系统中,USB热插拔的处理机制主要涉及到内核中的USB子系统,特别是USBHUB驱动。 ### Linux下USBHUB驱动的实现 Linux内核中的USBHUB驱动负责管理和监控USB集线器上的设备连接状态。在系统初始化阶段,...

    qcloud_iothub_mp_cloudfunctions_farmai.rar

    4. **云端函数**:在描述中提到的“云端获取设备状态函数”和“发布主题函数”,可能指的是腾讯云的云函数(Cloud Functions)。这是一种无服务器计算服务,可以根据事件触发自动执行代码,减少运维工作。在这个场景...

    linux USB驱动初始化流程及数据收发流程

    - 如果是集线器操作,`hub_control()`和`hub_status_data()`处理集线器相关的控制传输。 2. **数据接收**: - 数据接收通常在设备驱动的中断处理程序中进行,当数据到达时,`urb->complete`回调函数会被调用。 -...

    USB 热拔插1

    首先,`hub_driver`结构体定义了USB集线器(hub)驱动的行为,包括在设备插入时执行的`probe`函数、设备断开连接时的`disconnect`函数,以及设备的挂起、恢复、重置等相关操作。`usb_register`函数用于注册这个驱动...

    linux USB设备驱动开发深度解析-宋宝华

    主机控制器驱动是Linux USB驱动框架中最核心的部分之一,它位于操作系统和物理USB控制器硬件之间,负责协调USB设备的访问。 - **数据结构**: - `struct usb_hcd`:表示一个USB主机控制器。 - `struct hc_driver`...

    USB的Linux驱动开发

    ### Linux USB驱动开发详解 #### 一、USB及驱动框架简介 ##### 1.1 USB协议 USB(Universal Serial Bus)是一种广泛应用于个人电脑和其他数字设备间的通用串行总线标准,用于数据交换和电源供应。它支持多种类型的...

    intel开源i915显卡驱动代码

    Intel的i915驱动是Linux内核的一部分,负责与Intel的GMA(Graphics Memory Controller Hub)系列集成显卡进行通信。它不仅提供了基本的图形输出功能,还支持3D加速、视频解码和硬件加速等高级特性。由于是开源项目,...

    天祥ARM视频第10讲.pptx

    【ARM技术】学习ARM处理器平台并不比学习单片机复杂,尤其在涉及到USB驱动编程时,我们可以看到两者有很多相似之处。在Linux系统下,USB设备的访问方式是通过文件系统,每个USB设备都有对应的设备节点,由主设备号和...

    Android中G-Sensor相关流程

    5. 当 G-sensor 上报数据的时候,触发中断,然后在中断处理函数中提交一个报值的任务到队列中并禁止中断。 6. 在工作队列中读数 G-sensor 的数据并上报到 input 子系统中,最后使能中断。 在 Android 上层应用 APK ...

    2021-2022计算机二级等级考试试题及答案No.3476.docx

    1. Linux操作系统:Linux是由芬兰大学生Linus Torvalds开发的一种开源、免费的操作系统,现在已被广泛应用在各种领域,包括服务器、嵌入式设备和桌面环境。 2. Python编程:Python中的`random.shuffle()`函数用于...

    2021-2022计算机二级等级考试试题及答案No.10952.docx

    6. 二进制数值操作:删除一个非零无符号二进制偶整数后面的两个0相当于将该数除以4。 7. 互联网设备:Hub是集线器,属于网络连接设备,用于多台计算机间的信号汇总与分配。 8. 数学运算:`Int(198.555*100+0.5)/...

    STM32L496接入微软 Azure 物联网平台【RT-Thread工程,支持STM32L4系列单片机】.zip

    在RT-Thread中,你可以创建任务、信号量、消息队列等调度对象,并使用其内建的TCP/IP网络栈,实现与Azure IoT Hub的通信。 为了连接Azure IoT,你需要了解Azure IoT SDK,这是一组库,允许设备与Azure IoT Hub交互...

    ss2s_hub

    1. **文件I/O操作**:C语言提供了标准库中的`fopen`, `fwrite`, `fread`, `fclose`等函数来处理文件的读写,如果“ss2s_hub”与文件系统交互,这部分会很关键。 2. **网络编程**:如果项目涉及网络通信,可能会使用...

    2021-2022计算机二级等级考试试题及答案No.3063.docx

    15. 第17题,Python函数调用中,`hub(ss, 3)`改变了局部变量`ss`,但不会影响全局变量`ss`,因此输出`10 None`。 16. 第18题,循环队列的元素个数可能为0(队列为空)或35(满队列,front和rear重合)。 17. 第19...

    USB学习:GD32USB虚拟多串口(F470端点不够)

    4. **软件调度**:通过软件层面的优化,如队列管理和优先级控制,平衡不同串口的访问需求。 在提供的文件列表中,我们可以看到以下关键文件: 1. **app.c**:这是应用程序的主要源代码文件,其中包含了实现USB CDC...

    VxWorks下USB驱程栈框架模型及其应用研究.pdf

    例如,USB HUB是USB网络中的中心连接点,驱动程序负责实现对这些硬件设备的功能操作,包括HUB驱动器(HUB_Driver)和HUB事件处理(USB_HUB_Event)。 在研究VxWorks下USB驱动栈框架模型及其应用时,涉及到的关键词...

Global site tag (gtag.js) - Google Analytics