金城武说:不知道从什么时候开始,在什么东西上面都有个日期,秋刀鱼会过期,肉罐头会过期,连保鲜纸都会过期,我开始怀疑,在这个世界上,还有什么东西是不会过期的?

有时候我也被这个问题所困扰,我不知道是我不明白还是这世界变化太快.连Linux中都引入了过期这么一个概念.说文雅一点就是超时.设置一个时间,如果时间到了该做的事情还没有做完,那么某些事情就会发生.

比如,咱们需要做这样一些事情,定个闹钟,比如咱们需要烤蛋糕,现在是8点30,而咱们要烤45分钟,所以咱们希望闹钟9点一刻响,然后当时间到了,闹钟就如咱们期待的一样,响个不停.在计算机中,咱们也需要做这样的事情,有些事情,咱们需要时间控制,特别是网络,通信,等等,凡是涉及数据传输的事儿,就得考虑超时,换句话说,定个闹钟,你要是在这个给定的时间里还没做好你该做的事情,那么停下来,别做了,肯定有问题,比如,咱们如果烤蛋糕45分钟,发现蛋糕一点香味都没有,颜色也没变,那肯定有问题,别烤了,先检查一下烤箱是不是坏了,是不是停电了,等等.而具体到咱们这里,需要用一个闹钟,或者叫专业一点,定时器,如果时间到了,就执行某个函数,这个功能Linux内核的时间机制已经实现了,咱们只需要按"说明书"调用相应的接口函数即可.看代码,190行,如果timeout>0,也就是说需要设置闹钟,那么首先需要定义一个struct timer_list结构体的变量,咱们这里定义的变量叫做to_timer(在usb_stor_msg_common一开始就定义了的),然后用init_timer()函数和add_timer()函数来真正实现设置闹钟,init_timer()是初始化,然后设置好之后调用add_timer才能让闹钟生效.具体怎么设置的呢?在add_timer()之前,为to_timer.expires赋值为jiffies+timeout,to_timer.function赋值为timeout_handler,to_timer.data赋值为us.这表示,超时时间点为当前时间加上一个timeout,(jiffies,Linux内核中赫赫有名的全局变量,表示当前时间),timeout咱们前面调用usb_stor_msg_common的时候给设置成了HZ,也就是1秒.当时间到了之后,timeout_handler函数会被执行,而us作为参数传递给她.不妨来看一下timeout_handler函数吧,她定义于drivers/usb/storage/transport.c中:

119 /* This is the timeout handler which will cancel an URB when its timeout
120 * expires.
121 */
122 static void timeout_handler(unsigned long us_)
123 {
124 struct us_data *us = (struct us_data *) us_;
125
126 if (test_and_clear_bit(US_FLIDX_URB_ACTIVE, &us->flags)) {
127 US_DEBUGP("Timeout -- cancelling URB/n");
128 usb_unlink_urb(us->current_urb);
129 }
130 }
看得出,其实也没做什么,就是清除US_FLIDX_URB_ACTIVE flag,然后调用usb_unlink_urb()函数撤销当前这个urb.还记得刚才说的那个同步异步了吗?这正是刚才说的那个异步的情形,显然此刻这个函数不能睡眠,否则整个driver就挂了...

紧接着,199行,非常重要的一句wait_for_completion(&urb_done),这句话会使本进程进入睡眠.别忘了刚才我们那句init_completion(&urb_done),urb_done是一个struct completion结构体变量,这个定义在usb_stor_msg_common()函数的第一行就出现了.显然completion是Linux中同步机制的一个很重要的结构体.与wait_for_completion对应的一个函数是complete().其用法和作用是这样的:首先我们要用init_completion初始化一个struct completion的结构体变量,然后调用wait_for_completion()这样当前进程就会进入睡眠,处于一种等待状态,而另一个进程可能会去做某事,当它做完了某件事情之后,它会调用complete()函数,一旦它调用这个complete函数,那么刚才睡眠的这个进程就会被唤醒.这样就实现了一种同步机制,或者叫等待机制.那么我们来看complete函数在哪里被调用的,换句话说,咱们这里一旦睡去,何时才能醒来.

还记得在调用usb_fill_control_urb()填充urb的时候咱们设置了一个urb->complete指针吗?没错,当时咱们就看到了,urb->complete=usb_stor_blocking_completion,这相当于向usb host controller driver传达了一个信息.所以,当urb传输完成了之后,usb host controller会唤醒她,但不会直接唤醒她,而是通过执行之前设定的urb的complete函数指针所指向的函数,即调用usb_stor_blocking_completion()函数去真正唤醒她.usb_stor_blocking_completion()函数定义于drivers/usb/storage/transport.c中:

