Android7.0 Vold 进程

一、Vold简介

Vold是Volume Daemon的缩写,负责管理和控制Android平台外部存储设备,包括SD插拨、挂载、卸载、格式化等。它是通过init进程解析init.rc脚本所启动的进程.它处于Native层.

Vold的整个控制模块主要由三个类模块构成:NetlinkManager、VolumeManager和CommandListener,它们的功能划分大概是:

  1. NetlinkManager:用于从kernel中获取SD卡插拔的Uevnet消息
  2. VolumeManager:管理模块,对NetlinkManager转发的消息做一些处理,并通过CommandListener发送给framework(MountService.java);接着framework会通过套接字下发命令,指引VolumeManager对存储设备做下一步的操作,如挂载/卸载等
  3. CommandListener:通过socket,实现MountService.java与Vold之间的消息交换

 这里写图片描述

二、Vold进程的声明与创建

Vold进程的声明与创建过程跟zygote一样,在system/vold/vold.rc中声明,在init进程创建:

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
    ioprio be 2
    writepid /dev/cpuset/foreground/tasks

在创建Vold进程时,会为它创建两个socket,用于与framework进行信息交互。其他的细节,参考之前zygote进程创建的介绍。

三、Vold主程序

Vold的主程序在/system/vold目录中,直接看main.cpp::main()函数:

    ......
    VolumeManager *vm;
    CommandListener *cl;
    CryptCommandListener *ccl;
    NetlinkManager *nm;

    parse_args(argc, argv);

    sehandle = selinux_android_file_context_handle();
    if (sehandle) {
        selinux_android_set_sehandle(sehandle);
    }

    // Quickly throw a CLOEXEC on the socket we just inherited from init
    //拿到init进程创建的名为vold的socket句柄,并为它设置FD_CLOEXEC标志位
    fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
    //同上 
    fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);

    //创建/dev/block/vold目录,存放所有subdisk和sdcard的挂载点信息
    mkdir("/dev/block/vold", 0755);

    /* For when cryptfs checks and mounts an encrypted filesystem */
    klog_set_level(6);

    /* Create our singleton managers */
    //1、创建VolumeManager
    if (!(vm = VolumeManager::Instance())) {
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    //2、创建NetlinkManager
    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }

    if (property_get_bool("vold.debug", false)) {
        vm->setDebug(true);
    }

    //3、创建CommandListener、CryptCommandListener  
    cl = new CommandListener();
    ccl = new CryptCommandListener();
    vm->setBroadcaster((SocketListener *) cl);
    nm->setBroadcaster((SocketListener *) cl);

    //4、启动VolumeManager  
    if (vm->start()) {
        PLOG(ERROR) << "Unable to start VolumeManager";
        exit(1);
    }

    //5、解析Vold的配置文件fstab,初始化VolumeManager
    if (process_config(vm)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }

    //6、启动NetlinkManager,处理来自kernel的usb/sdcard插拔消息  
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }

    //7、冷启动,vold错过了一些uevent,重新触发。向/sys/block的uevent文件写入”add\n” 字符触发内核发送Uevent消息,相当执行了一次热插拔。
    coldboot("/sys/block");

    /*
     * Now that we're up, we can respond to commands
     */
    if (cl->startListener()) {
        PLOG(ERROR) << "Unable to start CommandListener";
        exit(1);
    }

    if (ccl->startListener()) {
        PLOG(ERROR) << "Unable to start CryptCommandListener";
        exit(1);
    }

    // Eventually we'll become the monitoring thread
    while(1) {
        sleep(1000);
    }

    LOG(ERROR) << "Vold exiting";
    exit(0);

第5步:

如果vold.fstab解析无误,VolueManager将创建具体的Volume,若vold.fstab解析不存在或者打开失败,Vold将会读取Linux内核中的参数,此时如果参数中存在SDCARD(也就是SD的默认路径),VolumeManager则会创建AutoVolume,如果不存在这个默认路径那么就不会创建。

它的格式对应如下:

type———————–挂载命令
lable———————–标签
mount_point ————挂载点
part ———————–第几个分区
sysfs_path—————设备的sysfs paths

sysfs_path可以有多个 part指定分区个数,如果是auto没有分区

