【输出文档】 Android 存储模块 解析

 

【存储模块培训文档,很认真写的,现在转移到博客上】

 

                                 Android  存储模块解析

 

 

1.存储系统架构

Android 的存储系统主要由SystemServer进程中的MountService和Vold进程中的VolumeManager组成,它们管理着系统的存储设备,执行各种操作,包括 卸载(unmount)、挂载(mount)、格式化(format)等。

存储系统的架构如下图所示:

 

在Android的存储系统中,MountService是为应用提供服务的Binder类,运行在SystemServer中,StorageManager是MountService的代理,在用户进程中被使用。Vold是一个守护进程,负责和底层存储系统的驱动交互。MountService和Vold之间通过socket进行双向通信:MountService向vold下发命令,vold向MountService反馈底层硬件发生的变化。

Vold进程的主体是VolumeManager对象,它管理着系统底层所有的Disk和Volume对象,实现存储的各种操作。Vold中的CommandListener对象负责和MountService中的NativeDemonConnector对象进行Socket通信,NetlinkHandler对象负责坚挺kernel层上报的Netlink Socket消息。

 

  1. Vold守护进程

Vold 是 Volume Daemon的缩写,主要用来管理Android系统中的:1.USB存储设备;2.SD卡设备。

2.1 vold 初始化

Vold通过init进程启动,它在init.rc中定义:

service vold /system/bin/vold \

        --blkid_context=u:r:blkid:s0 --blkid_untrusted_context=u:r:blkid_untrusted:s0 \

        --fsck_context=u:r:fsck:s0 --fsck_untrusted_context=u:r:fsck_untrusted:s0

    class core

    socket vold stream 0660 root mount

    socket cryptd stream 0660 root mount[p1] 

ioprio be 2

//blkid_context 等为linxu下blkid 和 fsck两个工具的运行环境,这里主要的规定的是运行时的selinux  环境。具体见注1.

Vold服务属于core组,系统启动时会被init进程启动。此外,这里还定义了2个socket:

   1)socket vold:用于vold和JAVA层的MountService通信

   2)socket cryptd:[p2] 

 

Vold的源码路径为:/system/vold , 入口函数main():

int main(int argc, char** argv){

... ...

    VolumeManager *vm;

    CommandListener *cl;

    CryptCommandListener *ccl;

    NetlinkManager *nm;

//检测vold运行时的selinux环境,具体见注1

parse_args(argc, argv);

    sehandle = selinux_android_file_context_handle();

    if (sehandle) {

        selinux_android_set_sehandle(sehandle);

    }

    //初始化在init.rc中定义的vold和cryptd两个socket。

    // Quickly throw a CLOEXEC on the socket we just inherited from init

    fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);

    fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);

    //创建 “/dev/block/vold”这个文件夹,此文件夹下记录着vold守护进程中创建的Disk对象和volume(包括public 和 private)对象

mkdir("/dev/block/vold", 0755);



    /* For when cryptfs checks and mounts an encrypted filesystem */

    klog_set_level(6);[p3] 

    //初始化VolumeManager 和NetlinkManager对象

    /* Create our singleton managers */

    if (!(vm = VolumeManager::Instance())) {

    ... ...

    }

    if (!(nm = NetlinkManager::Instance())) {

    ... ...

    }



    if (property_get_bool("vold.debug", false)) {

        vm->setDebug(true);

    }

    //CommandListener负责和JAVA层的NativeDeamonConnector的通信,CryptCommandListener负责[p4] 

    cl = new CommandListener();

    ccl = new CryptCommandListener();

    vm->setBroadcaster((SocketListener *) cl);

    nm->setBroadcaster((SocketListener *) cl);



    if (vm->start()) {

    ... ...

    }



    if (process_config(vm)) {

    ... ...

    }



    if (nm->start()) {

    ... ...

    }

    coldboot("/sys/block");

... ...

//进入循环睡眠

    exit(0);

}

 

1

 parse_args这个方法根据init.rc中配置的blkid_context fsck_context等信息初始化了android::vold::sBlkidContext / sBlkidUntrustedContext / sFsckContext / sFsckUntrustedContext等变量,这些变量影响到了后续vold流程中调用blkidfsck等工具时,调用/system/vold/Utils::ForkExecvp setexeccon(context) 的结果:

  如果下发BlkidContext,就要求vold处于selinux 已初始化且为enable状态,否则运行blkid工具时会导致vold进程被强制abort

  BlkidUntrustedContext则对selinux环境无限制。

2.2 process_config

process_config函数首先是读取fstab.${ro.hardware}文件的内容,然后根据文件中的每行定义的存储器的属性值来调用VolumeManager对象中的方法来创建Disk对象,并规划到VolumeManager的管理中。

static int process_config(VolumeManager *vm) {

    ... ...

    bool has_adoptable = false;

... ...

//读取fstab文件,筛选出<fs_mgr_flags>中有标记voldmanaged

    if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {

      //不支持标有nonremovable的。前面已经说了,vold仅负责管理“可移动”的存               储设备

        //初始化DiskSource::sysPattern,被赋值于fstab文件中的<sys_path>

            std::string sysPattern(fstab->recs[i].blk_device);

            //初始化DiskSource::nickname,被赋值于fstab文件的中的<fs_mgr_flags>中的flag_vals.label;

            std::string nickname(fstab->recs[i].label);

            int flags = 0;

            [p5] 

            if (fs_mgr_is_encryptable(&fstab->recs[i])) {

                flags |= android::vold::Disk::Flags::kAdoptable;

                has_adoptable = true;

            }

            if (fs_mgr_is_noemulatedsd(&fstab->recs[i])

                    || property_get_bool("vold.debug.default_primary", false)) {

                flags |= android::vold::Disk::Flags::kDefaultPrimary;

            }



            vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(

                    new VolumeManager::DiskSource(sysPattern, nickname, flags)));

    ... ...

    property_set("vold.has_adoptable", has_adoptable ? "1" : "0");

    return 0;

}

 