109 /* This is the completion handler which will wake us up when an URB
110 * completes.
111 */
112 static void usb_stor_blocking_completion(struct urb *urb, struct pt_regs *regs)
113 {
114 struct completion *urb_done_ptr = (struct completion *)urb->context;
115
116 complete(urb_done_ptr);
117 }
这个函数就两句话,但她调用了complete()函数,urb_done_ptr就被赋为urb->context,而urb->context是什么? usb_stor_msg_common()函数中,152行,可不就是把刚初始化好的urb_done赋给了它么?这个函数可是Linux内核的核心函数,不要问她从哪里来,她会告诉你她来自内核底层,没错,她的户口在kernel/sched.c,很显然,她就是唤醒刚才睡眠的那个进程.换言之,到这,wait_for_completion()将醒来,从而继续往下走.

如果你足够好奇,你会问如果超时,那么timeout_handler会被调用,于是usb_unlink_urb会被调用,然后呢?其实usb_stor_blocking_completion还是会被调用,而且会设置urb->status以告诉大家这个urb被cancel了.

下面只剩下几行代码了.首先是clear_bit()清除US_FLIDX_URB_ACTIVE,表明这个urb不再是active了.因为该干的事都干完了,就好比您的包裹已经寄到了,那显然您填的那个单子就没有用了.至少她上面应该有标志表明这份单子对应的包裹已经送过了,不要再送了.如果是超时了,那么也是一样的,urb都被cancel了,当然就不用设为active了.

然后下一行,如果这时timeout还大于0,那么说明刚才您设的那个超时闹钟还没到过期,而您该做的事情却已经做完了,所以这个闹钟就不需要设了,就好比邮局承诺您三天寄到,完了您记住了,三天她要没寄到,您就去索赔,所以您自个儿就订了个闹钟,三天真到期了您就可以去索赔,但是如果人家两天就给您寄到了,那您这个闹钟就没意义了嘛不是,所以这样您就得取消这个闹钟,省得她那弦老紧绷着,这里您也得删除刚才那个to_timer,这样您可以调用Linux内核为您提供的函数del_timer_sync(),她的参数就是刚才这个to_timer的地址.最后一句,usb_stor_msg_common()函数终于该返回了,return us->current_urb->status,返回的就是urb的status.于是我们总算是可以离开这个函数了.

返回之后,又回到usb_stor_control_msg()中来,如果status是0,那么说明成功传输了,对于成功传输的情况,urb的actual_length将被赋值为实际传输长度,然后usb_stor_control_msg()也返回了,要么是实际长度,要么就是不成功的具体status.因此我们也不得不离开这个函数.诚然,快乐要有悲伤作陪,雨过应该就有天晴.但是如果雨后还是雨,如果忧伤之后还是忧伤.请让我们从容面对这离别之后的离别.

于是,走过了千山万水,经历了千辛万苦,咱们再一次回到了久违的usb_stor_Bulk_max_lun()函数.这样一次真正的控制传输就这么开始就这么结束了.

接下来,我们继续看,控制传输的结果返回给了result,我们说过,单纯的U盘一般来说这个结果总是0,即它必然只有一个lun.这里判断的是result大于0,道理很简单,result是一个int型的数据,而返回的给它的实际上是iobuf[0]这么一个char类型的变量,所以是字符’0’,保存成int型当然就大于0了,所以这里打印出来结果是0,但是result实际上是大于0的.而945行我们同样注意到,如果result是一些奇怪的值,正如注释所说,有的设备它就不认GetMaxLUN这个命令,就像在她的心里潜伏着一个深渊,扔下巨石也发不出声音来,或者它干脆就返回一个0长度的结果来,那么这种情况那么我们就只能把这种设备当作只有一个LUN了,所以也就返回0得了.

不过933到935这三行需要说一下.这也是专门为一些变态的设备准备的.一般的设备用不着.只是usb_stor_clear_halt这个函数是我们自己定义的,并且今后我们也会用到,所以我们还是讲一下.不过,在这个函数中,我们将再一次见到控制传输,但是毕竟不再是我们的第一次亲密接触了,所以,虽然我们依然还是在usb_stor_Bulk_max_lun中,但还是让我们下一节在讲吧.

最后需要解释一下的是,像init_timer(),add_timer(),del_timer_sync()这几个函数都是Linux内核中的核心函数,包括结构体struct timer_list,他们都来自include/linux/timer.h和kernel/timer.c中,我们只需要知道调用就可以了,不用知道究竟怎么实现的,只需要知道这样设置了超时的话,我们注册的超时处理函数就会被执行.而至于它究竟如何去调用的,如何计时如何去判断超时这些内核自会处理,不用我们担心,我们瞎操心也没用.对于内核来说,时间是怎样划破她的皮肤,只有她自己最清楚.而对于写设备驱动的人来说,这些核心代码就像是天空中的云朵,你看着它往某一个方向飘,却什么也做不了.正如令狐冲所说的那样:”有些事情本身我们无法控制,只好控制自己.”