第7步:

coldboot方法会调用do_coldboot方法,往/sys/block目录写入add\n事件。

static void do_coldboot(DIR *d, int lvl) {
    struct dirent *de;
    int dfd, fd;

    dfd = dirfd(d);

    fd = openat(dfd, "uevent", O_WRONLY | O_CLOEXEC);
    if(fd >= 0) {
        //写入add\n事件
        write(fd, "add\n", 4);
        close(fd);
    }

从代码中的注释可知,Vold主要创了三个对象:NetlinkManager、VolumeManager和CommandListener。根据Vold的架构图,现分别对它们的创建及启动过程进行分析。

3.1 NetlinkManager

主要的处理过程:

  1. nm = NetlinkManager::Instance()
  2. nm->setBroadcaster((SocketListener *) cl)
  3. nm->start()

现按步骤进行分析。

NetlinkManager::Instance():

NetlinkManager *NetlinkManager::sInstance = NULL;

NetlinkManager *NetlinkManager::Instance() {
    if (!sInstance)
        sInstance = new NetlinkManager();
    return sInstance;
}

NetlinkManager::NetlinkManager() {
    mBroadcaster = NULL;
}

NetlinkManager::~NetlinkManager() {
}

这里使用了单例模式来构建NetlinkManager对象,构造函数中只是简单地初始化了成员变量。

NetlinkManager::setBroadcaster():

NetlinkManager.h:    void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

setBroadcaster()函数也很简单,为mBroadcast进行赋值。

NetlinkManager::start():

int NetlinkManager::start() {
    struct sockaddr_nl nladdr;
    //int sz = 64 * 1024;
        int sz = 1024 * 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) {
        SLOGE("Unable to create uevent socket: %s", strerror(errno));
        return -1;
    }

    if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
        SLOGE("Unable to set uevent socket SO_RCVBUFFORCE option: %s", strerror(errno));
        goto out;
    }

    if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno));
        goto out;
    }

    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        SLOGE("Unable to bind uevent socket: %s", strerror(errno));
        goto out;
    }

    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
        goto out;
    }

    return 0;

out:
    close(mSock);
    return -1;
}

        start()方法中创建了一个句柄值为mSock的套接字,用来和kernel通信;而实际具体的socket信息交互是由NetlinkHandler处理的。

NetlinkHandler的实现有一套继承机制,其实际继承关系如图所示:

按照继承关系,分析它的构建过程:

NetlinkHandler::NetlinkHandler(int listenerSocket) :
                NetlinkListener(listenerSocket) {
}

 下面是其父类构建函数

NetlinkListener::NetlinkListener(int socket) :  
                            SocketListener(socket, false) {  
    mFormat = NETLINK_FORMAT_ASCII;  
}  
SocketListener::SocketListener(const char *socketName, bool listen) {  
    init(socketName, -1, listen, false);  
}  

void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
    mListen = listen;//是否是监听端,这里为false  
    mSocketName = socketName;//保存socket的名字  
    mSock = socketFd;//保存socket的句柄值,与Kernel通信  
    mUseCmdNum = useCmdNum;  
    pthread_mutex_init(&mClientsLock, NULL);  
    mClients = new SocketClientCollection();//集合对象,保存类型SocketClient为的变量;保存了socket通信中的客户端对象  
}  

再看NetlinkHandler::start()方法

int NetlinkHandler::start() {  
    return this->startListener();  
} 

实际调用的是SocketListener::startListener():

int SocketListener::startListener() {  
    return startListener(4);  
}  
  
int SocketListener::startListener(int backlog) {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
        SLOGV("got mSock = %d for %s", mSock, mSocketName);  
        fcntl(mSock, F_SETFD, FD_CLOEXEC);  
    }  
  
    if (mListen && listen(mSock, backlog) < 0) {//如果mListen为true,则监听该socket;表明此socket应该是服务端  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)//实际传入的mListen值为false,走此分支  
        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));//创建一个SocketClient对象,并保存到集合中  
  
    if (pipe(mCtrlPipe)) {  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建一个线程,在其中调用threadStart(),根据mListen值,等待接收来自Kernel的Uevent消息  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
} 

创建一个SocketListener对象,并加入mClients中;随后,创建一个线程,并调用SocketListener::threadStart():

