Android 2.3 SD卡挂载流程浅析

来源:互联网 发布:淘宝客服英文怎么说 编辑:程序博客网 时间:2024/06/14 20:14








一、vold.fstab 配置文件格式:

 

## Format: dev_mount <label><mount_point> <part> <sysfs_path1...>

## label        - Label for the volume

## mount_point  - Where the volume will be mounted

## part         - Partition # (1 based), or 'auto' forfirst usable partition.

## <sysfs_path> - List of sysfs pathsto source devices

 

 

dev_mount udisk0 /mnt/usb_storage/USB_DISK01 /devices/platform/usb

dev_mount udisk1 /mnt/usb_storage/USB_DISK11 /devices/platform/usb

 

 

 

二、标准android 流程:

1、  收到一个mount DEVPATH的前缀等于vold.fstab中的sysfs_path,则将该dev mount到vold.fstab中的mount_point中。

2、  配置文件中的一条dev_mount语句等于一个volume。如果配置文件配置了4个volume,只支持4个volume。

3、  如果一个移动硬盘有5个分区,这个配置文件则不够用。

 

三、rk的vold方案:

1、  配置文件中的一条dev_mount语句等于一个disk(设备),多个分区都mount在mount_point/label%d中。

2、  这样就可以支持多个设备多分区。



以下转自:

转自:http://blog.csdn.net/yihongyuelan/article/details/6930112

和转自:http://blog.csdn.net/ken_GL/article/details/6050168

NetlinkManager中通过socket来接收来自kernel的event,获取设备的插拔信息。



 在上一篇博文《Android 2.3 SD卡挂载流程浅析(一)》主要简单的介绍了SD卡的挂载流程。包括了从内核层到用户层事件消息的传递,以及Vold的简介。本文将继续介绍SD卡的挂载,但文中并不会涉及代码的详细分析,因为这部分网上已有资料,我会在文章结尾贴出来供大家参考。本文主要目的是一方面对自己学习这一部分的总结,另一方面希望大家能够指出文中理解错误的地方。


      1.SD卡挂载流程图

      SD卡的挂载流程图如下:



        绿色箭头:表示插入SD卡后事件传递以及SD卡挂载

        红色箭头:表示挂载成功后的消息传递流程

        黄色箭头:表示MountService发出挂载/卸载SD卡的命令

        大家可能对图中突然出现的这么多的名称感到奇怪,这些都是在Android 2.3 源码中可以找到的,接下来我会为大家一一解释这些类的作用。


       2.各个文件的主要作用

    (1)Kernel:这个是系统内核啦。不是我要分析的文件,本文涉及内容不是内核级的哦!(努力学习中...)


       (2)NetlinkManager:全称是NetlinkManager.cpp位于Android 2.3源码位置/system/vold/NetlinkManager.cpp。该类的主要通过引用NetlinkHandler类中的onEvent()方法来接收来自内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。


    (3)VolumeManager:全称是VolumeManager.cpp位于Android 2.3源码位置/system/vold/VolumeManager.cpp。该类的主要作用是接收经过NetlinkManager处理过后的事件消息。因为我们这里是SD卡的挂载,因此经过NetlinkManager处理过后的消息会分为五种,分别是:block,switch,usb_composite,battery,power_supply。这里SD卡挂载的事件是block。


       (4)DirectVolume:位于/system/vold/DirectVolume.cpp。该类的是一个工具类,主要负责对传入的事件进行进一步的处理,block事件又可以分为:Add,Removed,Change,Noaction这四种。后文通过介绍Add事件展开。


       (5)Volume:Volume.cpp位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进行挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。


       (6)NativeDaemonConnector:该类位于frameworks/base/services/java/com.android.server/NativeDaemonConnector.java。该类用于接收来自Volume.cpp 发来的SD卡挂载消息并向上传递。


       (7)MountService:位于frameworks/base/services/java/com.android.server/MountService.java。MountService是一个服务类,该服务是系统服务,提供对外部存储设备的管理、查询等。在外部存储设备状态发生变化的时候,该类会发出相应的通知给上层应用。在Android系统中这是一个非常重要的类。


       (8)StorageManaer:位于frameworks/base/core/java/andriod/os/storage/StorageManager.java。在该类的说明中有提到,该类是系统存储服务的接口。在系统设置中,有Storage相关项,同时Setting也注册了该类的监听器。而StorageManager又将自己的监听器注册到了MountService中,因此该类主要用于上层应用获取SD卡状态。

    通过上文对各个文件的作用简介,以及整个SD卡的挂载流程图可以知道,Android 系统是如何从底层获取SD卡挂载信息的。

       后文将继续分析程序调用流程图。



