这篇文章中主要是分析一下,android 系统里面的 Vold—— Vold 是andorid 系统的设备管理器,扮演着linux里面的udev的角色。 它通过监听uevent的端口,取得uevent事件,dispatch 到 相应的 Listener,执行相应的动作。

 

 

UEvent

在linux平台上,uevent 给系统软件提供设备事件,设备节点的权限管理等等,它由kernel发出。通过socket与 udev 守护进程通讯 (systemd-udevd.service), 在 sysfs 下的很多 kobject 下都有 uevent 属性,它主要用于内核与 udev (自动设备发现程序)之间的一个通信接口,应用软件,也可以直接使用该uevent文件做作为一个驱动接口。 uevent 是具有一定格式的字符串,包含一些内容,如: action: {add, change, remove,} devpath设备节点号 {major, minor} 等。通过对uevent的解析,就能得到kernel发生了那些设备事件。

使用 man udev 可以得到更多关于udev的信息。

下面是我从kernel 的readme文件里面,找的两个 uevent例子。

1.) Path failure.

UEVENT[1192521009.711215] change@/block/dm-3

ACTION=change

DEVPATH=/block/dm-3

SUBSYSTEM=block

DM_TARGET=multipath

DM_ACTION=PATH_FAILED

DM_SEQNUM=1

DM_PATH=8:32

DM_NR_VALID_PATHS=0

DM_NAME=mpath2

DM_UUID=mpath-35333333000002328

MINOR=3

MAJOR=253

SEQNUM=1130

 

2.) Path reinstate.

UEVENT[1192521132.989927] change@/block/dm-3

ACTION=change

DEVPATH=/block/dm-3

SUBSYSTEM=block

DM_TARGET=multipath

DM_ACTION=PATH_REINSTATED

DM_SEQNUM=2

DM_PATH=8:32

DM_NR_VALID_PATHS=1

DM_NAME=mpath2

DM_UUID=mpath-35333333000002328

MINOR=3

MAJOR=253

SEQNUM=1131

 

Vold Listen and Dispatch

Vold 的工作流程比较简单,开机后它主要是启动了2个 Listener:CommandListener和NetlinkManager,它们针对监听的内容进行了过滤,所以不会出现“打架”的情况。拿到uevent事件后,分别dispatch到自己的下级模块。大部分的工作都是统一到VolumeManager里面来处理的。

下面举一个插入U盘的例子。

1) NetlinkManager 监听到事件后,通过 NetlinkHandler dispatch 到 VolumeManager.handleBlockEvent 函数。

2) 如果系统里面还没有设备节点,则创建一个。 mknod(device, mode, dev) , 创建的设备文件: /dev/block/vold/8:1

3) 得到uuid 号 —— getVolumeUUID(), 本例的结果是 1E37-C6B3

4) 生成 DirectVolume 对象,反馈信息到kernel.

mVm->getBroadcaster()->sendBroadcast( ResponseCode::VolumeDiskInserted, ) 反馈 VolumeDiskInserted 到kernel.

5) CommandListener 监听到uevent事件,dispatch到 VolumeCmd.

VolumeCmd::runCommand --> rc = vm->mountVolume(argv[2]);

Volume::mountVol()

Ntfs::doMount(devicePath, "/mnt/secure/staging", fals,)

doMount结束后,U盘被mount到了 /mnt/usb/sda1, 可以被正常使用。

 

Coldboot

这个功能利用uevnet文件作为与驱动层的接口, 直接写入 add 事件,达到重新初始化的目的。

我们也可以利用这个特性来,初始化一些设备。

coldboot("/sys/block");

fd = openat(dfd, "uevent", O_WRONLY);

write(fd, "add\n", 4); —— 代码里面通过递归,写了 /sys/block目录下,所有的uevent文件。

 

配置

路径:一般情况下是device/路径下的 fstab 文件,如 fstab.xx文件。 它在init.rc文件里面被include。

on fs

.....

mount_all /fstab.xx

init 解析该文件后,会做 system/core/init/builtins.c::do_mount_all . 顾名思义,会 mount 文件里面配置的设备。

格式: <src> <mnt_point> <type> <mnt_flags> <fs_mgr_flags>

  • src: sysfs 下面的设备文件路径。

  • fs_mgr_flags: 这个不太好理解,官网上的解释是:“Vold ignores any lines in the unified fstab that do not include the voldmanaged= flag in this field. This flag must be followed by a label describing the card, and a partition number or the word auto. Here is an example: voldmanaged=sdcard:auto. Other possible flags are nonremovable, encryptable=sdcard, and noemulatedsd. " ——说实在的,也看不太明白。当不明白的时候,代码是最好的解释。

    我搜索了一下代码(system/core/fs_mgr/fs_mgr.c):

    if (fstab->recs[i].fs_mgr_flags & MF_WAIT)
        wait_for_file(fstab->recs[i].blk_device, WAIT_TIMEOUT);

    WAIT 标志位,就是等待设备文件(<src>)的是否创建。如:   /dev/block/platform/xxxx /system ext4 ro wait

mount /system 的时候,会等待 "/dev/block/platform/xxxx" 这个文件,系统初始化是一个异步处 理过程, 执行到这一步的时候, 需要 wait mount 所需要的资源已经足够。

if (fstab->recs[i].fs_mgr_flags & MF_CHECK)
    check_fs(fstab->recs[i].blk_device, fstab->recs[i].fs_type,

CHECK 志位表示需要检查EXT2/3/4文件系统。

if ((fstab->recs[i].fs_mgr_flags & MF_CRYPT)
    mount("tmpfs", fstab[i].mnt_point, "tmpfs",

CRYPT标志,表示 mount tmpfs 文件系统。

 

SDCard

SD卡,在andorid系统上面不同于U盘,它有个特殊的地方:App会存储它的数据到主SD卡里面。 如果在init rc 文件里面,做了下面类似的配置:

export EXTERNAL_STORAGE /storage/sdcard0

Andorid系统会使用 /storage/sdcard0,做为App的数据存储卡。会对该SD卡做一些安全处理,使得每个App只能访问自己的目录,而不能去随意访问别人的目录。也可以配置使用内部的存储器(Flash)来模拟主SDCard, 类似下面的配置。

export EXTERNAL_STORAGE /storage/emulated/legacy

export EMULATED_STORAGE_SOURCE /mnt/shell/emulated

export EMULATED_STORAGE_TARGET /storage/emulated

on fs
setprop ro.crypto.fuse_sdcard true
service sdcard /system/bin/sdcard -u 1023 -g 1023 -l /data/media /mnt/shell/emulated

不同的系统,估计会有一些差异。

用户评论:
发表评论: (限500字)

注册 忘记密码 登录