2.3 fstab文件

Android系统上由于使用了Vold取代了udev来管理磁盘,自然也要有一个类似于udev的config来配置和管理磁盘的挂载,Android使用的是vold.fstab来实现这一目的。该配置文件的格式和解析意义如下表:

格式

说明

示例

<sys_path>

设备实际路径

/devices/platform/comip-mmc.0/mmc_host/mmc1

<mnt_point>

挂载点

storage/sdcard1

<type>

挂载格式

dev_mount

<mnt_flags and options>

 

 

<fs_mgr_flags>

分区的属性,包括是否加密、是否可以移动(物理卸载)

 

需要注意的是:

//TODO 解析fstab文件的代码

在L1860项目上,对应的配置文件为:/device/leadcore/lte26007/fstab.lc1860

//TODO 具体到18601项目上fstab文件的特殊性

 

2.4 VolumeManager

VolumeManager的主要作用就是创建并管理Disk和volume对象,并处理又kernel层上报的block_event消息以及由java层下发的对Disk 和 volume等对象的mount,unmount,format等操作。

2.4.1 start

VolumeManager的构造函数没什么可以说的,VolumeManager的对象是在main()中创建的,创建后即调用vm->start();

int VolumeManager::start() {

    //每次start VolumeManager之前均unmount所有VolumeManager管理的volume对象

    unmountAll();

    //创建mInternalEmulated对象。这个mInternalEmulated对象就代表我们经常说的“内部存储”或者“内置SD卡”

    CHECK(mInternalEmulated == nullptr);

    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(

            new android::vold::EmulatedVolume("/data/media"));

    mInternalEmulated->create();

... ...

关于EmulatedVolume 对象的具体信息,在后面分析。

 

有上述可以发现,在VolumeManager初始化时,仅仅只是初始化了mInternalEmulated这个volume,没有外部存储设备相关的volume信息:Vold默认仅仅只有内部存储一定是存在的。

2.4.2 handleBlockEvent

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {

    std::lock_guard<std::mutex> lock(mLock);



    std::string eventPath(evt->findParam("DEVPATH"));

    std::string devType(evt->findParam("DEVTYPE"));

    //仅仅只处理disk的消息

    if (devType != "disk") return;

    int major = atoi(evt->findParam("MAJOR"));

    int minor = atoi(evt->findParam("MINOR"));

    dev_t device = makedev(major, minor);

    switch (evt->getAction()) {

    case NetlinkEvent::Action::kAdd: {

        for (auto source : mDiskSources) {

            if (source->matches(eventPath)) {

                int flags = source->getFlags();

                if (major == kMajorBlockMmc) {

                    flags |= android::vold::Disk::Flags::kSd;

                } else {

                    flags |= android::vold::Disk::Flags::kUsb;

                }



                auto disk = new android::vold::Disk(eventPath, device,

                        source->getNickname(), flags);

                disk->create();

                mDisks.push_back(std::shared_ptr<android::vold::Disk>(disk));

                break;

            }

        }

        break;

    }

    case NetlinkEvent::Action::kChange: {

        LOG(DEBUG) << "Disk at " << major << ":" << minor << " changed";

        for (auto disk : mDisks) {

            if (disk->getDevice() == device) {

                disk->readMetadata();

                disk->readPartitions();

            }

        }

        break;

    }

    case NetlinkEvent::Action::kRemove: {

        auto i = mDisks.begin();

        while (i != mDisks.end()) {

            if ((*i)->getDevice() == device) {

                (*i)->destroy();

                i = mDisks.erase(i);

            } else {

                ++i;

            }

        }

        break;

    }

    default: {

        LOG(WARNING) << "Unexpected block event action " << (int) evt->getAction();

        break;

    }

    }

}

 

    HandleBlockEvent 是NetlinkHandler在接收到kernel上报的block_event后调用的,NetlinkHandler在后面会进行详细的分析。

● handleBlockEvent  会分析上报event的device的major num 和minor num,通过调用makedev来生成对应的device。以L1860位例,evt中上报的device num 为45952,转化为2进制为 1011001110000000,取后8位为minor,其余的前8位为major,则major =179,minor =129,所以device为”179,129”。

   ●从kernel层发送的消息分为3大类:NLActionAdd,NLActionRemove,NLActionChange.分别对应

1.NetlinkEvent::Action::kAdd

    handleBlockEvent  会分析上报的event中eventPath的值,这个值代表event事件中的设备所处于kernel层的<sys_path> 。当这个值和我们在vold的main:main()中调用process_config 读取fstab.${ro.hardware}生成的DiskSource对象的<sys_path> 相同时,handleBlockEvent 才会继续处理这个event。

AndroidFramework层原生是适配SD卡的,但是有些时候我们发现,即使kernel已经能正常的识别SD设备,Framework层仍然不会去挂载SD卡。原因就在于kernel上报的这个event中的eventPath和我们在fstab.${ro.hardware}中配置的<sys_path>不匹配,HandleBlockEvent 主动跳过了这个event

因此,我们在确定fstab.${ro.hardware}文件时中<sys_path>的值时,需要根据kernel上报的eventEventPath的值来确定。

lc1860为例:/dev/block/mmcblk1p1/devices/platform/comip-mmc.0/mmc_host/mmc1/*均可以代表SD卡的path,但是kernelevent上报的EventPath的为“”[p6] 

  

在NetlinkEvent::Action::kAdd中,VolumeManager主要做了两件事:

  1. 通过上报device 的major num判断设备的类型,major =179的确定为sdcard设备,将此设备对应的Disk对象的flags中添加android::vold::Disk::Flags::kSd位;其余的均为USB存储设备,在其对应的Disk对象的flags中添加 android::vold::Disk::Flags::kUsb。

对于Android的存储系统来说,Sdcard 和USB 存储设备是两种不同的存储设备,两者的挂载流程,挂载路径,挂载后的权限以及对两设备中文件的读写权限的限制都会不同,大部分时候都是通过Disk::Flags 来进行区分的。

  1. 创建disk 对象,强disk添加到mDisks,纳入VolumeManager的管理中
  1. NetlinkEvent::Action::kChange

    [p7] 

3.NetlinkEvent::Action::kRemove

[p8] 

tip 1 device的major和minor number

    Linux系的/dev目录下面的的设备文件是用来表示外设的,如/dev/sda1表示第一块硬盘的第一个分区。但是这个/dev/sda1仅仅是方便用户观察,linux内核中表示不同的设备是通过major 和minor number实现的,通过major和minor Number来加载相应的驱动程序。

major number:表示不同的设备类型,主设备号

minor number:表示同一个设备的的不同分区,次设备号

Linux 2.6 内核中,表示 major minor 的数据结构是一个32bit的字段 dev_t type (defined in <linux/types.h>),表示major minor 的位数分别是 12 20 bits

 

2.5 Disk 对象

Disk对象定义在/system/vold/disk.cpp中,构造函数如下:

Disk::Disk(const std::string& eventPath, dev_t device,

        const std::string& nickname, int flags) :

        mDevice(device), mSize(-1), mNickname(nickname), mFlags(flags), mCreated(

                false), mJustPartitioned(false) {

    mId = StringPrintf("disk:%u,%u", major(device), minor(device));

    mEventPath = eventPath;

    mSysPath [p9] = StringPrintf("/sys/%s", eventPath.c_str());

    mDevPath = StringPrintf("/dev/block/vold/%s", mId.c_str());

    CreateDeviceNode(mDevPath, mDevice);

}

 

其中:

mEventPath /eventPath:disk对象对应设备在kernel层的实际路径;

mDevPath :在lc1860中为Disk:179,129。Vold层管理的设备记录在/dev/block/vold文件下

device:disk对象对应设备的实际设备号,在lc1860中为179,129

nickname:disk对象对应设备的标志;

flags:disk对象的flags,具体定义在Disk.h中:   

enum Flags {

        /* Flag that disk is adoptable */

        kAdoptable = 1 << 0,

        /* Flag that disk is considered primary when the user hasn't

         * explicitly picked a primary storage location */

        kDefaultPrimary = 1 << 1,

        /* Flag that disk is SD card */

        kSd = 1 << 2,

        /* Flag that disk is USB disk */

        kUsb = 1 << 3,

        /* Flag that disk is EMMC internal */

        kEmmc = 1 << 4,

};

 

