一、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,略去部分代码
............