Linux那些事儿之我是Hub(13)树,是什么样的树?

同学们,今天我们来讲一棵树.

记得小时候我们看<<白眉大侠>>,记得那段精彩的对白:,是什么样的刀?金丝大环刀!,是什么样的剑?闭月羞光剑!,是什么样的招?天地阴阳招!,是什么样的人?飞檐走壁的人!,是什么样的情?美女爱英雄!

而今天我们要问的是:,是什么样的树?:USB设备树.这是怎样一棵树?让我慢慢的道来.

苏格拉底曾经说过:为人不识谭浩强,精通内核也枉然.

还记得谭浩强大哥书中那个汉诺塔的例子么?那时候我天真的以为这个例子没什么意义,可是今天我终于明白了.谭大哥早就知道我们学了他的书一定会在今后的生活工作中用得到,这不,hub_events()里面第2645,locktree(),用的就是汉诺塔里的那个经典思想,递归.谭大哥您要是穿裙子的话,我一定第一个拜倒在您的石榴裙下!locktree()定义于drivers/usb/core/hub.c:

    985 /* grab device/port lock, returning index of that port (zero based).

    986  * protects the upstream link used by this device from concurrent

    987  * tree operations like suspend, resume, reset, and disconnect, which

    988  * apply to everything downstream of a given port.

    989  */

    990 static int locktree(struct usb_device *udev)

    991 {

    992         int                     t;

    993         struct usb_device       *hdev;

    994

    995         if (!udev)

    996                 return -ENODEV;

    997

    998         /* root hub is always the first lock in the series */

    999         hdev = udev->parent;

   1000         if (!hdev) {

   1001                 usb_lock_device(udev);

   1002                 return 0;

   1003         }

   1004

   1005         /* on the path from root to us, lock everything from

   1006          * top down, dropping parent locks when not needed

   1007          */

   1008         t = locktree(hdev);

   1009         if (t < 0)

   1010                 return t;

   1011

   1012         /* everything is fail-fast once disconnect

   1013          * processing starts

   1014          */

   1015         if (udev->state == USB_STATE_NOTATTACHED) {

   1016                 usb_unlock_device(hdev);

   1017                 return -ENODEV;

   1018         }

   1019

   1020         /* when everyone grabs locks top->bottom,

   1021          * non-overlapping work may be concurrent

   1022          */

   1023         usb_lock_device(udev);

   1024         usb_unlock_device(hdev);

   1025         return udev->portnum;

   1026 }

咱们传递进来的是咱们这个hub对应的struct usb_device指针,995行自然不必说,就是防止那些唯恐天下不乱的人调用这个函数.

999,parent,struct usb_device结构体的parent自然也是一个struct usb_device指针.1000,判断udevparent指针,你一定觉得奇怪,好像之前从来没有看到过parent指针啊,为何它突然之间出现了?它指向什么呀?对此我只能说抱歉,其实生活中发生的一切,都是那么的突然.其实我也没有办法,Hub驱动作为一个驱动程序,它并非是孤立存在的,没有主机控制器的驱动,没有usb core,Hub驱动的存在将没有任何意义.其实我以前就说过,Hub,准确的说应该是说,Root Hub,它和Host Controller是绑定在一起的,专业一点说叫做集成在一起的,所以它根本不会像我一样除了拥有孤独其它的一无所有.有时候我真的想知道,当全世界孤独的人团结在一起,孤独是会加深,还是消失

因为Hub驱动不孤立,所以具体来说,作为Root Hub,它的parent指针在Host Controller的驱动程序中就已经赋了值,这个值就是NULL,换句话说,对于Root Hub,它不需要再有父指针了,这个父指针本来就是给从Root Hub连出来的节点用的,让我们打开天窗说亮话,这里这个函数名字叫做locktree,这个意思就很直接了,locktree,顾名思义,锁住一棵树,这棵树就是USB设备树.很显然,USB设备是从Root Hub开始,一个一个往外面连的,比如Root Hub4个口,每个口连一个USB设备,比如其中有一个是Hub,那么这个Hub有可以继续有多个口,于是一级一级的往下连,最终肯定会连成一棵树,八卦两句,自从某年某月某一天,我家Intel提出了EHCI的规范以来,当今USB世界的发展趋势是,硬件厂商们总是让EHCI主机控制器里面拥有尽可能多的端口,换言之,理想情况就是希望大家别再用外接的Hub,有一个Root Hub就够用了,也就是说,真的到了那种情况,USB设备树的就不太像树了,顶多就是两级,一级是Root Hub,下一级就是普通设备,严格来说,对于咱们普通人来说,这样子也就够用了,假设你的Root Hub8个口,你说你够用不够用?鼠标,键盘,音响,U,存储卡,还有啥啊?8个口对正常人来说肯定够了,当然你要是心理变态那么另当别论,说不准就有人愿意往一台电脑里插入个三五十个USB设备的,林子大了,什么鸟没有?