2.5.1 CreateDeviceNode

     定义在/system/vold/Utils.cpp下,主要的工作是调用了 mknod(cpath, mode, dev)  函数。其中:
     cpath为Disk::mDevPath,在1860项目中为 /dev/block/vold/Disk:179,129;

     dev:为Disk::mDevice,在1860中为179,129这个device;

     mknod 命令是建立一个目录项和一个特殊文件的对应索引节点CreateDeviceNode函数的作用就是将dev/block/vold/Disk:179,129这个文件夹作为了179,129 这个设备的索引点,之前在2.4 VolumeManager中说明,179,129这个device实际代表kernel层中外置SD卡的对应的文件夹,因此,现/dev/block/vold/Disk:179,129就代表了外置SD卡的文件夹索引点[p10] 。

2.5.2 Disk::readMetadata()

元数据(Metadata),又称中介数据、中继数据,为描述数据的数据(data about data),主要是描述数据属性(property)的信息,用来支持如指示存储位置、历史数据、资源查找、文件记录等功能。元数据算是一种电子式目录,为了达到编制目录的目的,必须在描述并收藏数据的内容或特色,进而达成协助数据检索的目的。

具体函数如下:

status_t Disk::readMetadata() {

    mSize = -1;

    mLabel.clear();

    ... ...

    switch (major(mDevice)) {

    case kMajorBlockScsiA: case kMajorBlockScsiB:case ... ...

    //插入SD卡或者USB存储设备时都不会满足条件,忽略

    ... ...: {

    ... .. ..

    }

    //kMajorBlockMmc = “179”,代表设备属于mmc存储设备

    case kMajorBlockMmc: {

        std::string path(mSysPath + "/device/manfid");

        std::string tmp;

        if (!ReadFileToString(path, &tmp)) {

            PLOG(WARNING) << "Failed to read manufacturer from " << path;

            return -errno;

        }

        uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);[p11] 

        switch (manfid) {

        case 0x000003: mLabel = "SanDisk"; break;

        case 0x00001b: mLabel = "Samsung"; break;

        case 0x000028: mLabel = "Lexar"; break;

        case 0x000074: mLabel = "Transcend"; break;

        }

        break;

    }

    ... ...

    }

    //向java层发送DiskSizeChanged,DiskLabelChanged,DiskSysPathChanged的Event

    notifyEvent(ResponseCode::DiskSizeChanged, StringPrintf("%" PRId64, mSize));

    notifyEvent(ResponseCode::DiskLabelChanged, mLabel);

    notifyEvent(ResponseCode::DiskSysPathChanged, mSysPath);

    return OK;

}

简单来说,Disk::readMetadata函数,主要就是为了检查Disk对象所对应的存储设备的各种属性信息:

Dis::mLabel : 对应设备的manfid,即生产厂商;

Disk::mSize : disk的代表的存储设备的总容量[p12] 

Disk::mSysPath :  Device path under sysfs

并通知JAVA层的MountService更新对应Disk的相应属性值。

2.5.3 Disk::readPartitions()

//TODO code explain