void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  
    me->runListener();  
    pthread_exit(NULL);  
    return NULL;  
}  
  
void SocketListener::runListener() {  
  
    SocketClientCollection pendingList;  
  
    while(1) {  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds);  
  
        if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中  
            max = mSock;  
            FD_SET(mSock, &read_fds);  
        }  
  
        FD_SET(mCtrlPipe[0], &read_fds);  
        if (mCtrlPipe[0] > max)  
            max = mCtrlPipe[0];  
  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            // NB: calling out to an other object with mClientsLock held (safe)  
            int fd = (*it)->getSocket();  
            FD_SET(fd, &read_fds);  
            if (fd > max) {  
                max = fd;  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
        SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果监测到read_fds集合中有socket可读  
            if (errno == EINTR)  
                continue;  
            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
            sleep(1);  
            continue;  
        } else if (!rc)  
            continue;  
  
        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {  
            char c = CtrlPipe_Shutdown;  
            TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));  
            if (c == CtrlPipe_Shutdown) {  
                break;  
            }  
            continue;  
        }  
        if (mListen && FD_ISSET(mSock, &read_fds)) {//监听端客户端连接请求  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen);//接受client的连接请求,c是代表client套接字的文件描述符  
                SLOGV("%s got %d from accept", mSocketName, c);  
            } while (c < 0 && errno == EINTR);  
            if (c < 0) {  
                SLOGE("accept failed (%s)", strerror(errno));  
                sleep(1);  
                continue;  
            }  
            fcntl(c, F_SETFD, FD_CLOEXEC);  
            pthread_mutex_lock(&mClientsLock);  
            mClients->push_back(new SocketClient(c, true, mUseCmdNum));//根据c,创建一个SocketListener对象,并加入到队列中  
            pthread_mutex_unlock(&mClientsLock);  
        }  
  
        /* Add all active clients to the pending list first */  
        pendingList.clear();  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            SocketClient* c = *it;  
            // NB: calling out to an other object with mClientsLock held (safe)  
            int fd = c->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {//遍历所有保存的客户端;如果该socket可读,将该套接字加入到队列中  
                pendingList.push_back(c);  
                c->incRef();  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
  
        /* Process the pending list, since it is owned by the thread, 
         * there is no need to lock it */  
        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)) {//客户端收到数据,调用NetlinkListener::onDataAvailable()处理  
                release(c, false);//数据处理失败,则释放socket资源  
            }  
            c->decRef();  
        }  
    }  
}

我们初始化NetlinkListener时传入的mListener值为false;上述代码中,会遍历所有保存的客户端socket,如果收到数据,则进行处理。

调用NetlinkListener::onDataAvailable():

bool NetlinkListener::onDataAvailable(SocketClient *cli)  
{  
    int socket = cli->getSocket();  
    ssize_t count;  
    uid_t uid = -1;  
  
    bool require_group = true;  
    if (mFormat == NETLINK_FORMAT_BINARY_UNICAST) {  
        require_group = false;  
    }  
  
    count = TEMP_FAILURE_RETRY(uevent_kernel_recv(socket,  
            mBuffer, sizeof(mBuffer), require_group, &uid));//从kernel获取Uevent消息,保存到mBuffer中  
    if (count < 0) {  
        if (uid > 0)  
            LOG_EVENT_INT(65537, uid);  
        SLOGE("recvmsg failed (%s)", strerror(errno));  
        return false;//读取失败,则返回false,上层调用则会关闭socket资源  
    }  
  
    NetlinkEvent *evt = new NetlinkEvent();//事件的代码封装  
    if (evt->decode(mBuffer, count, mFormat)) {解析Uevent数据,填充到NetlinkEvent对象中  
        onEvent(evt);//NetlinkHandler::onEvent()  
    } else if (mFormat != NETLINK_FORMAT_BINARY) {  
        // Don't complain if parseBinaryNetlinkMessage returns false. That can  
        // just mean that the buffer contained no messages we're interested in.  
        SLOGE("Error decoding NetlinkEvent");  
    }  
  
    delete evt;  
    return true;  
} 