相对于linux来说,udev还是一个新事物。然而,尽管它03年才出现,尽管它很低调(J),但它无疑已经成为linux下不可或缺的组件了。udev是什么?它是如何实现的?最近研究Linux设备管理时,花了一些时间去研究udev的实现。 
   
  udev是什么?u 是指user space,dev是指device,udev是用户空间的设备驱动程序吗?最初我也这样认为,调试内核空间的程序要比调试用户空间的程序复杂得多,内核空间的程序的BUG所引起的后果也严重得多,device driver是内核空间中所占比较最大的代码,如果把这些device driver中硬件无关的代码,从内核空间移动到用户空间,自然是一个不错的想法。 
   
  但我的想法并不正确,udev的文档是这样说的, 
  1. dynamic replacement for /dev。作为devfs的替代者,传统的devfs不能动态分配major和minor的值,而major和minor非常有限,很快就会用完了。 udev能够像DHCP动态分配IP地址一样去动态分配major和minor。 
   
  2. device naming。提供设备命名持久化的机制。传统设备命名方式不具直观性,像/dev/hda1这样的名字肯定没有boot_disk这样的名字直观。udev能够像DNS解析域名一样去给设备指定一个有意义的名称。 
   
  3. API to access info about current system devices 。提供了一组易用的API去操作sysfs,避免重复实现同样的代码,这没有什么好说的。 
   
  我们知道,用户空间的程序与设备通信的方法,主要有以下几种方式, 
  1. 通过ioperm获取操作IO端口的权限,然后用inb/inw/ inl/ outb/outw/outl等函数,避开设备驱动程序,直接去操作IO端口。(没有用过) 
  2. 用ioctl函数去操作/dev目录下对应的设备,这是设备驱动程序提供的接口。像键盘、鼠标和触摸屏等输入设备一般都是这样做的。 
  3. 用write/read/mmap去操作/dev目录下对应的设备,这也是设备驱动程序提供的接口。像framebuffer等都是这样做的。 
   
  上面的方法在大多数情况下,都可以正常工作,但是对于热插拨(hotplug)的设备,比如像U盘,就有点困难了,因为你不知道:什么时候设备插上了,什么时候设备拔掉了。这就是所谓的hotplug问题了。 
   
  处理hotplug传统的方法是,在内核中执行一个称为hotplug的程序,相关参数通过环境变量传递过来,再由hotplug通知其它关注 hotplug事件的应用程序。这样做不但效率低下,而且感觉也不那么优雅。新的方法是采用NETLINK实现的,这是一种特殊类型的socket,专门用于内核空间与用户空间的异步通信。下面的这个简单的例子,可以监听来自内核hotplug的事件。 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
  #include 
   
  staticintinit_hotplug_sock(void) 
  { 
  structsockaddr_nl snl 
  constintbuffersize= 16 * 1024 * 1024; 
  intretval 
   
  memset(&snl, 0x00, sizeof(structsockaddr_nl)); 
  snl.nl_family = AF_NETLINK; 
  snl.nl_pid = getpid(); 
  snl.nl_groups = 1; 
   
  inthotplug_sock= socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
  if(hotplug_sock== -1) { 
  printf("error getting socket: %s", strerror(errno)); 
  return-1; 
  } 
   
  /* set receive buffersize */ 
  setsockopt(hotplug_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); 
   
  retval= bind(hotplug_sock, (structsockaddr*) &snl, sizeof(structsockaddr_nl)); 
  if(retval<0) { 
  printf("bind failed: %s", strerror(errno)); 
  close(hotplug_sock); 
  hotplug_sock= -1; 
  return-1; 
  } 
   
  returnhotplug_sock 
  } 
   
  #define UEVENT_BUFFER_SIZE 2048 
   
  intmain(intargc, char* argv[]) 
  { 
  inthotplug_sock = init_hotplug_sock(); 
   
  while(1) 
  { 
  charbuf[UEVENT_BUFFER_SIZE*2] = {0}; 
  recv(hotplug_sock, &buf, sizeof(buf), 0); 
  printf("%s/n", buf); 
  } 
   
  return0; 
  } 
   
  编译: 
  gcc -g hotplug.c -o hotplug_monitor 
   
  运行后插/拔U盘,可以看到: 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0 
  add@/class/scsi_host/host2 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83 
  add@/class/usb_device/usbdev2.2 
  add@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0 
  add@/class/scsi_disk/2:0:0:0 
  add@/block/sda 
  add@/block/sda/sda1 
  add@/class/scsi_device/2:0:0:0 
  add@/class/scsi_generic/sg0 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep81 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep02 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/usbdev2.2_ep83 
  remove@/class/scsi_generic/sg0 
  remove@/class/scsi_device/2:0:0:0 
  remove@/class/scsi_disk/2:0:0:0 
  remove@/block/sda/sda1 
  remove@/block/sda 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0/host2/target2:0:0/2:0:0:0 
  remove@/class/scsi_host/host2 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/2-1:1.0 
  remove@/class/usb_device/usbdev2.2 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1/usbdev2.2_ep00 
  remove@/devices/pci0000:00/0000:00:1d.1/usb2/2-1 
   
  udev的主体部分在udevd.c文件中,它主要监控来自4个文件描述符的事件/消息,并做出处理: 
  1. 来自客户端的控制消息。这通常由udevcontrol命令通过地址为/org/kernel/udev/udevd的本地socket,向udevd发送的控制消息。其中消息类型有: 
  l UDEVD_CTRL_STOP_EXEC_QUEUE 停止处理消息队列。 
  l UDEVD_CTRL_START_EXEC_QUEUE 开始处理消息队列。 
  l UDEVD_CTRL_SET_LOG_LEVEL 设置LOG的级别。 
  l UDEVD_CTRL_SET_MAX_CHILDS 设置最大子进程数限制。好像没有用。 
  l UDEVD_CTRL_SET_MAX_CHILDS_RUNNING 设置最大运行子进程数限制(遍历proc目录下所有进程,根据session的值判断)。 
  l UDEVD_CTRL_RELOAD_RULES 重新加载配置文件。 
  2. 来自内核的hotplug事件。如果有事件来源于hotplug,它读取该事件,创建一个udevd_uevent_msg对象,记录当前的消息序列号,设置消息的状态为EVENT_QUEUED,然后并放入running_list和exec_list两个队列中,稍后再进行处理。 
  3. 来自signal handler中的事件。signal handler是异步执行的,即使有signal产生,主进程的select并不会唤醒,为了唤醒主进程的select,它建立了一个管道,在 signal handler中,向该管道写入长度为1个子节的数据,这样就可以唤醒主进程的select了。 
  4. 来自配置文件变化的事件。udev通过文件系统inotify功能,监控其配置文件目录/etc/udev/rules.d,一旦该目录中文件有变化,它就重新加载配置文件。 
   
  其中最主要的事件,当然是来自内核的hotplug事件,如何处理这些事件是udev的关键。udev本身并不知道如何处理这些事件,也没有必要知道,因为它只实现机制,而不实现策略。事件的处理是由配置文件决定的,这些配置文件即所谓的rule。 
   
  关于rule的编写方法可以参考《writing_udev_rules》,udev_rules.c实现了对规则的解析。 
   
  在规则中,可以让外部应用程序处理某个事件,这有两种方式,一种是直接执行命令,通常是让modprobe去加载驱动程序,或者让mount去加载分区。另外一种是通过本地socket发送消息给某个应用程序。 
   
  在udevd.c:udev_event_process函数中,我们可以看到,如果RUN参数以”socket:”开头则认为是发到socket,否则认为是执行指定的程序。 
   
  下面的规则是执行指定程序: 
  60-pcmcia.rules: RUN+="/sbin/modprobe pcmcia" 
   
  下面的规则是通过socket发送消息: 
  90-hal.rules:RUN+="socket:/org/freedesktop/hal/udev_event" 
   
  hal正是我们下一步要关心的,接下来我会分析HAL的实现原理。 
   

原创粉丝点击