所以说写代码也不是一件容易的事情,除了保证你的代码能让正常人正常使用,还得保证变态也能使用.locktree()的想法就是这样,hub_events()里面加入locktree()的理由很简单,如果你的电脑里有两个hub,一个叫parent一个叫child,child接在parent的某个口上,那么parent在执行下面这段代码的时候,child hub就不要去执行这段代码,否则会引起混乱,为何会引起混乱?要知道,对于一个hub来说,其所有正常的工作都是在hub_events()这个函数里进行的,那么这些工作就比如,一种情况是,删除一个子设备, 这将有可能直接导致USB设备树的拓扑结构发生变化,或者另一种情况,遍历整个子树去执行一个resume或者reset之类的操作,那么很显然,在这种情况下,一个parent hub在进行这些操作的时候,不希望受到child hub的影响,所以在这样一个政治背景下,2004年的夏天,作为Linux内核开发中USB子系统的三剑客之一的David Brownell大侠,决定加入locktree这么一个函数,这个函数的哲学思想很简单,实际上就是借用了我国古代军事思想中的擒贼仙擒王,David Brownell本人的话说,就是”lock parent first”.每一个Hub在执行hub_events()中下面的那些代码时,(特指locktree那个括号以下的那些代码.)都得获得一把锁,锁住自己,而在锁住自己之前,又先得获得父亲的锁,确切的说,是尝试获得父亲的锁,如果能够获得父亲的锁,那么说明父亲当前没有执行hub_events()(因为否则就没有办法获得父亲的锁),那么这种情况下子hub才可以执行自己的hub_events(),但是需要注意,在执行自己的代码之前,先把父hub的锁给释放掉,因为我们说了,我们的目的是尝试获得父亲的锁,这个尝试的目的是为了保证在我们执行hub_events()之前的那一时刻,hub并不是正在执行hub_events(),而至于我们已经开始执行了hub_events(),我们就不在乎父hub是否也想开始执行hub_events().

那么有人问,hub复父hub,hub何其多?刚才不是说了吗?Root Hub就是整棵树的根,Root Hub就没有父hub,所以,整个递归到了父Hub就可以终止了.这也正是为什么1000行那句if语句,在判断出该设备是一个Root Hub之后,马上就执行锁住该设备.而如果不是Root Hub,那么继续往下走,递归调用locktree(),对于locktree(),正常情况下它的返回值大于等于0,所以小于0就算出错了.

然后1015行判断一下,如果我们把父hub锁住了,可是自己却被断开了,disconnect函数被执行了,那么就立刻停止,把父hub的锁释放,然后返回吧错误代码-ENODEV.

最后1023,锁住自己,1024,释放父设备.

1025,返回当前设备的portnum.portnum就是端口号,您一定奇怪,没见过什么时候为portnum赋值了啊?这就不好意思了,别忘了咱们走到今天这里是在讨论Root Hub,对于Root Hub来说,它本身没有portnum这么一个概念,因为它不插在别的Hub的任何一个口上.所以对于Root Hub来说,它的portnumHost Controller的驱动程序里给设置成了0.而对于普通的Hub,它的portnum在哪里赋的值呢?我们后面就会看到的.别急.不过友情提醒一下,对于Root Hub来说,这里根本就不会执行到1025行来,刚才说了,对于Root Hub,实际上1002行那里就返回了,而且返回值就是0.

就这样,我们看完了locktree()这个函数,接下来我们又该返回到hub_events()里面去了.再次爆料一下,2004,当时的内核还只是2.6.8,David Brownell大侠在那个夏天提出来locktree()的时候,遭到了另一位同是三剑客之一的Alan Stern大侠强烈反对,当时Alan Stern认为David Brownell的算法并不好,没能真正解决并发事件的相互干扰问题,Alan Stern自己有他的算法,于是两个人各自坚持自己的意见,在开源社区轰轰烈烈争论了N,差点没打起来.那个夏天这两个人从730日一直吵到了818,要不是815日我在珠江电影制片厂外景地看张柏芝拍大运摩托的那个广告,我说不准就会上去劝架,不过像我这样的人去劝架估计人家也不会理睬,人家会问:你丫谁呀?

最终locktree()被加入到了内核里面,而且不止加了一处,除了加入到了hub_events()之外,在另外两个函数usb_suspend_device()usb_resume_device()里面也有调用locktree().有趣的是,2.6.9一直到2.6.22.1的内核,我们都能看到locktree()这么一个函数,但是后来,usb_suspend_device/usb_resume_device中没有了这个函数,就比如我们现在看到的2.6.22.1,搜索整个内核,只有hub_events()这一个地方调用了locktree(),对此,Alan Stern给出的说法是,内核中关于USB挂起的支持有了新的改进,不需要再调用locktree.但是从2.6.23的内核开始,估计整个内核中就不会再有locktree,Alan Stern大侠在这个夏天,提交了一个patch,最终把locktree相关的代码全部从内核中移除了.如果您对锁机制特别感兴趣,那么研究一下Linux 2.6以来Hub驱动的历史发展,这个过程锁机制的变更,或者专业一点说,叫同步机制算法的不断改进,足以让你写一篇能在国内核心刊物发表的文章来,要知道首都有一所叫做清华的名牌大学,其硕士研究生也就是一篇国内核心就可以毕业了.

 
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值