先通过socket获取到Uevent数据,再解析并将信息封装到NetlinkEvent对象中。NetlinkHandler::onEvent()分发处理该对象:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {  
    VolumeManager *vm = VolumeManager::Instance();  
    const char *subsys = evt->getSubsystem();  
  
    if (!subsys) {//如果事件和外部存储设备无关,则不处理  
        SLOGW("No subsystem found in netlink event");  
        return;  
    }  
  
    if (!strcmp(subsys, "block")) {//如果Uevent是block子系统  
        vm->handleBlockEvent(evt);//进入VolumeManager中处理;此处和VolumeManager进行交互  
    }  
}  

如果事件是和外部存储有关,则调用VolumeManager::handleBlockEvent()处理该事件;这里,就看到了NetlinkManager和VolumeManager之间进行数据流动的处理了。

3.2 VolumeManager

old使用VolumeManager的过程和NetlinkManager类似,也是三步:

  1. vm= VolumeManager::Instance()
  2. vm->setBroadcaster((SocketListener *) cl)
  3. vm->start()

1、2步与NetlinkManager的处理类似

直接看VolumeManager::start()的处理:

int VolumeManager::start() {  
    // Always start from a clean slate by unmounting everything in  
    // directories that we own, in case we crashed.  
    unmountAll();//在处理外部设备事件之前,先重置所有状态  
  
    // Assume that we always have an emulated volume on internal  
    // storage; the framework will decide if it should be mounted.  
    CHECK(mInternalEmulated == nullptr);  
    mInternalEmulated = std::shared_ptr<android::vold::VolumeBase>(  
            new android::vold::EmulatedVolume("/data/media"));  
    mInternalEmulated->create();//预先设定/data/media,由framework决定是否mount;EmulatedVolume和VolumeBase之间是继承关系,代表不同类型的Volume  
  
    return 0;  
}  

再直接看NetlinkManager和VolumeManager之间信息处理的调用过程:

void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {  
    std::lock_guard<std::mutex> lock(mLock);  
  
    if (mDebug) {  
        LOG(VERBOSE) << "----------------";  
        LOG(VERBOSE) << "handleBlockEvent with action " << (int) evt->getAction();  
        evt->dump();  
    }  
  
    std::string eventPath(evt->findParam("DEVPATH"));//设备路径  
    std::string devType(evt->findParam("DEVTYPE"));//设备类型  
  
    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)) {  
                // For now, assume that MMC devices are SD, and that  
                // everything else is USB  
                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对象,表示一个检测到的物理设备  
                disk->create();//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;  
    }  
    }  
}  

向上层发送各种类型的消息都是通过notifyEvent()函数处理的,其实际就是通过socket来发送的。

同时,Vold的主函数中还有一个重要的函数调用process_config():

static int process_config(VolumeManager *vm) {  
    std::string path(android::vold::DefaultFstabPath()); //获取到vold.fstab文件路径  
    fstab = fs_mgr_read_fstab(path.c_str());//解析.fstab文件,并返回封装的fstab对象  
    if (!fstab) {  
        PLOG(ERROR) << "Failed to open default fstab " << path;  
        return -1;  
    }  
  
    /* Loop through entries looking for ones that vold manages */  
    bool has_adoptable = false;  
    for (int i = 0; i < fstab->num_entries; i++) {  
        if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {  
            if (fs_mgr_is_nonremovable(&fstab->recs[i])) {  
                LOG(WARNING) << "nonremovable no longer supported; ignoring volume";  
                continue;  
            }  
  
            std::string sysPattern(fstab->recs[i].blk_device);  
            std::string nickname(fstab->recs[i].label);  
            int flags = 0;  
  
            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;//is Primary?  
            }  
  
            vm->addDiskSource(std::shared_ptr<VolumeManager::DiskSource>(  
                    new VolumeManager::DiskSource(sysPattern, nickname, flags)));//添加一个VolumeManager::DiskSource对象,保存了一些信息  
        }  
    }  
    property_set("vold.has_adoptable", has_adoptable ? "1" : "0");  
    return 0;  
}
struct fstab *fs_mgr_read_fstab(const char *fstab_path)  
{  
    FILE *fstab_file;  
    int cnt, entries;  
    ssize_t len;  
    size_t alloc_len = 0;  
    char *line = NULL;  
    const char *delim = " \t";  
    char *save_ptr, *p;  
    struct fstab *fstab = NULL;  
    struct fs_mgr_flag_values flag_vals;  
#define FS_OPTIONS_LEN 1024  
    char tmp_fs_options[FS_OPTIONS_LEN];  
  
