上一篇写了些关于SD Driver的东西,在卡被系统识别到之后最后会加载文件系统。这部分工作是由CE下的Storage Manager来完成的,在系统启动时会加载Filesys.exe,这个会提供Storager Manager的功能。在Storage Manager的初始化阶段会创建一个Pnp的线程来监测有无Pnp的设备插入或移出。当SD卡插入后,系统监测到之后会发送一个Msg出来,这个Msg包括设备的名称和GUID。那么Pnp的线程等到这个Msg后开始为刚插入的Block设备加载文件系统。函数MountStore用来加载。
在这里CE设计了一个类CStore来表述这个块设备,在创建一个实例的时候会执行构造函数来完成部分成员的初始化。Storage Manager作为一个管理器的角色自然要对系统中各个存储设备进行管理,StorageManager会构造一个链表,然后将前面创建的Store的指针插入这个链表。由于之前块设备的Block Driver已经由Device.exe也就是设备管理器加载过了,因此已经存在设备文件,那么存储管理器就在OpenDisk函数内使用CreateFileW来获取设备句柄,这个句柄也是CStore的一个成员变量。之后利用该句柄调用DeviceIoControl来获取Block驱动的部分信息,这里比较重要的是Profile对应的字串。有了这个就在接下来的GetStorageMessage(函数名忘记是不是这个了)中获取注册表中关于该块设备的信息。包括Partition Driver和该设备需要加载的文件系统等。
在获取了Partition Driver之后(一般都是使用MS的mspart.dll)接着就涉及到Partition部分了,在这个部分会创建Partition的类,该类的成员中包括一系列的函数指针,接着调用LoadPartitionDriver函数,该函数是把mspart.dll中的函数地址赋给Partition类中的函数指针。有了这些函数的具体实现就可以加载分区驱动了。分区驱动的入口应该是PD_OpenStore函数,在这个函数中利用设备句柄和DeviceIoControl来读取MBR,通过这部分信息来创建Partition的结构(这里不是Partition的类),有几个分区就有几个对应的结构,并用链表相连,表头赋给CStore的一个成员(没有记错的话应该是dwStoreId吧),而Partition类的指针也赋给CStoer的成员,好像叫做m_pPartitionList,有了这些之后再调用Partition驱动中LoadPartition的函数来加载分区,分区加载上之后就要加载该分区上的文件系统了,我们使用FAT文件系统。
在分区加载之后加载文件系统,在这里涉及到一个函数InitEx,在这个函数中来完成对于文件系统的加载。该函数中会去创建两个结构:FSD和DSK。同时该函数的返回值m_pDsk是类Partition的成员。DSK结构中有指向FSD的指针,也有指向_DSK的指针,同时_DSK有指向Volume的结构。
由以上可以看出,实际存储管理维护了几个类和结构:CStore,CPartition,FSD,DSK和Volume等。文件系统加载会调用一个RegisterVolume函数,在注册成功后就可以看到在设备上显示SD的卷标了。
在这个部分涉及到一个断点续传的问题,比如在向SD卡写入数据的过程中如果Suspend设备,之后再Wakeup。那么之前的写入操作会从刚才的断点开始。在这里有一个Pnp设备Unmount的Delay时间,这个时间一般通过注册表来设定。在设备WakeUp之后,实际会执行一次Unmount的动作,之后又会去执行一次Mount操作。在Unmount之后,系统把要Unmount的设备标记一下,之后等待这个Delay时间,Delay时间过了之后,存储管理才会把标记过的设备Unmount掉,如果在时间还没有到的时候重新Mount,系统会重复之前介绍过的动作,但是在这里会去比较这个新Mount进来的设备和之前CStore链表上的是否有一致的,这个比较是非常严格的,比如说设备的ID,修改的时间等等。这样才不会在中途换卡而系统仍认为是前一张卡的错误出现。如果确认是同一张卡并且卡上内容没有变动过,就把新的CStore中的部分信息赋给原来链表上的那个CStore,但是大部分内容不变,之后删除新的这个CStore实例。在删除过程中,需要把部分成员设为NULL,这样这些空间就不会在Delete Store的时候也被删除,因为这些还要在老的CStore上使用,由于大部分内容没有变化,特别是CPartition中的m_pDsk没有变,它所指向的Volume的内容也没有变化,这样才能够做到断点的续传。
最后还有在Unmount的时候系统会做些什么操作呢。在Unmount的时候,由于要保证该Store上的操作都必须完成。判断Vol的成员c_Thread的值是否为0,如果为0说明没有线程在操作该Store,那么就正常Unmount。如果不为0说明还有线程没有退出,这时创建一个线程去判断是否操作都完成,同时将Vol设为不可用,这样之后对该Store的操作将不被允许。那么这些未完成的线程操作会在退出时将c_Thread减1,如果该变量为0,那么就会SetEvent告诉刚才创建的判断线程,这样Unmount才会去执行接下来的Unmount操作
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/gooogleman/archive/2008/11/20/3339247.aspx