1总体介绍
在Android 中,当SD卡插⼊系统之后,系统会⾃动挂载。Vold就是负责挂载SD卡的,vold的全称是volume daemon。实际上是负责完成系统的CDROM,USB⼤容量存储,MMC卡等扩展存储的挂载任务⾃动完成的守护进程。它提供的主要特点是⽀持这些存储外设的热插拔。 1.1总体流程图
Ø绿⾊箭头:表⽰插⼊SD卡后事件传递以及SD卡挂载 Ø红⾊箭头:表⽰挂载成功后的消息传递流程
Ø黄⾊箭头:表⽰MountService发出挂载/卸载SD卡的命令
1.2总体类图
main.cpp,vold的⼊⼝函数,系统起来会只执⾏vold的可执⾏⽂件,调到这个main函数中。
NetlinkManager.cpp位于源码位置/system/vold/NetlinkManager.cpp。该类的主要通过引⽤NetlinkHandl
er类中的onEvent()⽅法来接收来⾃内核的事件消息,NetlinkHandler位于/system/vold/NetlinkHandler.cpp。
VolumeManager:位于源码位置/system/vold/VolumeManager.cpp。该类的主要作⽤是接收经过NetlinkManager处理过后的事件消息。 DirectVolume:位于/system/vold/DirectVolume.cpp。该类的是⼀个⼯具类,主要负责对传⼊的事件进⾏进⼀步的处理,block事件⼜可以分为:Add,Removed,Change,Noaction这四种。
Volume:Volume.cpp位于/system/vold/Volume.cpp,该类是负责SD卡挂载的主要类。Volume.cpp主要负责检查SD卡格式,以及对复合要求的SD卡进⾏挂载,并通过Socket将消息SD卡挂载的消息传递给NativeDaemonConnector。
总的讲,vold程序需要分层三部分,第⼀部分为NetlinkManager,管理接受来⾃kernel的UEvent消息,第⼆部分为VolumeManager,主要负责处理来⾃NetlinkManager的消息和来⾃java层的消息,之后真正的挂载卸载动作就需要volume负责了。
2初始化流程
2.1时序图
2.2代码分析
在Android 系统启动的时候,init进程会去解析⽂件,在该⽂件中,有如下代码:
service vold /system/bin/vold
class core
socket vold stream 0660 root mount
ioprio be 2
定义了⼀个vold的service,去执⾏vold程序,并创建了⼀个名字为vold的socket,init进程解析完后就去执⾏vold程序,创建与java层通信的Socket。 在Android 源码/system/vold路径下的main.cpp,这个就是vold程序的⼊⼝,我们看看起main函数,代码如下:
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
if (!(vm = VolumeManager::Instance())) {//创建VolumeManager实例
};
if (!(nm = NetlinkManager::Instance())) {//创建NelinkManager实例
};
cl = new CommandListener(); //创建与java层socket通信的接⼝
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
笨笨牛历险记if (vm->start()) { //什么都没做
}
手术显微镜
if (process_config(vm)) {//初始化fstab
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
if (nm->start()) {//开始监听kernel上报的vold消息
}
……
if (cl->startListener()) {//开始监听来⾃java层的socket消息
SLOGE("Unable to start CommandListener (%s)", strerror(errno));
exit(1);
}
while(1) {
sleep(1000);
}
exit(0);
}
⾸先,在 main 函数中,需要创建 VolumeManager 和 NetlinkManager 的实例,⾥⾯就做了⼀些初始化的动作,这⾥就不多说了。
接着,则是初始化vold与java层的socket通信接⼝。创建了的CommandListener实例。在上⾯的类图关系中,我们知
道,CommandListener继承于FrameworkListener,⽽FrameworkListener有继承于SocketListener。先看看CommandListener的初始化代码:
CommandListener::CommandListener() :
FrameworkListener("vold") {
registerCmd(new DumpCmd());
registerCmd(new VolumeCmd()); //处理volume事件
registerCmd(new AsecCmd());
registerCmd(new ObbCmd());
registerCmd(new StorageCmd());
张君劢
registerCmd(new XwarpCmd());
registerCmd(new CryptfsCmd());
}
在上⾯代码中我们看到,先以“vold”为参数构造 FrameworkListener 类,完成之后,则调⽤ FrameworkListener 类中的registerCmd() ⽅法,注册⼀些处理⽅法类,⽽对于 sd 卡挂载的事件,我们先关注 VolumeCmd 类,它是 FrameworkListener 的内部类,⽤于处理 Volume 事件。接下来,看 FrameworkListener 的构造函数:
FrameworkListener::FrameworkListener(const char *socketName) :
SocketListener (socketName, true) {
mCommands = new FrameworkCommandCollection();
}
以之前传进来的“vold”参数构造 SocketListener 类,然后在 FrameworkListener 构造函数中,创建FrameworkCommandCollection 的实例,其实它就是⼀个容器,⽤于存储之前调⽤的 registerCmd() 注册的处理⽅法类。下⾯就看SocketListener 的构造函数:
SocketListener::SocketListener(const char *socketName, bool listen) {
mListen = listen;
mSocketName = socketName; /将vold字符串存储在mSocketName变量中
mSock = -1;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection(); //创建socket客户端容器
}
其实很简单,就是做了⼀些变量的初始化⼯作,⽤ mSocketName 变量存储“vold”字符串,这个 vold 是很有讲究的,因为是 定义的 vold Service 中,就创建了⼀个名字为 vold 的 socket 端⼝,后⾯将通过“vold”获取到该 socket 端⼝。
到此,CommandListener的初始化就完成了的。
sram
我们回到main函数中,创建了CommandListener实例之后,然后调⽤VolumeManger的setBroadcaster⽅法,将CommandListener的实例存储在mBroadcaster变量中,代码如下:
void setBroadcaster(SocketListener *sl) { mBroadcaster = sl; }
其实NetlinkManager也做了同样的设置,但我还没发现它有什么⽤,所以就不再关注了。
接下来就开始调⽤了main.cpp的process_config()⽅法了,在介绍之前,我必须先介绍下vold.fstab配置⽂件,这个配置⽂件就是
在process_config()中被解析的,⽽vold.fstab配置⽂件,就是⽤于描述vold的挂载动作的,其配置例⼦如下:
dev_mount sdcard /mnt/sdcard auto /devices/platform/goldfish_mmc.0
挂载命令标签挂载点⼦分区个数挂载路径
我们就以上⾯例⼦来说明,意思就是将/devices/platform/goldfish_mmc.0挂载到/mnt/sdcard中,/devices/platform/goldfish_mmc.0可以认为是kernel上报上来的路径。⼦分区个数如果为auto则表⽰只有1个⼦分区,也可以为任何不为0的整数。如果vold.fstab解析⽆
误,VolueManager将创建DirectVolume。
好了,下⾯可以看process_config()⽅法了,代码如下:
static int process_config(VolumeManager *vm) {
FILE *fp;
int n = 0;
char line[255];
if (!(fp = fopen("/etc/vold.fstab", "r"))) {
return -1;
}
while(fgets(line, sizeof(line), fp)) {
const char *delim = " \t";
char *save_ptr;
char *type, *label, *mount_point, *mount_flags, *sysfs_path;
int flags;
n++;
line[strlen(line)-1] = '\0';
if (line[0] == '#' || line[0] == '\0')
continue;
if (!(type = strtok_r(line, delim, &save_ptr))) {
goto out_syntax;
}
if (!(label = strtok_r(NULL, delim, &save_ptr))) {
goto out_syntax;
}
if (!(mount_point = strtok_r(NULL, delim, &save_ptr))) {
goto out_syntax;
}
if (!strcmp(type, "dev_mount")) {
DirectVolume *dv = NULL;
char *part;
if (!(part = strtok_r(NULL, delim, &save_ptr))) {
goto out_syntax;
}
if (strcmp(part, "auto") && atoi(part) == 0) {
goto out_syntax;
}
if (!strcmp(part, "auto")) {//如果解析没有错,那么就将创建DirectVolume
dv = new DirectVolume(vm, label, mount_point, -1);
} else {
dv = new DirectVolume(vm, label, mount_point, atoi(part));
}
while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {
if (*sysfs_path != '/') {
break;
}
if (dv->addPath(sysfs_path)) {
goto out_fail;
}
}
if (sysfs_path)
flags = parse_mount_flags(sysfs_path);
else
针织圆机flags = 0;
dv->setFlags(flags);
vm->addVolume(dv); //将创建的DirectVolume添加到VolumeManager中。
} else if (!strcmp(type, "map_mount")) {
} else {
}
}
fclose(fp);
return 0;
}
该⽅法,通过⼀个 wihle ⽅法,逐⾏进⾏解析,如果认为合理,那么将拿到的信息⽤于创建 DirectVolume 实例,然后调⽤
VolumeManager 的 addVolume ⽅法,存储在 mVolumes 变量中。
好了,下⾯就开始看注册监听kernel的sockect端⼝了。就是NetLinkManager的start⽅法,代码如下:
int NetlinkManager::start() {
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;
if ((mSock = socket(PF_NETLINK,//创建socket,返回⽂件描述符
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 SO_RECBUFFORCE option: %s", strerror(errno));
return -1;
}
if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
SLOGE("Unable to set uevent socket SO_PASSCRED option: %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;
}
mHandler = new NetlinkHandler(mSock);
if (mHandler->start()) {
return -1;
}
return 0;
}
其实就是调⽤socket()创建socket端⼝,返回描述符,经过⼀些设置,然后就描述符作为参数,创建的NetlinkHandler实例,然后就直接调⽤起start⽅法。看NetLinkHandler构造函数:
NetlinkHandler::NetlinkHandler(int listenerSocket) :
NetlinkListener(listenerSocket) {
}
构造函数⾥什么都没做,NetlinkHandler继承于NetlinkListener,然后讲socket端⼝的描述符传进去。
NetlinkListener::NetlinkListener(int socket) :
SocketListener(socket, false) {
mFormat = NETLINK_FORMAT_ASCII;
}
⼜是这么⼏句代码,NetlinkListener也是继承于SocketListener,所以还将socket描述符传进去,再次创建了SocketListener的实例,所以,在vold系统中,有两个SocketListener的实例。看其构造函数,这⾥的构造函数与之前的是不⼀样的,代码如下:
SocketListener::SocketListener(int socketFd, bool listen) {
mListen = listen;
mSocketName = NULL;
mSock = socketFd;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();