    fstab_file = fopen(fstab_path, "r");  
    if (!fstab_file) {  
        ERROR("Cannot open file %s\n", fstab_path);  
        return 0;  
    }  
  
    entries = 0;  
    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {  
        /* if the last character is a newline, shorten the string by 1 byte */  
        if (line[len - 1] == '\n') {  
            line[len - 1] = '\0';  
        }  
        /* Skip any leading whitespace */  
        p = line;  
        while (isspace(*p)) {  
            p++;  
        }  
        /* ignore comments or empty lines */  
        if (*p == '#' || *p == '\0')  
            continue;  
        entries++;  
    }  
  
    if (!entries) {  
        ERROR("No entries found in fstab\n");  
        goto err;  
    }  
  
    /* Allocate and init the fstab structure */  
    fstab = calloc(1, sizeof(struct fstab));  
    fstab->num_entries = entries;  
    fstab->fstab_filename = strdup(fstab_path);  
    fstab->recs = calloc(fstab->num_entries, sizeof(struct fstab_rec));  
  
    fseek(fstab_file, 0, SEEK_SET);  
  
    cnt = 0;  
    //解析fstab中每行的内容,并进行封装  
    while ((len = getline(&line, &alloc_len, fstab_file)) != -1) {  
        /* if the last character is a newline, shorten the string by 1 byte */  
        if (line[len - 1] == '\n') {  
            line[len - 1] = '\0';  
        }  
  
        /* Skip any leading whitespace */  
        p = line;  
        while (isspace(*p)) {  
            p++;  
        }  
        /* ignore comments or empty lines */  
        if (*p == '#' || *p == '\0')  
            continue;  
  
        /* If a non-comment entry is greater than the size we allocated, give an 
         * error and quit.  This can happen in the unlikely case the file changes 
         * between the two reads. 
         */  
        if (cnt >= entries) {  
            ERROR("Tried to process more entries than counted\n");  
            break;  
        }  
  
        if (!(p = strtok_r(line, delim, &save_ptr))) {  
            ERROR("Error parsing mount source\n");  
            goto err;  
        }  
        fstab->recs[cnt].blk_device = strdup(p);  
  
        if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
            ERROR("Error parsing mount_point\n");  
            goto err;  
        }  
        fstab->recs[cnt].mount_point = strdup(p);//mount的位置  
  
        if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
            ERROR("Error parsing fs_type\n");  
            goto err;  
        }  
        fstab->recs[cnt].fs_type = strdup(p);  
  
        if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
            ERROR("Error parsing mount_flags\n");  
            goto err;  
        }  
        tmp_fs_options[0] = '\0';  
        fstab->recs[cnt].flags = parse_flags(p, mount_flags, NULL,  
                                       tmp_fs_options, FS_OPTIONS_LEN);  
  
        /* fs_options are optional */  
        if (tmp_fs_options[0]) {  
            fstab->recs[cnt].fs_options = strdup(tmp_fs_options);  
        } else {  
            fstab->recs[cnt].fs_options = NULL;  
        }  
  
        if (!(p = strtok_r(NULL, delim, &save_ptr))) {  
            ERROR("Error parsing fs_mgr_options\n");  
            goto err;  
        }  
        fstab->recs[cnt].fs_mgr_flags = parse_flags(p, fs_mgr_flags,  
                                                    &flag_vals, NULL, 0);  
        fstab->recs[cnt].key_loc = flag_vals.key_loc;  
        fstab->recs[cnt].verity_loc = flag_vals.verity_loc;  
        fstab->recs[cnt].length = flag_vals.part_length;  
        fstab->recs[cnt].label = flag_vals.label;  
        fstab->recs[cnt].partnum = flag_vals.partnum;  
        fstab->recs[cnt].swap_prio = flag_vals.swap_prio;  
        fstab->recs[cnt].zram_size = flag_vals.zram_size;  
        cnt++;  
    }  
    fclose(fstab_file);  
    free(line);  
    return fstab;  
  