status_t Disk::readPartitions() {

    int8_t maxMinors = getMaxMinors();[p13] 

    if (maxMinors < 0) {

        return -ENOTSUP;

    }

    // read代表重新读取Disk中个partition信息并生产信息的volume对象。所以需要destroy all。

    destroyAllVolumes();

    //1860中,下发的命令为/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”

    std::vector<std::string> cmd;

    cmd.push_back(kSgdiskPath);

    cmd.push_back("--android-dump");

    cmd.push_back(mDevPath);



    std::vector<std::string> output;

    status_t res = ForkExecvp(cmd, output);

    ... ...

    //解析返回的结果

    for (auto line : output) {

    ... ...

        if (!strcmp(token, "DISK")) {

            //解析disk信息,确定记录Disk的文件系统的是MBR表还是gpt表

            const char* type = strtok(nullptr, kSgdiskToken);

            if (!strcmp(type, "mbr")) {

                table = Table::kMbr;

            } else if (!strcmp(type, "gpt")) {

                table = Table::kGpt;

            }

        } else if (!strcmp(token, "PART")) {

            //解析Disk下的partition信息

            foundParts = true;

            int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);

            ... ...

            //根据的disk的Major_num和Minor_num 来确定其中个partition的          device_num。Major_num代表设备的类型,不变;Minor_num 由disk的Minor_num加此分区的分区num

            dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);



            if (table == Table::kMbr) {

                const char* type = strtok(nullptr, kSgdiskToken);



                switch (strtol(type, nullptr, 16)) {

                case 0x06: // FAT16

                case 0x0b: // W95 FAT32 (LBA)

                case 0x0c: // W95 FAT32 (LBA)

                case 0x0e: // W95 FAT16 (LBA)

                    createPublicVolume(partDevice);

                    break;

                }

            } else if (table == Table::kGpt) {

                const char* typeGuid = strtok(nullptr, kSgdiskToken);

                const char* partGuid = strtok(nullptr, kSgdiskToken);



                if (!strcasecmp(typeGuid, kGptBasicData)) {

                    createPublicVolume(partDevice);

                } else if (!strcasecmp(typeGuid, kGptAndroidExpand)) {

                    createPrivateVolume(partDevice, partGuid);

                }

... ...

}

 

[p14] 

    readPartition()的主要作用是调用linux原生的blkid工具,下发

“/system/bin/blkid --android-dump /dev/block/vold/Disk:179,128”命令,读取disk对应的存储设备的partition分区信息,根据反馈的信息,决定创建PublicVolume对象还是PrivateVolume对象。

 

2.5.4 Disk::partitionPublic()

status_t Disk::partitionPublic() {

... ...

    //清楚当前disk下所有的volume对象

    destroyAllVolumes();

... ...

//对disk设备下发 /system/bin/sgdisk --zap-all /dev/block/vold/disk:179,128命令

//擦出disk设备的分区表

    std::vector<std::string> cmd;

    cmd.push_back(kSgdiskPath);

    cmd.push_back("--zap-all");

    cmd.push_back(mDevPath);

... ...

    if ((res = ForkExecvp(cmd)) != 0) {

        LOG(WARNING) << "Failed to zap; status " << res;

    }



   //更新diskinfo

    int rc = apply_disk_config(&dinfo, 0);

//



    return rc;

}

2.5.5 Disk::partitionPrivate()

代码较多,其实就是对diks的存储设备下发了一系列命令,已1860为例,下发了:

/system/bin/sgdisk 

--new=0:0:+16M

--typecode=0:19A710A2-B3CA-11E4-B026-10604B889DCF

--change-name=0:android_meta

--new=0:0:-0

--typecode=0:193D1EA4-B3CA-11E4-B075-10604B889DCF

--partition-guid=0:aae61afb6d099da2a04cf52ce5b5d01a

--change-name=0:android_expand

/dev/block/vold/disk:179,128

 

此命令的作用就是将/dev/block/vold/disk:179,128 对应的存储设备分为2个partitions,其中

 

1.一个应该为PART 1,地址为 179:129(这个partition应该暂时无用,作为后续扩展什么的用途);

Part1 的注释:   

 // Define a metadata partition which is designed for future use; there

    // should only be one of these per physical device, even if there are

    // multiple private volumes.
  1. PART2,地址为179:130.用于作为存储卡用途的partition

 

实际下发的命令结果:

 

 

2.6 volume对象

2.6.1 VolumeBase

1.构造函数:

VolumeBase::VolumeBase(Type type) :

        mType(type), mMountFlags(0), mMountUserId(-1), mCreated(false), mState(

                State::kUnmounted), mSilent(false) {

}

 

2.具体成员   

enum class Type {

        //PublciVolume,公共存储vol,对于与外置SD卡

        kPublic = 0,

        //PrivateVolume,私有存储vol,对应于adoptable devices volumes

        kPrivate,

        //EmulatedVolume,内部存储vol,对应于/data/media

        kEmulated,

        //

        kAsec,

        //

        kObb,

    };

 

 

 

private:  

  /* ID that uniquely references volume while alive */

    //volume对应的ID,一般是根据Disk的ID 生成,与disk的major_num相同,代表属于同种类型是存储设备,minor_num是在Disk的minor_num基础上计算得出,一般Disk分化为几个分区,第N个分区对应的minor_num = disk_minor_num + N,具体见

    std::string mId;



    /* ID that uniquely references parent disk while alive */

    //生成volume的Disk的ID

    std::string mDiskId;

    /* Partition GUID of this volume */

    std::string mPartGuid;

    /* Volume type */

    Type mType;

    /* Flags used when mounting this volume */

    int mMountFlags;



    /* User that owns this volume, otherwise -1 */

    //多用户状态下会涉及到,每个Volume对于与特定的user

    userid_t mMountUserId;



    /* Flag indicating object is created */

    bool mCreated;

   

    /* Current state of volume */

    State mState;



    /* Path to mounted volume */

    // volume 实际挂载路径,对于public来说,就是"storge" + Fs_UUID

    std::string mPath;



    /* Path to internal backing storage */

    std::string mInternalPath;

    /* Flag indicating that volume should emit no events */

    bool mSilent;



    /* Volumes stacked on top of this volume */

    std::list<std::shared_ptr<VolumeBase>> mVolumes;



    void setState(State state);



    DISALLOW_COPY_AND_ASSIGN(VolumeBase);

};

    2.6.1.1 volume状态

  

  enum class State {

        //被卸载状态

        kUnmounted = 0,

        //正处于被检查状态,一般处于此状态时,代表volume正处于被fsck检查文件系统的状态,fsck会根据Volume对于的Disk所对应的emmc设备的MBR表检测emmc设备是否正常,然后检查emmc设备的文件系统是否正常,如果以上出现问题,会先尝试修复,修复失败后才返回-1

        kChecking,

        //挂载状态

        kMounted,

        //挂载状态,且只能读,不可写

        kMountedReadOnly,

        //正处于被格式化状态

        kFormatting,

        //

        kEjecting,

        //无法挂载状态

        kUnmountable,

        //emmc设备被移除状态,一般是接受到底层emmc驱动上报的消息后才会处于此种状态

        kRemoved,

        //非法移除状态,USB OTG热插拔

        kBadRemoval,

    };

