本文简要介绍Android Gingerbread中Vold的启动过程。根据网上的资料和code改写了一下。
1 Vold
Vold的全称是volume daemon。主要负责系统对大容量存储设备(USB/SD)的挂载/卸载任务,它是一个守护进程,该进程支持这些存储外设的热插拔。自Android 2.2开始,Vold升级为vold 2.0,之前的配置文件路径在system/etc/vold.conf,Android 2.3之后变为system/etc/vold.fstab,目前的vold版本为2.1。
2 Vold工作流程
Vold的工作流程大致可以分为三个部分:创建监听、引导、事件处理。
(1)创建监听
创建监听指的是创建监听链接,一方面用于监听来自内核的uevent,另一方面用于监听来自上层的控制命令,这些命令包括控制SD卡的挂载与卸载,这里所说的链接也就是Socket。在Android 系统启动的时候,init进程会去解析init.rc文件,在该文件中,有如下代码:
Service vold /system/bin/vold
Socket vold stream 0660 root mount
Iprio be 2
这样系统会在启动的时候创建与上层通信的Socket。
在Android 2.3源码/system/vold路径下的main.cpp中创建了与内核通信的Socket。在main.cpp中通过实例化VolumeManager和NetlinkManager时创建。
(2)引导
Vold进程启动时候会对现有的外部存储设备进行检查。首先加载并解析vold.fstab,因为系统是按行解析的,通过查看vold.fstab可以很清楚的知道这一点。
vold.fatab中最重要的语句:
dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0 /devices/platform/msm_sdcc.2/mmc_host/mmc1
dev_mount <lable> <mount_point> <part> <sysfs_path…>
挂载命令 标签 挂载点 子分区个数 挂载路径
注:
子分区个数如果为auto则表示只有1个子分区,也可以为任何不为0的整数。
参数之间不能有空格,只能以tab为间隔(注意:这里为了对齐因此采用空格隔开,如果自行修改vold.fstab之后加以空格的话系统会识别不到的)。
如果vold.fstab解析无误,VolueManager将创建DirectVolume。
(3)事件处理
通过对两个socket的监听,完成对事件的处理以及对上层应用的响应。
a. Kernel发出uevent
NetlinkManager检测到kernel发出的uevent,解析后调用NetlinkHandler::onEvent()方法。该方法会分别处理不同的事件,这里重要的事件有:
“block”事件主要指Volume的mount、unmount、createAsec等。由VolumeManager的handleBlockEvent(evt)来处理,根据多态性最终将会调用AutoVolume或者DirectVolume的handleBlockEvent方法来处理。
“switch”事件主要指Volume的connet、disconnet等。根据相关操作,改变设备参数(设备类型、挂载点等)通过CommandListener告知FrameWork层。
b. FrameWork发出控制命令
与a相反,CommandListener检测到FrameWork层的命令(MountService发出的命令)调用VolumeManager的函数,VolumeManager找出对应的Volume,调用Volume函数去挂载/卸载操作。而Volume类中的相关操作最终通过调用Linux函数完成。
3 Vold启动过程详细介绍
Vold的入口函数在system/vold/main.cpp中。
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
//注释1
SLOGI("Vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
//注释2
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
//注释3
cl = new CommandListener();
//注释4
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
//注释5
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
//注释6
if (process_config(vm)) {
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
//注释7
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
//注释8
coldboot("/sys/block");
//注释9
// coldboot("/sys/class/switch");
/*
* Now that we're up, we can respond to commands
*/
if (cl->startListener()) {
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
//注释10
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
4 注释
4.1 注释1
声明VolumeManager,CommandListener 和NetlinkManager
4.2 注释2
实例VolumeManager,具体代码在VolumeManager.cpp中,并读取了initial mass storage enabled state 和initial USB connected state
4.3 注释3
实例化一个NetlinkManager对象,具体的构造函数位于文件NetlinkManager.cpp中
4.4 注释4
构造一个CommandListener对象,这个类定义在文件CommandListener.h中,继承了类FrameworkListener,这个类定义在sysutils/FrameworkListener.h中这个类又继承了类SocketListener,这个类定义在文件SocketListener.h中,
首先来看CommandListener.cpp:
CommandListener::CommandListener() :
FrameworkListener("vold") {
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new ShareCmd());
registerCmd(new StorageCmd());
registerCmd(new XwarpCmd());
}
在system/core/libsysutils/src/FrameworkListener.cpp
FrameworkListener::FrameworkListener(const char *socketName) :
SocketListener(socketName, true) {
mCommands = new FrameworkCommandCollection();
//typedef android::List<FrameworkCommand *> FrameworkCommandCollection;
}
在system/core/libsysutils/src/SocketListener.cpp中
SocketListener::SocketListener(const char *socketName, bool listen) {
mListen = listen;
mSocketName = socketName;
mSock = -1;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}
回头我们再看看CommandListener的构造函数
CommandListener::CommandListener() :
FrameworkListener("vold") {
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd());
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new ShareCmd());
registerCmd(new StorageCmd());
registerCmd(new XwarpCmd());
}
这里会调用其继承类的protected成员函数registerCmd,其参数是一个指向类FrameworkCommand的指针。类CommandListener包含几个私有的内部类class DumpCmd : public VoldCommand
class VolumeCmd : public VoldCommand
class ShareCmd : public VoldCommand
class AsecCmd : public VoldCommand
class StorageCmd : public VoldCommand
class XwarpCmd : public VoldCommand
我们分析如何实例化一个DumpCmd
CommandListener::DumpCmd::DumpCmd() :
VoldCommand("dump") {
}
其父类在文件system/vold/VoldCommand.cpp中
VoldCommand::VoldCommand(const char *cmd) :
FrameworkCommand(cmd) {
}
其父类FrameworkCommand::FrameworkCommand(const char *cmd) {
mCommand = cmd;
}
同理创建VolumeCmd, AsecCmd, ShareCmd, StorageCmd, XwarpCmd
其mCommand私有成员分别取值为 dump, volume, asec, share, storage, xwarp
我们看看怎么注册指令的
void FrameworkListener::registerCmd(FrameworkCommand *cmd) {
mCommands->push_back(cmd);
}
mCommands指向FrameworkCommandCollection 而FrameworkCommandCollection是一个包含FrameworkCommand指针的链表
typedef android::List<FrameworkCommand *> FrameworkCommandCollection
因此这里是将创建的FrameworkCommand指针对象压入链表存储起来
4.5 注释5
设置vm的私有成员SocketListener *mBroadcaster;
使得mBroadcaster指向cl
设置nm的私有成员mBroadcaster,同样指向cl
4.6 注释6
vm->start()始终返回0
4.7 注释7
解析/system/etc/vold.fstab文件,
读取type, label, mount_point, part
(1). 构建DirectVolume对象 :如果part为auto, 则调用dv = new DirectVolume(vm, label, mount_point, -1);
(2). 添加vold.fstab中定义的某一挂载项对应的sysfs_path到 DirectVolume对象的mPaths容器 dv->addPath(sysfs_path);
(3). 将这个DirectVolume 对象添加到 VolumeManager对象的容器mVolumes中 vm->addVolume(dv);
4.8 注释8
nm->start()会调用NetlinkManager类的start()方法
int NetlinkManager::start() {
struct sockaddr_nl nladdr;
int sz = 64 * 1024;
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,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 options: %s", strerror(errno));
return -1;
}
if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
SLOGE("Unable to bind uevent socket: %s", strerror(errno));
return -1;
}
//创建一个socket用于内核空间和用户空间的异步通信,监控系统的hotplug事件
mHandler = new NetlinkHandler(mSock);
/*
利用新创建的socket实例化一个NetlinkHandler类对象,NetlinkHandler继承了类NetlinkListener,NetlinkListener又继承了类SocketListener
我们看看其构造的过程
NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
} //NetlinkHandler.cpp
NetlinkListener::NetlinkListener(int socket) :
SocketListener(socket, false) {
}//NetlinkListener.cpp
SocketListener::SocketListener(int socketFd, bool listen) {
mListen = listen;
mSocketName = NULL;
mSock = socketFd;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}
*/
if (mHandler->start()) {
SLOGE("Unable to start NetlinkHandler: %s", strerror(errno));
return -1;
}
/*
mHandler->start()调用this->startListener()
最终会调用SocketListener::startListener()
int SocketListener::startListener() {
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;
}
}
if (mListen && listen(mSock, 4) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock));
//实例化一个SocketClient,并将其压入mClients容器中
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
//建立管道, 并将文件描述词存于数组mCtrlPipe[2]中,mCtrlPipe[0]为管道的读取端,mCtrlPipe[1]为管道的写入端
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
/*
创建线程,线程号为mThread,.
线程函数为
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
me->runListener();//将this即NetlinkHandler对象强制转换为SocketListener类型,调用其成员函数runListener()
pthread_exit(NULL);//线程退出,不会跑到这里
return NULL;
}
******************************
线程真正执行的函数:mListen成员用来判定是否监听套接字
Netlink套接字属于udp套接字,非监听套接字,该函数的主要功能体现在,如果该套接字有数据到来,就调用相关函数读取数据。
void SocketListener::runListener() {
while(1) {//无线循环,一直监听
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = 0;
FD_ZERO(&read_fds); //清空文件描述符集read_fds
if (mListen) {
max = mSock;
FD_SET(mSock, &read_fds); //添加文件描述符到文件描述符集read_fds
}
FD_SET(mCtrlPipe[0], &read_fds); //添加管道的读取端文件描述符到read_fds
if (mCtrlPipe[0] > max)
max = mCtrlPipe[0];
pthread_mutex_lock(&mClientsLock);//对容器mClients的操作需要加锁
for (it = mClients->begin(); it != mClients->end(); ++it) {
FD_SET((*it)->getSocket(), &read_fds); //遍历容器mClients的所有成员,调用内联函数getSocket()获取文件描述符,并添加到文件描述符集read_fds
if ((*it)->getSocket() > max)
max = (*it)->getSocket();
}
pthread_mutex_unlock(&mClientsLock);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) { // 等待文件描述符中某一文件描述符或者说socket有数据到来
SLOGE("select failed (%s)", strerror(errno));
sleep(1);
continue;
} else if (!rc)
continue;
if (FD_ISSET(mCtrlPipe[0], &read_fds))
break;
if (mListen && FD_ISSET(mSock, &read_fds)) {//监听套接字处理
struct sockaddr addr;
socklen_t alen = sizeof(addr);
int c;
if ((c = accept(mSock, &addr, &alen)) < 0) { //接收链接请求,建立连接,如果成功c即为建立链接后的数据交换套接字,
将其添加到mClient容器;
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
continue;
}
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c));
pthread_mutex_unlock(&mClientsLock);
}
do {//非监听套接字处理
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
int fd = (*it)->getSocket();
if (FD_ISSET(fd, &read_fds)) {//调用相应的数据读取函数,读取数据
pthread_mutex_unlock(&mClientsLock);
if (!onDataAvailable(*it)) {
close(fd);
pthread_mutex_lock(&mClientsLock);
delete *it;
it = mClients->erase(it);
pthread_mutex_unlock(&mClientsLock);
}
FD_CLR(fd, &read_fds);
continue;
}
}
pthread_mutex_unlock(&mClientsLock);
} while (0);
}
}
***************************
分析函数: onDataAvailable(*it)
onDataAvailable是在类SocketListener中定义的纯虚函数,在Android2.2中共有五个类继承该类,并对函数 onDataAvailable进行了实现,分别是:
DhcpListener(system\core\nexus)、
FrameworkListener(system\core\libsysutils\src)、
NetlinkListener(system\core\libsysutils\src)、
SupplicantListener(system\core\nexus)、
TiwlanEventListener(system\core\nexus);
针对Netlink创建的套接字,是由NetlinkHandler对象调用的startListen函数,并开启的相关线程,而且 NetlinkHandler继承了NetlinkListener,
所以此处调用的是NetlinkListener类的成员函数onDataAvailable;
system\core\libsysutils\src\NetlinkListener.cpp
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
int socket = cli->getSocket();
ssize_t count;
char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
struct sockaddr_nl snl;
struct iovec iov = {mBuffer, sizeof(mBuffer)};
struct msghdr hdr = {&snl, sizeof(snl), &iov, 1, cred_msg, sizeof(cred_msg), 0};
if ((count = recvmsg(socket, &hdr, 0)) < 0) { //读取数据
SLOGE("recvmsg failed (%s)", strerror(errno));
return false;
}
if ((snl.nl_groups != 1) || (snl.nl_pid != 0)) {
SLOGE("ignoring non-kernel netlink multicast message");
return false;
}
struct cmsghdr * cmsg = CMSG_FIRSTHDR(&hdr);
if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
SLOGE("ignoring message with no sender credentials");
return false;
}
struct ucred * cred = (struct ucred *)CMSG_DATA(cmsg);
if (cred->uid != 0) {
SLOGE("ignoring message from non-root UID %d", cred->uid);
return false;
}
NetlinkEvent *evt = new NetlinkEvent();
if (!evt->decode(mBuffer, count)) {
SLOGE("Error decoding NetlinkEvent");
goto out;
}
onEvent(evt); //NetlinkListener类定义了纯虚函数,其子类NetlinkHandler对其进行了实现,所以此处调用子类 NetlinkHandler的onEvent函数;
out:
delete evt;
return true;
}
onEvent 函数分析:
system/vold/NetlinkHandler.cpp
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")) {
vm->handleBlockEvent(evt);
} else if (!strcmp(subsys, "switch")) {
vm->handleSwitchEvent(evt);
} else if (!strcmp(subsys, "usb_composite")) {
vm->handleUsbCompositeEvent(evt);
} else if (!strcmp(subsys, "battery")) {
} else if (!strcmp(subsys, "power_supply")) {
}
}
至此即为获取uevent的事件通道
4.9 注释9
冷启动,vold错过了一些uevent,重新触发。向sysfs的uevent文件写入”add\n” 字符也可以触发sysfs事件,相当执行了一次热插拔。
4.10 注释10
调用其父类SocketListener的startListener()方法
cl实例的mSocketName为vold
调用内联函数android_get_control_socket(mSocketName),定义在文件system/core/include /cutils/sockets.h中
根据参数mSocketName, 加上前缀ANDROID_SOCKET_,即字符串ANDROID_SOCKET_vold,调用getenv获取到相应socket的文件描述符
例如,vold的socket在init.rc就写好了
service vold /system/bin/vold
socket vold stream 0660 root mount
ioprio be 2
init程序在读取init.rc文件后,在调用service_start启动service时调用create_socket在 ANDROID_SOCKET_DIR(/dev/socket)下建立相应的socket
调用publish_socket-->add_environment, 将socket的文件描述符加上ANDROID_SOCKET_ENV_PREFIX添加到环境变量之中
可以根据环境变量ANDROID_SOCKET_ENV_PREFIX<name> ("ANDROID_SOCKET_foo"), 调用getenv查找到相应的文件描述符
if (mListen && listen(mSock, 4) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
mClients->push_back(new SocketClient(mSock));
这里cl是监听套接字,因此调用listen 用来等待参数mSock 的socket连线. 指定同时能处理的最大连接要求为4,如果连接数目达此上限则client端将收到ECONNREFUSED的错误。Listen()并未开始接收连线,只是设置socket为listen模式,真正接收client端连线的是accept()。通常listen()会在 socket(),bind()之后调用,接着才调用accept()
if (pipe(mCtrlPipe)) {
SLOGE("pipe failed (%s)", strerror(errno));
return -1;
}
if (pthread_create(&mThread, NULL, SocketListener::threadStart, this)) {
SLOGE("pthread_create (%s)", strerror(errno));
return -1;
}
*/
/*
线程执行函数
void *SocketListener::threadStart(void *obj) {
SocketListener *me = reinterpret_cast<SocketListener *>(obj);
me->runListener();
pthread_exit(NULL);
return NULL;
}
void SocketListener::runListener() {
while(1) {
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = 0;
FD_ZERO(&read_fds);
if (mListen) { //cl是监听套接字,所以这里mListen为真
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) { //遍历mClients容器,将所有的socket文件描述符添加到集合read_fds中
FD_SET((*it)->getSocket(), &read_fds);
if ((*it)->getSocket() > max)
max = (*it)->getSocket();
}
pthread_mutex_unlock(&mClientsLock);
if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {// 等待文件描述符中某一文件描述符或者说socket有数据到来
SLOGE("select failed (%s)", strerror(errno));
sleep(1);
continue;
} else if (!rc)
continue;
if (FD_ISSET(mCtrlPipe[0], &read_fds))
break;
if (mListen && FD_ISSET(mSock, &read_fds)) {//这里cl是监听套接字,所以mListen为真,并且mSock在集合read_fds中
struct sockaddr addr;
socklen_t alen = sizeof(addr);
int c;
if ((c = accept(mSock, &addr, &alen)) < 0) {
SLOGE("accept failed (%s)", strerror(errno));
sleep(1);
continue;
}//accept用来接受参数mSock的socket连线。参数mSock的socket必需先经bind()、listen()函数处理过,当有连线进来时accept()会返回一个新的socket处理代码,往后的数据传送与读取就是经由新的socket处理,而原来参数mSock的 socket能继续使用accept()来接受新的连线要求
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c));//新的socket文件描述符将被实例化并加入到集合mClients中
pthread_mutex_unlock(&mClientsLock);
}
do {
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {//遍历集合mClients,如果其成员描述符在集合read_fds中,读取数据,如果没有数据发送过来,
//则从mClients中删除此socket,并清除集合read_fds中的文件描述符。
int fd = (*it)->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pthread_mutex_unlock(&mClientsLock);
if (!onDataAvailable(*it)) { //这里的cl的onDataAvailable在FrameworkListener中实现,返回为false,说明没有读取到数据,如果有数据,
调用FrameworkListener::dispatchCommand发送数据到socket
close(fd);
pthread_mutex_lock(&mClientsLock);
delete *it;
it = mClients->erase(it);
pthread_mutex_unlock(&mClientsLock);
}
FD_CLR(fd, &read_fds);
continue;
}
}
pthread_mutex_unlock(&mClientsLock);
} while (0);
}
}
************************************
In FrameworkListener.cpp
bool FrameworkListener::onDataAvailable(SocketClient *c) {
char buffer[255];
int len;
if ((len = read(c->getSocket(), buffer, sizeof(buffer) -1)) < 0) {
SLOGE("read() failed (%s)", strerror(errno));
return false;
} else if (!len)
return false;
int offset = 0;
int i;
for (i = 0; i < len; i++) {
if (buffer[i] == '\0') {
dispatchCommand(c, buffer + offset);
offset = i + 1;
}
}
return true;
}
5 参考文献
http://blog.csdn.net/yihongyuelan/article/details/6926034
http://apps.hi.baidu.com/share/detail/44662055#content
http://blog.csdn.net/sustzombie/article/details/6118256