err:  
    fclose(fstab_file);  
    free(line);  
    if (fstab)  
        fs_mgr_free_fstab(fstab);  
    return NULL;  
}  

fstab文件是Linux下配置分区的一个文件,解析fstab文件后,会根据配置信息创建DiskSource对象,加入到VolumeManager::mDiskSource中。

3.3CommandListener

VolumeManager要想向MountService发送消息,就要借助CommandListener。CommandListener有一个较为复杂的继承关系:

 CommandListener的创建过程跟NetlinkManager类似:

CommandListener::CommandListener() :  
                 FrameworkListener("vold", true) {
    //vold是socket名称,init.rc文件中声明的一个socket资源,用于和framework通信  
    registerCmd(new DumpCmd());//注册不同的命令对象,保存到mCommands成员中;同时,创建Cmd对象时,会保存一个字符串标识(一般是上层下发命令中的第一个字符串),用于后续区分不同的命令  
    registerCmd(new VolumeCmd());//标识:volume  
    registerCmd(new AsecCmd());//标识:asec  
    registerCmd(new ObbCmd());//标识:obb  
    registerCmd(new StorageCmd());//标识:storage  
    registerCmd(new FstrimCmd());//标识:fstrim  
}  
FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :  
                            SocketListener(socketName, true, withSeq) {  
    init(socketName, withSeq);  
}
SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {  
    init(socketName, -1, listen, useCmdNum);  
}

void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {  
    mListen = listen;//是否是监听端,与前面不同,这里为true  
    mSocketName = socketName;//保存socket的名字"vold",与MountService通信  
    mSock = socketFd;//保存socket的句柄值  
    mUseCmdNum = useCmdNum;  
    pthread_mutex_init(&mClientsLock, NULL);  
    mClients = new SocketClientCollection();//集合对象  
}

下面直接看CommandListener->startListener():

int SocketListener::startListener() {  
    return startListener(4);  
}  
  
int SocketListener::startListener(int backlog) {  
  
    if (!mSocketName && mSock == -1) {  
        SLOGE("Failed to start unbound listener");  
        errno = EINVAL;  
        return -1;  
    } else if (mSocketName) {  
        if ((mSock = android_get_control_socket(mSocketName)) < 0) {//名为"vold"的socket的句柄值  
            SLOGE("Obtaining file descriptor socket '%s' failed: %s",  
                 mSocketName, strerror(errno));  
            return -1;  
        }  
        SLOGV("got mSock = %d for %s", mSock, mSocketName);  
        fcntl(mSock, F_SETFD, FD_CLOEXEC);  
    }  
  
    if (mListen && listen(mSock, backlog) < 0) {//mListener为true,则监听该socket  
        SLOGE("Unable to listen on socket (%s)", strerror(errno));  
        return -1;  
    } else if (!mListen)//mListener为false,走此分支  
        mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));  
  
    if (pipe(mCtrlPipe)) {  
        SLOGE("pipe failed (%s)", strerror(errno));  
        return -1;  
    }  
  
    if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {//创建一个线程,在其中调用threadStart(),并在mSock代表的套接字上等待客户端的连接请求  
        SLOGE("pthread_create (%s)", strerror(errno));  
        return -1;  
    }  
  
    return 0;  
}

在CommandListener监听流程中,mListene为true;表示这一端是监听侧,等待Client的连接请求。这种场景下,MountService就是这里描述的客户端。MountService在创建过程,会通过创建NativeDaemonConnector对象,去连接名为"vold"的socket,这样两者就可以通信了。

void *SocketListener::threadStart(void *obj) {  
    SocketListener *me = reinterpret_cast<SocketListener *>(obj);  
  
    me->runListener();  
    pthread_exit(NULL);  
    return NULL;  
}  
  
