Android 7.0 Vold工作流程

一、Vold工作机制

Vold是Volume Daemon的缩写,它是Android平台中外部存储系统的管控中心,是管理和控制Android平台外部存储设备的后台进程。其功能主要包括:SD卡的插拔事件检测、SD卡挂载、卸载、格式化等。

如上图所示,Vold中的NetlinkManager模块接收来自Linux Kernel的uevent消息。
NetlinkManager将这些消息转发给VolumeManager模块。VolumeManager会对应做一些操作,然后把相关信息通过CommandListener发送给MountService。
MountService根据收到的消息后,根据情况会利用CommandListener发送相关的处理命令给VolumeManager做进一步处理。
CommandListener模块内部封装了一个Socket用于跨进程通信。它一方面接收来自MountService的控制命令,另一方面VolumeManager通过它将消息发送给MountService。

Tips:
Netlink是Linux系统中用户空间进程和Kernel进行通信的一种机制,是基于Socket的异步通信机制。
通过这种机制,位于用户空间的进程可以接收来自Kernel的一些信息,同时用户空间进程也可以利用Netlink向Kernel发送一些控制命令。

二、Vold进程启动过程
Vold进程启动文件定义于system/vold/vold.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
    ioprio be 2
    writepid /dev/cpuset/foreground/tasks

被init进程启动后,将调用system/vold/main.cpp中的main函数:

int main(int argc, char** argv) {
    .............
    VolumeManager *vm;
    CommandListener *cl;
    .............
    NetlinkManager *nm;

    //解析vold.rc中定义的blkid和fsck相关的参数
    parse_args(argc, argv);
    ..............
    // Quickly throw a CLOEXEC on the socket we just inherited from init
    //这里的含义不是很明白
    //对于fcntl(fd, F_SETFD, FD_CLOEXEC)函数
    //FD_CLOEXEC表示当程序执行exec函数时, fd将被系统自动关闭, 不传递给exec创建的新进程,以免fd在子进程中仍然有效
    //但在init进程中,是先fork出子进程,然后在进程中创建出socket,才执行exec函数,有必要使用fcntl函数么?
    fcntl(android_get_control_socket("vold"), F_SETFD, FD_CLOEXEC);
    fcntl(android_get_control_socket("cryptd"), F_SETFD, FD_CLOEXEC);

    //创建文件夹/dev/block/vold
    mkdir("/dev/block/vold", 0755);
    .........
    //创建VolumeManager
    if (!(vm = VolumeManager::Instance())) {
        LOG(ERROR) << "Unable to create VolumeManager";
        exit(1);
    }

    //创建NetlinkManager
    if (!(nm = NetlinkManager::Instance())) {
        LOG(ERROR) << "Unable to create NetlinkManager";
        exit(1);
    }
    ...................
    //创建CommandListener
    cl = new CommandListener();
    ............
    vm->setBroadcaster((SocketListener *) cl);
    nm->setBroadcaster((SocketListener *) cl);

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

    //根据配置文件初始化VolumeManager
    if (process_config(vm)) {
        PLOG(ERROR) << "Error reading configuration... continuing anyways";
    }

    //启动NetlinkManager
    if (nm->start()) {
        PLOG(ERROR) << "Unable to start NetlinkManager";
        exit(1);
    }

    //与ueventd进程进行冷启动类似,此处通过往/sys/block目录下对应的uevent文件写"add\n"来触发内核发送uevent消息
    coldboot("/sys/block");

    //启动CommandListener
    if (cl->startListener()) {
        PLOG(ERROR) << "Unable to start CommandListener";
        exit(1);
    }
    .......
    // Eventually we'll become the monitoring thread
    while(1) {
        sleep(1000);
    }

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

从上面的代码不难看出,Vold进程的main函数中,创建并启动其子模块VolumeManager、NetlinkManager和CommandListener后,就不再执行实际的工作了。
以后Vold进程具体的工作就会交付给子模块进行处理。

三、Vold进程中各模块分析
为了进一步了解整个Vold进程的主要工作流程,接下来我们分析一下其主要模块的工作流程。

1、NetlinkManager模块

1.1 NetlinkManager的创建和启动
在Vold的main函数中,调用NetlinkManager::Instance创建出NetlinkManager:

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

//mBroadcaster的类型为SocketListener
NetlinkManager::NetlinkManager() {
    mBroadcaster = NULL;
}

从上面的代码可以看到,NetlinkManager的创建比较简单。

在创建出NetlinkManager后,Vold调用了NetlinkManager的setBroadcaster函数:

void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }

依然言简意赅。
这里唯一需要说明的是,Android这里的设计看起来比较很奇怪,虽然NetlinkManager设置了CommandListener对象,但它并没有通过CommandListener发送消息和接收命令。

配置好NetlinkManager后,Vold就调用了NetlinkManger的start函数:

int NetlinkManager::start() {
    //以下定义并初始化socket的地址结构
    struct sockaddr_nl nladdr;
    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;

    //创建PF_NETLINK地址簇的socket,NETLINK_KOBJECT_UEVENT表示该socket将接收内核的Uevent事件
    if ((mSock = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC,
            NETLINK_KOBJECT_UEVENT)) < 0) {
        SLOGE("Unable to create uevent socket: %s", strerror(errno));
        return -1;
    }

    //setsockopt设置socket的选项,此处设置socket的接收缓冲区大小为64 * 1024
    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;
    }

    //将创建出的socket绑定到之前的地址上,此时socket可以收到Kernel的数据了
    if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
        SLOGE("Unable to bind uevent socket: %s", strerror(errno));
        goto out;
    }

    //创建并启动一个NetlinkHandler
    mHandler = new NetlinkHandler(mSock);
    if (mHandler->start()) {
        SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
        goto out;
    }

    return 0;

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

通过上面的代码不难看出,其实NetlinkManager启动后就是创建一个可以接收Kernel消息的socket,并以此socket构建并启动NetlinkHandler。
可以预见NetlinkHandler将用来处理socket收到的信息。

1.2 NetlinkHandler

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

NetlinkHandler初始化时,将与Kernel通信的socket描述符传入到父类NetlinkListener中。

NetlinkListener::NetlinkListener(int socket) :
                          SocketListener(socket, false) {
    mFormat = NETLINK_FORMAT_ASCII;
}

NetlinkListener又进一步调用其父类SocketListener:

SocketListener::SocketListener(int socketFd, bool listen) {
    init(NULL, socketFd, listen, false);
}

//socektName为null, listen和useCmdNum的值均为false
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
    mListen = listen;
    mSocketName = socketName;
    mSock = socketFd;
    mUseCmdNum = useCmdNum;
    //初始化一个mutex
    pthread_mutex_init(&mClientsLock, NULL);

    //SocketClientCollection用于存储与Socket服务端通信的客户端
    mClients = new SocketClientCollection();
}

从上面的代码可以看出,NetlinkHandler对应的继承体系如下图所示:

创建完NetlinkHandler后,NetlinkManager调用了NetlinkHandler的start方法:

int NetlinkHandler::start() {
    //根据继承体系,实际上调用了SocketListener的startListenr函数
    return this->startListener();
}

int SocketListener::startListener() {
    return startListener(4);
}

int SocketListener::startListener(int backlog) {
    //前面代码已经提及,构造NetlinkHandler时,mSocketName为null,略去部分代码
    ............
  • 11
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值