2.6.2 PublicVolume对象

由Disk::readPatitions() 中调用Disk::createPublicVolume()创建。公共访问存储区,对应于外置SD卡,USB OTG。

void Disk::createPublicVolume(dev_t device) {

    auto vol = std::shared_ptr<VolumeBase>(new PublicVolume(device));

    if (mJustPartitioned) {

        LOG(DEBUG) << "Device just partitioned; silently formatting";

        vol->setSilent(true);

        vol->create();

        vol->format("auto");

        vol->destroy();

        vol->setSilent(false);

    }



    mVolumes.push_back(vol);

    vol->setDiskId(getId());

    vol->create();

}

2.6.3 PrivateVolume对象

由Disk::readPatitions() 中调用Disk::createPrivateVolume()创建。

void Disk::createPrivateVolume(dev_t device, const std::string& partGuid) {

    std::string normalizedGuid;

    if (NormalizeHex(partGuid, normalizedGuid)) {

        LOG(WARNING) << "Invalid GUID " << partGuid;

        return;

    }



    std::string keyRaw;

    if (!ReadFileToString(BuildKeyPath(normalizedGuid), &keyRaw)) {

        PLOG(ERROR) << "Failed to load key for GUID " << normalizedGuid;

        return;

    }



    LOG(DEBUG) << "Found key for GUID " << normalizedGuid;



    auto vol = std::shared_ptr<VolumeBase>(new PrivateVolume(device, keyRaw));

    if (mJustPartitioned) {

        LOG(DEBUG) << "Device just partitioned; silently formatting";

        vol->setSilent(true);

        vol->create();

        vol->format("auto");

        vol->destroy();

        vol->setSilent(false);

    }



    mVolumes.push_back(vol);

    vol->setDiskId(getId());

    vol->setPartGuid(partGuid);

    vol->create();

}

2.6.4 EmulatedVolume对象

 

 在VolumeManager中创建:   

mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(

            new android::vold::EmulatedVolume("/data/media"));

    mInternalEmulated->create();

 

  1. 消息的传递

3.1 监听驱动发出的消息

3.1.1 NetlinkManager对象

NetLinkManager对象的主要作用是监听驱动发出的uevent消息。main()函数中调用NetlinkManager类的静态函数Instance()里创建NetlinkManager对象:

/system/vold/NetlinkManager.java

NetlinkManager *NetlinkManager::Instance() {

    if (!sInstance)

        sInstance = new NetlinkManager();

    return sInstance;

}

Istance()函数创建了NetlinkManager对象并通过静态变量sInstance来应用,这就意味着vold进程中只会有一个NetlinkManager对象。 

 

构造函数:

NetlinkManager::NetlinkManager() {

    mBroadcaster = NULL;

}     

                                                      

