我们刚刚跟着storage_probe()几乎完整地走了一遍,貌似一切都该结束了,可是你不觉得你到目前为止还根本没有看明白设备究竟怎么工作的吗?U盘,不仅仅是USB设备,还是“盘”,它还需遵守USB Mass Storage协议,以及Transparent SCSI规范。从驱动程序的角度来看,它和一般的SCSI磁盘差不多。正是因为如此,所以U盘的工作真正需要的是四个模块,usbcore,scsi_mod,sd_mod,以及咱们这里的usb-storage,其中sd_mod恰恰就是SCSI硬盘的驱动程序。没有它,你的SCSI硬盘就别想在Linux下面转起来。
那么我们从哪里开始去接触这些SCSI命令呢?别忘了我们现在的主题,内核守护进程,别忘了我们曾经有一段代码只讲到一半就没讲了。没错,那就是usb_stor_control_thread(),当初我们用kthread_create创建它时就说了,从此以后一个进程变成两个进程。而我们刚才沿着storage_probe讲完的是父进程,父进程最终返回了,而子进程则没有那么简单,我们已经说过,usb_stor_control_thread()中的死循环注定了这个子进程是一个永恒的进程,只要这个模块还没有被卸载,或者说还没有被要求卸载,这个子进程就必将永垂不朽地战斗下去。于是让我们推开记忆的门,回过来看这个函数,当初我们讲到了308行,由于us->sema一开始就是锁着的,所以down_interruptible这里一开始就进入睡眠了,只有在接到唤醒的信号或者锁被释放了释放锁的进程来唤醒它,它才会醒过来。那么谁来释放这把锁呢?
有两个地方,一个是这个模块要卸载了,这个我们稍后来看。另一个就是有SCSI命令发过来了。SCSI命令从哪里发过来?很简单,SCSI核心层,硬件上来说,SCSI命令就是SCSI主机到SCSI设备,而从代码的角度来说,SCSI核心层要求为每一个SCSI主机实现一个queuecommand命令,每一次应用层发送来了SCSI命令了,比如你去读写/dev/sda,最终SCSI核心层就会调用与该主机相对应queuecommand,(确切地说是structScsi_Host结构体中的成员structscsi_host_template中的成员指针queuecommand,这是一个函数指针。)那么我们来看,当初我们定义的struct
scsi_host_template usb_stor_host_template,其中的确有一个queuecommand,我们赋给它的值也是queuecommand,即我们让queuecommand指向一个叫做queuecommand的函数,在struct scsi_host_template的定义中,函数指针的原型来自include/scsi/scsi_host.h:
124 int (*queuecommand)(struct scsi_cmnd *,
125 void (*done)(struct scsi_cmnd *));
而我们所定义的queuecommand()函数又在哪里呢?在drivers/usb/storage/scsiglue.c中:
208 /* queue a command */
209 /* This is always called with scsi_lock(host)held */
210 static int queuecommand(struct scsi_cmnd *srb,
211void (*done)(struct scsi_cmnd *))
212 {
213 struct us_data *us =host_to_us(srb->device->host);
214
215 US_DEBUGP("%scalled\n", __FUNCTION__);
216
217 /*check for state-transition errors */
218 if (us->srb != NULL) {
219 printk(KERN_ERR USB_STORAGE "Error in %s: us->srb =%p\n",
220__FUNCTION__, us->srb);
221 return SCSI_MLQUEUE_HOST_BUSY;
222 }
223
224 /* fail the command if we aredisconnecting */
225 if(test_bit(US_FLIDX_DISCONNECTING, &us->flags)) {
226 US_DEBUGP("Fail command during disconnect\n");
227 srb->result = DID_NO_CONNECT << 16;
228 done(srb);
229 return 0;
230 }
231
232 /* enqueuethe command and wake up the control thread */
233 srb->scsi_done= done;
234 us->srb = srb;
235 up(&(us->sema));
236
237 return 0;
238 }
这个函数不长,它的使命也很简单,就是为了唤醒那个沉睡中的守护进程,告诉它不能再沉睡。
我们来仔细看一下,213行,us的身影无处不在。
218行,判断us->srb,事到如今,我们不得不去面对一个新的数据结构,它就是struct scsi_cmnd。queuecommand()函数的第1个参数就是structscsi_cmnd指针,而structus_data中也有一个structscsi_cmnd *srb,也是一个指针。
那么我们来看structscsi_cmnd,这个数据结构的意义很明显,就是代表一个SCSI命令。它定义于include/scsi/scsi_cmnd.h中。如果你感兴趣可以去看一下,我们只要知道有这么一个数据结构就可以了,同时需要知道在us中有一个成员srb,由它来指向scsi命令。
继续说218行,看一下us->srb是不是为空,如果为空我们才可以继续往下走去唤醒那个守护进程,否则就说明之前的一个命令还没有执行完。它会执行完一个命令就会把它设为空。显然us->srb为空,因为还没有任何人为它赋过值,只是初始化us时把所有元素都初始化为0了,所以这第一次来到这里时肯定是空。这里如果不为空就返回SCSI_MLQUEUE_HOST_BUSY给SCSI Core,这样核心层就知道,这边主机正忙着呢,先不急着执行下面的命令。
225行, US_FLIDX_DISCONNECTING这个flag我们已经不是第一次遇见了,无须多讲,现在只是不知道究竟是哪里设置了这个flag,日后我们看到storage_disconnect就知道了。这里和以往一样,如果这个flag设置了,赶紧结束吧。设置srb->result让scsi core知道这里已经断开连接了。而queuecommand命令本身就返回0。不过我们需要注意的是228行这个done函数,仔细看这个done是queuecommand()函数的第2个参数,是一个函数指针,实际上scsi
core调用queuecommand时传递的参数名字就叫做scsi_done,这就是一个函数名,SCSI核心层定义了一个叫做scsi_done的函数。SCSI核心层要求当低层的驱动程序完成一个命令后要调用这个函数去通知SCSI核心层,这实际上就相当于一种中断机制。scsi核心层调用了queuecommand()之后它就不管事了,它就去干别的了,等底层的代码把这个queuecommand执行完了之后,或者准确地说当底层把命令执行完了之后,就调用scsi_done从而scsi核心层就知道这个命令完成了,然后它就会接着做一些它该做的事情比如清理这个命令,或者别的一些收尾的工作。
所以这里我们看到,如果设备已经设置了断开的flag,那么这里就执行done,如果没有断开那就在下面的233行设置srb->scsi_done等于这个done,实际上就是等于scsi_done,这两个scsi_done,一个是struct scsi_cmnd *srb的成员指针,一个是SCSI核心层的函数名。虽然它们同名,但是是两个不同的东西。
最后,234行,令us->srb等于这个srb。而235行,这正是我们苦苦寻找的代码,正是这个up(&us->sema),唤醒了我们的守护进程。之后,237行,这个函数本身就结束了。而我们显然就该去看那个usb_stor_control_thread()了。因为,它醒了,它终于醒了。
分享到:
相关推荐
### Linux那些事儿之我是USB(第2版)关键知识点概览 #### 一、书籍概述 - **核心主题**:本书主要围绕Linux内核中的USB子系统展开,深入剖析其工作原理和技术细节。 - **目标读者**:面向Linux初学者、驱动开发者...
本压缩包文件"linux那些事儿之我是USB.zip"包含了深入理解Linux USB驱动及内核相关知识的九个文档,包括Block层、EHCI主机控制器、HUB、PCI、SCSI硬盘、Sysfs、UHCI、USB core以及U盘。这些文档旨在提供一个系统性的...
### Linux那些事儿之我是USB —— USB技术在Linux下的实现与探索 #### 引言 在探讨《Linux那些事儿之我是USB》这篇文章之前,我们先简单回顾一下文章的背景及主要内容。该文由一位自称对Linux并无太多好感的作者...
Linux那些事儿之我是U盘 Linux那些事儿之我是Hub Linux那些事儿之我是USB Core Linux那些事儿之我是UHCI Linux那些事儿之我是EHCI控制器 Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是...
Linux那些事儿之我是U盘-有书签版.pdf
在《Linux那些事儿之我是U盘》这篇诙谐幽默的技术文章中,作者以独特的视角介绍了Linux内核2.6版本中USB驱动的相关知识。通过轻松的叙述方式,让读者在轻松愉快的氛围中理解复杂的USB驱动原理及其在Linux系统中的...
读过《linux那些事儿之我是U盘》的人,都知道其风格,我就不多说了。 导读: linux那些事儿之我是U盘 linux那些事儿之我是HUB linux那些事儿之我是USB Core linux那些事儿之我是UHCI Linux那些事儿之我是EHCI主机控制...
根据给定的信息,“Linux那些事儿之我是U盘”这一标题及描述主要聚焦于USB技术在Linux环境下的工作原理和技术细节。接下来,我们将基于这个主题展开深入探讨,涵盖USB技术的基本概念、USB在Linux系统中的实现机制...
3. **Linux那些事儿之我是U盘.pdf**: USB闪存驱动器,通常称为U盘,是常见的便携式存储设备。这部分可能讲述Linux如何识别、挂载和管理U盘,包括使用ums(USB Mass Storage)驱动程序,以及如何处理文件系统的读写...
导读.doc Linux那些事儿之我是Block层.pdf Linux那些事儿之我是EHCI主机控制器.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是USB_core.pdf Linux那些事儿之我是U盘.pdf等等 Linux那些事儿系列全在这里了
》包括《Linux那些事儿之我是Hub》、《Linux那些事儿之我是Sysfs》《Linux那些事儿之我是UHCI》、《Linux那些事儿之我是USB core》、《Linux那些事儿之我是U盘》,令人叹为观止的一个linux系列书籍。只能说,江山代...
“Linux那些事儿之我是U盘.pdf”将关注通用串行总线(USB)闪存驱动器。这部分会解释Linux如何识别和处理USB闪存设备,包括文件系统的挂载和数据交换过程。 “Linux那些事儿之我是USB Core.pdf”涉及Linux内核的USB...
Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Hub Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs ...Linux那些事儿之我是U盘
"Linux那些事儿之我是XXX全集"这个资源集合,旨在深入剖析Linux内核中的关键组件,特别是与USB(通用串行总线)相关的部分,包括USB core、U盘、UHCI、PCI、SCSI硬盘、Block层和Hub等核心概念。这些文件将通过源代码...
### Linux与USB存储设备——《Linux那些事儿之我是U盘》知识点提炼 #### 引言 本文基于一篇幽默且深入的教程《Linux那些事儿之我是U盘》,该文章通过作者的亲身经历和深入浅出的讲解,介绍了Linux操作系统中USB存储...