void SocketListener::runListener() {  
  
    SocketClientCollection pendingList;  
  
    while(1) {  
        SocketClientCollection::iterator it;  
        fd_set read_fds;  
        int rc = 0;  
        int max = -1;  
  
        FD_ZERO(&read_fds);  
  
        if (mListen) {//如果我们是服务端,则将该socket的套接字加入到可读监控队列中  
            max = mSock;  
            FD_SET(mSock, &read_fds);  
        }  
  
        FD_SET(mCtrlPipe[0], &read_fds);  
        if (mCtrlPipe[0] > max)  
            max = mCtrlPipe[0];  
  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            // NB: calling out to an other object with mClientsLock held (safe)  
            int fd = (*it)->getSocket();  
            FD_SET(fd, &read_fds);  
            if (fd > max) {  
                max = fd;  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
        SLOGV("mListen=%d, max=%d, mSocketName=%s", mListen, max, mSocketName);  
        if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {//如果集合read_fds中有socket可读  
            if (errno == EINTR)监测  
                continue;  
            SLOGE("select failed (%s) mListen=%d, max=%d", strerror(errno), mListen, max);  
            sleep(1);  
            continue;  
        } else if (!rc)  
            continue;  
  
        if (FD_ISSET(mCtrlPipe[0], &read_fds)) {  
            char c = CtrlPipe_Shutdown;  
            TEMP_FAILURE_RETRY(read(mCtrlPipe[0], &c, 1));  
            if (c == CtrlPipe_Shutdown) {  
                break;  
            }  
            continue;  
        }  
        if (mListen && FD_ISSET(mSock, &read_fds)) {//mListener值实际为true;服务端,等待客户端连接请求  
            struct sockaddr addr;  
            socklen_t alen;  
            int c;  
  
            do {  
                alen = sizeof(addr);  
                c = accept(mSock, &addr, &alen);//接受MountService发起的socket连接请求,  
                SLOGV("%s got %d from accept", mSocketName, c);  
            } while (c < 0 && errno == EINTR);  
            if (c < 0) {  
                SLOGE("accept failed (%s)", strerror(errno));  
                sleep(1);  
                continue;  
            }  
            fcntl(c, F_SETFD, FD_CLOEXEC);  
            pthread_mutex_lock(&mClientsLock);  
            mClients->push_back(new SocketClient(c, true, mUseCmdNum));//根据c,创建一个SocketListener对象,并加入到集合中  
            pthread_mutex_unlock(&mClientsLock);  
        }  
  
        /* Add all active clients to the pending list first */  
        pendingList.clear();  
        pthread_mutex_lock(&mClientsLock);  
        for (it = mClients->begin(); it != mClients->end(); ++it) {  
            SocketClient* c = *it;  
            // NB: calling out to an other object with mClientsLock held (safe)  
            int fd = c->getSocket();  
            if (FD_ISSET(fd, &read_fds)) {//遍历保存的所有客户端socket,如果对应的socket可读,则将该套接字加入到队列中  
                pendingList.push_back(c);  
                c->incRef();  
            }  
        }  
        pthread_mutex_unlock(&mClientsLock);  
  
        /* Process the pending list, since it is owned by the thread, 
         * there is no need to lock it */  
        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)) {//有数据来,调用FrameworkListener::onDataAvailable()处理  
                release(c, false);  
            }  
            c->decRef();  
        }  
    }  
} 

由于mListen值的变化(此时为true),处理流程有所不同。首先作为服务端,会等待Client的连接请求;如果有连接请求,并有数据发送过来,则通过
onDataAvailable()处理。根据继承关系,此处调用FrameworkListener::onDataAvailable():

bool FrameworkListener::onDataAvailable(SocketClient *c) {  
    char buffer[CMD_BUF_SIZE];  
    int len;  
  
    len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));//从MountService接收指令数据,存入buffer中  
    if (len < 0) {  
        SLOGE("read() failed (%s)", strerror(errno));  
        return false;  
    } else if (!len)  
        return false;  
   if(buffer[len-1] != '\0')  
        SLOGW("String is not zero-terminated");  
  
    int offset = 0;  
    int i;  
  
    for (i = 0; i < len; i++) {  
        if (buffer[i] == '\0') {  
            /* IMPORTANT: dispatchCommand() expects a zero-terminated string */  
            dispatchCommand(c, buffer + offset);//命令分发处理  
            offset = i + 1;  
        }  
    }  
  
    return true;  
}  

如果消息不为空,则调用FrameworkListener::dispatchCommand()进行处理:

void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {  
    FrameworkCommandCollection::iterator i;  
    int argc = 0;  
    char *argv[FrameworkListener::CMD_ARGS_MAX];  
    char tmp[CMD_BUF_SIZE];  
    char *p = data;  
    char *q = tmp;  
    char *qlimit = tmp + sizeof(tmp) - 1;  
    bool esc = false;  
    bool quote = false;  
    bool haveCmdNum = !mWithSeq;  
  
    memset(argv, 0, sizeof(argv));  
    memset(tmp, 0, sizeof(tmp));  
    while(*p) {  
        if (*p == '\\') {  
            if (esc) {  
                if (q >= qlimit)  
                    goto overflow;  
                *q++ = '\\';  
                esc = false;  
            } else  
                esc = true;  
            p++;  
            continue;  
        } else if (esc) {  
            if (*p == '"') {  
                if (q >= qlimit)  
                    goto overflow;  
                *q++ = '"';  
            } else if (*p == '\\') {  
                if (q >= qlimit)  
                    goto overflow;  
                *q++ = '\\';  
            } else {  
                cli->sendMsg(500, "Unsupported escape sequence", false);  
                goto out;  
            }  
            p++;  
            esc = false;  
            continue;  
        }  
  
        if (*p == '"') {  
            if (quote)  
                quote = false;  
            else  
                quote = true;  
            p++;  
            continue;  
        }  
  
        if (q >= qlimit)  
            goto overflow;  
        *q = *p++;  
        if (!quote && *q == ' ') {  
            *q = '\0';  
            if (!haveCmdNum) {  
                char *endptr;  
                int cmdNum = (int)strtol(tmp, &endptr, 0);  
                if (endptr == NULL || *endptr != '\0') {  
                    cli->sendMsg(500, "Invalid sequence number", false);  
                    goto out;  
                }  
                cli->setCmdNum(cmdNum);  
                haveCmdNum = true;  
            } else {  
                if (argc >= CMD_ARGS_MAX)  
                    goto overflow;  
                argv[argc++] = strdup(tmp);  
            }  
            memset(tmp, 0, sizeof(tmp));  
            q = tmp;  
            continue;  
        }  
        q++;  
    }  
  
    *q = '\0';  
    if (argc >= CMD_ARGS_MAX)  
        goto overflow;  
    argv[argc++] = strdup(tmp);  
#if 0  
    for (int k = 0; k < argc; k++) {  
        SLOGD("arg[%d] = '%s'", k, argv[k]);  
    }  
#endif  
  
    if (quote) {  
        cli->sendMsg(500, "Unclosed quotes error", false);  
        goto out;  
    }  
  
    if (errorRate && (++mCommandCount % errorRate == 0)) {  
        /* ignore this command - let the timeout handler handle it */  
        SLOGE("Faking a timeout");  
        goto out;  
    }  
  
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {  
        FrameworkCommand *c = *i;  
  
        if (!strcmp(argv[0], c->getCommand())) {//获取命令的第一个参数(即标识),遍历mCommands,找到符合的Command对象去执行runCommand();例如,如果标识是volume,则执行VolumeCommand的runCommand()函数处理下发的指令  
            if (c->runCommand(cli, argc, argv)) {//重要,调用不同Command类型的、我们之前注册过的对象调用runCommand()方法处理指令(定义在CommandListener中)  
                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));  
            }  
            goto out;  
        }  
    }  
    cli->sendMsg(500, "Command not recognized", false);  
out:  
    int j;  
    for (j = 0; j < argc; j++)  
        free(argv[j]);  
    return;  
  
overflow:  
    LOG_EVENT_INT(78001, cli->getUid());  
    cli->sendMsg(500, "Command too long", false);  
    goto out;  

首先,根据上层下发的指令中的信息去得到一个符合要求的Command命令对象,然后执行相应的runCommand()方法来处理不同的指令。我们下发的指令有一定的规则,一般第一个字符串是标识,用以获得不同的Command对象;第二个参数一般是我们需要进行的操作命令;后续的参数一般都是下发的用以完成操作的数据。一般情况,格式类似于:

volume mount /mnt/sda/sda1

字符串之间以空格分开。

到此,Vold机制及原理的分析就基本结束了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒羊羊的南瓜屋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值