NetlinkManager的构造函数只是对变量mBroadcaster进行初始化。main()函数中会调用NetlinkManager的setBroadcaster()函数给变量mBroadcaster重新赋值:

 int main(int argc, char** argv) {

... ...

   nm->setBroadcaster((SocketListener *) cl);

... ...

   if (nm->start()){

... ...

}

 

/system/vold/NetlinkManager.cpp

int NetlinkManager::start() {

struct sockaddr_nl nladdr;

//设置64K 的缓冲区

    int sz = 64 * 1024;

    int on = 1;

    memset(&nladdr, 0, sizeof(nladdr));

    nladdr.nl_family = AF_NETLINK;

    nladdr.nl_pid = getpid();

    nladdr.nl_groups = 0xffffffff;

    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,

            NETLINK_KOBJECT_UEVENT)) < 0) {

    ... ...

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {

    ... ...

    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {

    ... ...

    }

    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {

    ... ...

    }

    mHandler = new NetlinkHandler(mSock);

    if (mHandler->start()) {

    ... ...

}

 

其中:NETLINK_KOBJECT_UEVENT代表kernel的内核消息的Uevent事件。

 

 

3.1.2 NetlinkHandler

void NetlinkHandler::onEvent(NetlinkEvent *evt) {

    VolumeManager *vm = VolumeManager::Instance();

const char *subsys = evt->getSubsystem();

... ...

    if (!strcmp(subsys, "block")) {

        vm->handleBlockEvent(evt);

    }

}

仅仅只会监听“block”类型的uevent事件。这个uevent事件会下发到VolumeManager::handleBlockEvent中进行处理。

 

 

         

3.1.*  socket   

int socket(int domain, int type, int protocol);

第一个参数指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数置AF_INET;

第二个参数指定要创建的套接字类型,流套接字类型为SOCK_STREAM、数据报套接字类型为SOCK_DGRAM、原始套接字SOCK_RAW(WinSock接口并不适用某种特定的协议去封装它,而是由程序自行处理数据包以及协议首部);

两个重要的类型是 SOCK_STREAM 和 SOCK_DGRAM。 SOCK_STREAM表明数据象字符流 一样通过 socket 。而 SOCK_DGRAM 则表明数据将是数据报(datagrams)的形式。

     

3.2 处理MountService发送的消息

MountServicevold之间是通过CommandListener对象进行通信的,在main函数中,对其进行初始化:

/system/vold/main.cpp

main() {

... ...

  cl = new CommandListener();

  vm->setBroadcaster((SocketListener *) cl);

... ...

  if (cl->startListener()) {

     PLOG(ERROR) << "Unable to start CommandListener";

     exit(1);

  }

... ... 

}

 

clCommandListener类实例化的一个对象,该对象负责和Framework进行通信。

CommandListener>FrameworkListener>SocketLisenter

3.2.1 CommandListener

在CommandLisenter中,定义了6个类,这6个类都继承了VoldCommand类,VoldCommand类继承了FrameworkCommand类:

VoldCommand—>FrameworkCommand



class CommandListener : public FrameworkListener {

public:

    CommandListener();

    virtual ~CommandListener() {}



private:

    static void dumpArgs(int argc, char **argv, int argObscure);

    static int sendGenericOkFail(SocketClient *cli, int cond);



    class DumpCmd : public VoldCommand {

    ... ...

    class VolumeCmd : public VoldCommand {

    ... ...

    class AsecCmd : public VoldCommand {

    ... ...

    class ObbCmd : public VoldCommand {

    ... ...

    class StorageCmd : public VoldCommand {

    ... ...

class FstrimCmd : public VoldCommand {

... ...

};

在这个类中,VoldCommand主要写了一个纯虚函数runCommand,声明如下:
virtual int runCommand(SocketClient *c, int argc, char **argv) = 0;
为了就是在CommandListener类中的实现,这里总共实现了6个版本的runCommand函数:

 

CommandLisenter在/system/vold/main.cpp::main()中被实例化,然后调用 cl->startListener()

我们现在讲startListener函数:

/system/core/libsysutils/src/SocketListenercpp :: startListener



int SocketListener::startListener() {

    return startListener(4);

}



int SocketListener::startListener(int backlog) {

... ...

//初始化很多变量,其中传入的backlog = 4,代表sock可以同时处理的最大连接请求数量为4。

if (mListen && listen(mSock, backlog) < 0){

... ...

//将此socket添加到client端

mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));

... ...

if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {

... ...



} 





threadStart最后调用runListener启动SocketListener:



void SocketListener::runListener() {

  While(1) {

  ... ...

    for (it = mClients->begin(); it != mClients->end(); ++it) {

         SocketClient* c = *it;

         int fd = c->getSocket();

         if (FD_ISSET(fd, &read_fds)) {

             pendingList.push_back(c);

             c->incRef();

         }

    }

  ... ...

     while (!pendingList.empty()) {

         /* Pop the first item from the list */

         it = pendingList.begin();

         SocketClient* c = *it;

         pendingList.erase(it);

         /* Process it, if false is returned, remove from list */

         if (!onDataAvailable(c)) {

             release(c, false);

         }

         c->decRef();

     }

  ... ...

  }

}

 

runListener 会在一个死循环环中一直遍历mClients队列中的socket对象,调用onDaraAvailable()函数来处理。onDataAvailable函数是类的纯虚函数,在FrameworkLisenter类中实现了该函数:

/system/core/libsysutils/src/FrameworkListenser.cpp::onDataAvailable()

bool FrameworkListener::onDataAvailable(SocketClient *c) {

   ... ...

   len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));

   ... ...

    for (i = 0; i < len; i++) {

        if (buffer[i] == '\0') {

            /* IMPORTANT: dispatchCommand() expects a zero-terminated string */

            dispatchCommand(c, buffer + offset);

            offset = i + 1;

        }

    }

    ... ...

}

 

 

onDataAvailable()函数从socket中读取数据,这些数据是一个或多个以0结尾的字符串,每个字符串都是一条命令。然后调用dispatchCommand()函数来分命令:

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {

... ...

    for (i = mCommands->begin(); i != mCommands->end(); ++i) {

        FrameworkCommand *c = *i;

        if (!strcmp(argv[0], c->getCommand())) {

            if (c->runCommand(cli, argc, argv)) {

                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));

            }

            goto out;

        }

    }

... ...

}

dispatchCommand()函数先检查命令的格式是否都正确,然后再检查mCommands中是否有和FrameworkCommand匹配的对象,然后再调用runCommand()函数,在这之前:

 

CommandListener::CommandListener() :

                 FrameworkListener("vold", true) {

    registerCmd(new DumpCmd());

    registerCmd(new VolumeCmd());

    registerCmd(new AsecCmd());

    registerCmd(new ObbCmd());

    registerCmd(new StorageCmd());

    registerCmd(new FstrimCmd());

}  

      

 

CommandListener的构造函数中,通过registerCmd()函数注册了包括 DumpCmd,VolumeCmd等多个cmd命令。             

 

3.2.2 CommandListener::runCommand()

关于CommandListener中的cmd,总共有VolumeCmd,AsecCmd,ObbCmd,StorageCmd,XwarpCmd,cryptfscmd,FstrimCmd, 共7个cmd:

 

CmdType

cmd

explain

DumpCmd

 

 

 

 

 

 

 

 

 

 

VolumeCmd

reset

 

shutdown

 

debug

 

partition(public)

 

partition(private)

 

partition(mixed)

 

mkdirs

 

user_added

 

user_removed

 

user_started

 

user_stopped

 

mount

 

unmount

 

format

 

move_storage

 

benchmark

 

forget_partition

 

StorageCmd

mountall

 

 

users

 

AsecCmd

 

 

 

3.3 NativeDaemonConnector

NativeDeamonConnector用于和底层vold进程进行Socket通信,构造函数如下:

NativeDaemonConnector(INativeDaemonConnectorCallbacks callbacks, String socket,

     int responseQueueSize, String logTag, int maxLogSize, PowerManager.WakeLock wl,

        Looper looper) {

        mCallbacks = callbacks;

        mSocket = socket;

        mResponseQueue = new ResponseQueue(responseQueueSize);

        mWakeLock = wl;

        if (mWakeLock != null) {

            mWakeLock.setReferenceCounted(true);

        }

        mLooper = looper;

        mSequenceNumber = new AtomicInteger(0);

        TAG = logTag != null ? logTag : "NativeDaemonConnector";

        mLocalLog = new LocalLog(maxLogSize);

}

其中,mCallbacks 就是MountService的引用,mSocket 这里就是传下来的“vold”字符串。

3.3.1 run()

NativeDeamonConnector对象是在MountService的构造函数中被实例化的,然后启动线程时运行run()方法:

public void run() {

    mCallbackHandler = new Handler(mLooper, this);

    while (true) {

        try {

            listenToSocket();

        } catch (Exception e) {

... ... ...

无限循环,调用listenToSocket()函数:


 

private void listenToSocket() throws IOException {

 ... ...

 //通过检查“mSocket”来确定vold对应的socket

 LocalSocketAddress address = determineSocketAddress();

 //连接vold进程中的socket

 socket.connect(address);

 ... ...

 //回调MountService中onDaemonConnecte()

 mCallbacks.onDaemonConnected();



 while (true) {

   int count = inputStream.read(buffer, start, BUFFER_SIZE - start);

      for (int i = 0; i < count; i++) {

         if (buffer[i] == 0) {//每条vold发送的消息都是已“0”结尾的字符串

           final String rawEvent = new String(buffer, start, i - start, StandardCharsets.UTF_8);

         ... ...

           try {

               //将socket传送的消息放置在event 这个rawEvent中

               final NativeDaemonEvent event = NativeDaemonEvent.parseRawEvent(

                                    rawEvent);

               log("RCV <- {" + event + "}");

               if (event.isClassUnsolicited()) {

               //通过MountService检查处理消息时是否要禁止系统休眠,消息处理完成后解除休眠

                  if (mCallbacks.onCheckHoldWakeLock(event.getCode())

                     && mWakeLock != null) {

                        mWakeLock.acquire();

                        releaseWl = true;

                   }

                   //收到的是底层的通知消息,发送给MountService

                   if (mCallbackHandler.sendMessage(mCallbackHandler.obtainMessage(

                                        event.getCode(), event.getRawEvent()))) {

                          releaseWl = false;

                       }

                    } else {

                      //如果收到的返回的结果,则放到mResponseQueue消息队列中

                      mResponseQueue.add(event.getCmdNumber(), event);

                    }

    ... .... ...                   

}

listenToSocket()在无限循环中读取到的socket数据经过parseRawEvent()方法解析成为NativeDaemonEvent对象。从Vold进程中收到的消息分为两种:

  1. vold上报的通知消息;
  2. MountService发送的命令的反馈;

以上两种可以通过isClassUnsolicited()方法区分。

    Vold上报的消息会通过mCallbackHandler发送,反馈的消息先放入mResponseQueue队列中,在NativeDaemonConnector的execute()方法结束时返回。

 

  1. MountService

MountService是systemserver起的binder服务,它的作用就是让用户进程听过它的借口对系统的存储设备进行包括mount,unmount,format等操作,在之前已经分析过了,听过CommandListener向vold进程中下发命令。

4.1 MountService的启动

在SystemServer中启动:

/frameworks/base/services/java/com/android/server/SystemServer.java


 

private static final String MOUNT_SERVICE_CLASS =

            "com.android.server.MountService$Lifecycle";

... ... ...

mSystemServiceManager.startService(MOUNT_SERVICE_CLASS);

mountService = IMountService.Stub.asInterface(ServiceManager.getService("mount"));



 

  /frameworks/base/services/core/java/com/android/server/SystemServiceManager.java


 

public <T extends SystemService> T startService(Class<T> serviceClass) {

        final String name = serviceClass.getName();

        // Create the service.

        if (!SystemService.class.isAssignableFrom(serviceClass)) {

        ... ...

        }

        final T service;

        try {

            Constructor<T> constructor = serviceClass.getConstructor(Context.class);

            //将service实例化

            service = constructor.newInstance(mContext);

        } catch (InstantiationException ex) {

        ... ...

        //将service放入自己维护的mService队列上去

        mServices.add(service);

        try {

        //调用service的onStart方法

            service.onStart();

        } catch (RuntimeException ex) {

        ... ...

        return service;

}       

 

/frameworks/base/services/core/java/com/android/server/MountService.java


   

public static class Lifecycle extends SystemService {

        private MountService mMountService;



        public Lifecycle(Context context) {

            super(context);

        }



        @Override

        public void onStart() {

            mMountService = new MountService(getContext());

            publishBinderService("mount", mMountService);

        }

 在Lifecycle类中的onStart方法中实例化MountService。publishBinderService方法会调用ServiceManager中的addService方法,通过binder

4.1.1MountService构造函数

 public MountService(Context context) {

        ... ...

        mCallbacks = new Callbacks(FgThread.get().getLooper());

      

        mPms = (PackageManagerService) ServiceManager.getService("package");

        //实例化MountServiceHandler对象,用于处理MountService的流程

        mHandler = new MountServiceHandler(hthread.getLooper());

        //实例化ObbActionHandler对象,用于处理obb的流程

        mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper());



        // Initialize the last-fstrim tracking if necessary

        File dataDir = Environment.getDataDirectory();

        File systemDir = new File(dataDir, "system");

        //  data/system/last-fstrim

        mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE);

        ... ... ...

        // mSettingsFile = data/secure/system/storage.xml

        mSettingsFile = new AtomicFile(

                new File(Environment.getSystemSecureDirectory(), "storage.xml"));



        synchronized (mLock) {

            readSettingsLocked();

        }



        LocalServices.addService(MountServiceInternal.class, mMountServiceInternal);

        //初始化mConnecter对象,用于处理MountService和vold进程的通信

        mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25,null);

        ... ...

        //初始化mCryptConnector与vdc进程的通信

        mCryptConnector = new NativeDaemonConnector(this, "cryptd",

                MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null);



        ... ...

        final IntentFilter userFilter = new IntentFilter();

        userFilter.addAction(Intent.ACTION_USER_ADDED);

        userFilter.addAction(Intent.ACTION_USER_REMOVED);

        mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler);

        addInternalVolume();

    }

            

[p15] 


   

private void addInternalVolume() {

        // Create a stub volume that represents internal storage

        final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL,

                VolumeInfo.TYPE_PRIVATE, null, null);

        internal.state = VolumeInfo.STATE_MOUNTED;

        internal.path = Environment.getDataDirectory().getAbsolutePath();

        mVolumes.put(internal.id, internal);

}   

addInternalVolume函数就是添加一个private type的volume对象。        

                                                          

 

4.1.2 onBootPhase()

由于MountService的内部Lifecycle已添加SystemServiceManager的mServices服务列表,因此系统启动到 PHASE_ACTIVITY_MANAGER_READY 时会回调mServices中的onBootPhase方法:MountService的onBootPhase()主要作用就是最终调用handleSystemReady()函数:


  

  private void handleSystemReady() {

        synchronized (mLock) {

            resetIfReadyAndConnectedLocked();

        }



        // Start scheduling nominally-daily fstrim operations

        MountServiceIdler.scheduleIdlePass(mContext);

}            

其中,比较重要的是resetIfReadyAndConnectedLocked():

private void resetIfReadyAndConnectedLocked() {

... ...

  if (mSystemReady && mDaemonConnected) {

    killMediaProvider();

    mDisks.clear();

    mVolumes.clear();

    addInternalVolume();

    try {

      mConnector.execute("volume", "reset");

      final UserManager um = mContext.getSystemService(UserManager.class);

      final List<UserInfo> users = um.getUsers();

      for (UserInfo user : users) {

          mConnector.execute("volume", "user_added", user.id, user.serialNumber);

      }

      for (int userId : mStartedUsers) {

          mConnector.execute("volume", "user_started", userId);

       }

  ... ...

 

作用:

  1. 调用killMediaProvider()函数:在MountService启动之前,启动MediaProvider时生成的database数据库可能有异常,因此在MountService初始启动(或者更新其维护的存储设备信息)后,强制杀掉MediaProvider进程,强迫其重启,在重启过程中 MediaProvider重新扫描系统的存储设备已更新database信息。

(android原生未考虑SD卡的情况,事实上如果添加的外置SD卡,因为第一次开机时ActivityManagerService处理的流程比较多,会导致com.android.media.provider 的进程实际上是在MountService发送Intent.ACTION_MEDIA_MOUNTED后才被杀掉,MediaProvider启动后就不可能接收到ACTION_MEDIA_MOUNTED)

2.重置MountService维护的:

 mDisks:存储设备

 mVolumes:卷

  1. 向vold发送reset,user_add,user_started相关的命令(对disk),这涉及到存储模块的多户用状态的问题,简单点说,vold认为每个它维护的volume对象都应该有一个对应的user,每次初始化volume对象时都需要相应的初始化其对应的user,这样才能做到volume-user之间的对应。                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             

4.1.3 onStartUser()

 同样也是SystemServiceManager运行到一定阶段后调用mService中onStartUser()方法:

private void onStartUser(int userId) {

... ...

   synchronized (mVolumes) {

      for (int i = 0; i < mVolumes.size(); i++) {

         final VolumeInfo vol = mVolumes.valueAt(i);

         if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) {

            final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false);

            mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget();

            final String envState = VolumeInfo.getEnvironmentForState(vol.getState());

            mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState);

          }

        }

        mStartedUsers = ArrayUtils.appendInt(mStartedUsers, userId);

   }

}

1遍历当前维护的所有volume对象,筛选出当前user可见的,已经正常挂载且可读的对象,通过mHandler 发送Intent.ACTION_MEDIA_MOUNTED的广播,这就是MountService的开机挂在存储设备的广播——未插入SD卡状态下,此广播仅包含内置存储设备的挂在消息;插入SD卡状态下,还包含外置SD卡正常挂载的消息。

2.通过notifyStorageStateChanged回调,通知存储设备的状态已经改变。

5.1开机流程

 



1.init.lc1860.rc中:

 调用  mount_all /fstab.leadcoreinnopower 挂载配置在/device/leadcore/lte26007/fstab.lc1860中各分区的信息,包括system,cache,userdata,kernel,modemarm等。

 

  1. init.rc 启动vold进程,vold进程会检查fstab文件中标记有voldmanaged标志的信息,记录在mDiskSources队列中。

 

  1. MountService 由SystemServer启动,在构造函数中,增加一个内部存储对象,放在维护的mVolumes队列中

 

  1. SystemServer触发AMS回调MountService::onBootPhase(),MountService对vold下发初始化用户状态的命令

 

  1. vold中VolumeManager接受到kernel层上报的NetlinkEvent::Action::kAdd uvent消息,获取eventpath,与 流程2 中mDiskSources队列中的disk对象的path进行对比,相同的代表vold进程会进行操作,调用disk->create()。

 

  1. vold中disk的create完成后会通知MountService有VOLUME_CREATED。MountService将新增的volume对象添加到维护的mVolumes队列中,并下发mount命令到vold

 

  1. vold对volume执行mount操作,上报volume 状态改变,为MEDIA_MOUNTED

 

  1. MountService改变mVolumes队列中SD卡对应vol的状态为MEDIA_MOUNTED

 

9.systemServer触发AMS回调MountService::onStartUser(),MountService对vold下发volume user_started 的命令,并遍历mVolumes队列,发送其中各volume对象的状态。

 

5.2 用户主动进行T卡操作


 

猜你喜欢

转载自blog.csdn.net/pirionFordring/article/details/83419919
今日推荐