Linux那些事儿之我是U盘(28)第一次亲密接触(四)相关推荐

  1. linux 那些事儿之我是 u 盘,《Linux那些事儿之我是USB》.PDF

    <Linux 那些事儿之我是 USB> 作者:华清远见 第 1 章 Linux 那些事儿之我是 USB Core 专业始于专注 卓识源于远见 1 .引子 老夫子们痛心疾首地总结说,现代青年 ...

  2. Linux那些事儿之我是U盘(5)外面的世界很精彩

    看代码之前,我曾经认真的思考过这么一个问题,我需要关注的仅仅是drivers/usb/storage/目录下面那相关的3000多行代码吗?就是这样几个文件就能让一个个不同的U盘在Linux下面工作起来 ...

  3. Linux那些事儿之我是U盘(4)想到达明天,现在就要启程

    既然知道了怎么编写一个模块,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块的人都不会觉得写设备驱动有困难.对自己行不行不确定的话,可以去问一下葛优,他准说:"(神州行),我看行. ...

  4. 转载本论坛 (fudan_abc ) :linux那些事儿之我是u盘(16)冰冻三尺非一日之寒

    不是一天建成的 . 在让 U 盘工作之前 , 其实我们的驱动作了很多准备工作 . 我们继续跟着感觉走,storage_probe(),943行至948行,一系列的以init_*命名的函数在此刻被调用, ...

  5. Linux那些事儿之我是U盘(1)小城故事

    这个故事中使用的是2.6.10的内核代码.Linux内核代码目录中, 所有去设备驱动程序有关的代码都在drivers/目录下面,在这个目录中我们用ls命令可以看到很多子目录. localhost:/u ...

  6. 【转】Linux那些事儿之我是U盘(4)想到达明天,现在就要启程

    既然知道了怎么编写一个模块,那么编写设备驱动程序自然也就不难了.我相信,每一个会写模块的人都不会觉得写设备驱动有困难.对自己行不行不确定的话,可以去问一下葛优,他准说:"(神州行),我看行. ...

  7. Linux那些事儿之我是U盘--引子

    也许是在复旦养成了昼伏夜出的坏习惯,工作之后也总是很晚也不愿意睡.来到北京之后,开始听广播听都市之声的北京不眠夜.这个节目是从23点直到第二天凌晨一点,我常常是听完了才会睡觉.无论是北京还是上海,对我 ...

  8. Linux那些事儿之我是U盘(50)跟着感觉走(二)

    回到usb_stor_invoke_transport()中来,540行,还是老套路,又问是不是命令被放弃了,放弃了当然下面的就别执行了.goto Handle_Abort去. 546行,如果有错误, ...

  9. Linux那些事儿之我是U盘(51)光荣属于苹果,属于诺基亚,属于摩托罗拉,属于索尼爱立信

    这一节我们来分析一个在很多企业的产品中都存在的bug.写设备驱动是一件很实在的事情,你得根据实实在在的硬件来编写你的代码,如果你的硬件存在某种bug,那么你就要去fix它.如果你希望成为通用的驱动程序 ...

  10. Linux那些事儿之我是U盘(29)将控制传输进行到底

    其实usb_stor_clear_halt这个函数的作用很简单,就是spec里边规定了,usb设备中,有两类端点,必须具有一个叫做Halt的特征,啥是Halt?查金山词霸去,中断,停止,暂停,怎么解释 ...

最新文章

  1. python使用matplotlib可视化线图(line plot)、使用invert_yaxis函数将Y轴坐标反序(invert the y axis in matplotlib)
  2. android sdk启动不了,windows server 2008下android sdk不能正常启动
  3. 如何在hadoop中控制map的个数
  4. 基于linux 的2048
  5. SQL SERVER备份脚本
  6. 排队接水(信息学奥赛一本通-T1319)
  7. dubbo通信协议之对比
  8. OSChina 周日乱弹 ——不穿泳衣,我都不好意思上厕所
  9. 试读《JavaScript语言精粹(修订版)》
  10. oracle sql语句_7个维度查看oracle执行计划的sql语句执行效率
  11. Django+xadmin的安装与配置
  12. POI导出换行和水平居中
  13. win10 cmd窗口切换目录并运行python代码
  14. 天津麒麟签约“京津冀大数据协同处理中心”
  15. 为什么存png还有白色底_用photoshop保存透明背景的图片为png格式,为什么打开后是白色背景了?...
  16. 首批!工信部下达2021年国家工业专项节能监察任务,涉及270 个数据中心(附名单)...
  17. Android笔记之(图片高斯+Glide实现微信图片加载策略+仿微信进度条)
  18. 词袋模型和词向量模型
  19. 会声会影2022智能、快速、简单的视频剪辑软件
  20. 7、基于51单片机智能热水器温度水温水位检测控制系统设计

热门文章

  1. 微信小助手插件WeChatTweak
  2. Learn C++学习笔记:第M章—最常用的智能指针:std::unique_ptr std::make_unique
  3. Vscode 快速打开setting.json
  4. HTML的relative与absolute区别
  5. 中国人想在外国卖东西,这些经典网站不能错过
  6. Windows下智能卡开发
  7. 为何要使用加密邮箱?
  8. word打开很慢解决办法
  9. 邮件服务器正常工作亮几个灯,光纤猫正常亮几个灯 光纤猫的灯都代表意思是什么【详解】...
  10. Linux基础课程汇总-辛舒展-专题视频课程