熟悉操作系统的都知道,硬件存储设备如何才能被系统的软件操作,需要将设备节点挂载至相应的挂载点(目录),挂载的时候会有一系列的参数,包括挂载为什么格式的文件系统等,然后标准的文件系统操作就可以控制硬件存储设备了,kernel部分linux已经做得比较完善,但android上层需要做一个上层的适配,vold系统应运而生。。。
Vold系统核心代码:/system/vold目录,还包含一些lib库目录,从vold目录下的Android.mk可以看出来,
vold入口函数:
int main() {
VolumeManager *vm;
CommandListener *cl;
NetlinkManager *nm;
SLOGI("Vold 2.1 (the revenge) firing up");
mkdir("/dev/block/vold", 0755);
/* For when cryptfs checks and mounts an encrypted filesystem */
klog_set_level(6);
/* Create our singleton managers */
if (!(vm = VolumeManager::Instance())) {
SLOGE("Unable to create VolumeManager");
exit(1);
};
if (!(nm = NetlinkManager::Instance())) {
SLOGE("Unable to create NetlinkManager");
exit(1);
};
cl = new CommandListener();
vm->setBroadcaster((SocketListener *) cl);
nm->setBroadcaster((SocketListener *) cl);
if (vm->start()) {
SLOGE("Unable to start VolumeManager (%s)", strerror(errno));
exit(1);
}
if (process_config(vm)) {
SLOGE("Error reading configuration (%s)... continuing anyways", strerror(errno));
}
if (nm->start()) {
SLOGE("Unable to start NetlinkManager (%s)", strerror(errno));
exit(1);
}
coldboot("/sys/block");
// 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);
}
// Eventually we'll become the monitoring thread
while(1) {
sleep(1000);
}
SLOGI("Vold exiting");
exit(0);
}
启动流程如下:
创建/dev/block/vold,放置后面建立的vold节点;
实例化VolumeManager(VM)和NetlinkManager(NM);
分别创建SocketListener类型的Listener,并设置。CommandListener继承自FrameworkListener,此Listener是用来监听Framework层的事件,并作出响应;
调用VM:start方法,启动VM;
执行process_config方法,
static int process_config(VolumeManager *vm)
{
char fstab_filename[PROPERTY_VALUE_MAX + sizeof(FSTAB_PREFIX)];
char propbuf[PROPERTY_VALUE_MAX];
int i;
int ret = -1;
int flags;
property_get("ro.hardware", propbuf, "");
snprintf(fstab_filename, sizeof(fstab_filename), FSTAB_PREFIX"%s", propbuf);
fstab = fs_mgr_read_fstab(fstab_filename);
if (!fstab) {
SLOGE("failed to open %s\n", fstab_filename);
return -1;
}
/* Loop through entries looking for ones that vold manages */
for (i = 0; i < fstab->num_entries; i++) {
if (fs_mgr_is_voldmanaged(&fstab->recs[i])) {
DirectVolume *dv = NULL;
flags = 0;
/* Set any flags that might be set for this volume */
if (fs_mgr_is_nonremovable(&fstab->recs[i])) {
flags |= VOL_NONREMOVABLE;
}
if (fs_mgr_is_encryptable(&fstab->recs[i])) {
flags |= VOL_ENCRYPTABLE;
}
/* Only set this flag if there is not an emulated sd card */
if (fs_mgr_is_noemulatedsd(&fstab->recs[i]) &&
!strcmp(fstab->recs[i].fs_type, "vfat")) {
flags |= VOL_PROVIDES_ASEC;
}
dv = new DirectVolume(vm, &(fstab->recs[i]), flags);
if (dv->addPath(fstab->recs[i].blk_device)) {
SLOGE("Failed to add devpath %s to volume %s",
fstab->recs[i].blk_device, fstab->recs[i].label);
goto out_fail;
}
vm->addVolume(dv);
}
}
ret = 0;
out_fail:
return ret;
}
首先解析平台相关的.fstab文件,该方法是逐行解析文件内容,并将相应命令及参数保存在fstab结构体中;
nm->start(),通过NM类中的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,
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_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;
}
创建本地监听UEVENT事件的套接字mSock,并和nladdr绑定,并将mSock作为参数传递给NetlinkHandler,后者最终继承自SocketListener,并调用其init()方法如下:
SocketListener::SocketListener(int socketFd, bool listen) {
init(NULL, socketFd, listen, false);
}
void SocketListener::init(const char *socketName, int socketFd, bool listen, bool useCmdNum) {
mListen = listen;
mSocketName = socketName;
mSock = socketFd;
mUseCmdNum = useCmdNum;
pthread_mutex_init(&mClientsLock, NULL);
mClients = new SocketClientCollection();
}
之后是mHandler->start()方法,根据继承关系,最终调用SocketListener的startListener()方法,
int SocketListener::startListener() {
if (!mSocketName && mSock == -1) {
SLOGE("Failed to start unbound listener");
errno = EINVAL;
return -1;
} else if (mSocketName) {
if ((<span style="color:#ff0000;">mSock = android_get_control_socket(mSocketName)) < 0</span>) {
SLOGE("Obtaining file descriptor socket '%s' failed: %s",
mSocketName, strerror(errno));
return -1;
}
SLOGV("got mSock = %d for %s", mSock, mSocketName);
}
if (mListen && listen(mSock, 4) < 0) {
SLOGE("Unable to listen on socket (%s)", strerror(errno));
return -1;
} else if (!mListen)
<span style="color:#ff0000;">mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));</span>
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;
}
return 0;
}
根据之前的init方法调用,我们知道mSocketName为NULL,mListen为false;则该方法会走红色标注的字段流程,第一段红字是根据socket名获取文件描述符句柄,上述方法在末段会创建thread,而该thread会通过runListener来开启socket侦听;
void SocketListener::runListener() {
SocketClientCollection *pendingList = new SocketClientCollection();
while(1) {
SocketClientCollection::iterator it;
fd_set read_fds;
int rc = 0;
int max = -1;
FD_ZERO(&read_fds);
if (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) {
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) {
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))
break;
if (mListen && FD_ISSET(mSock, &read_fds)) {
struct sockaddr addr;
socklen_t alen;
int c;
do {
alen = sizeof(addr);
c = accept(mSock, &addr, &alen);
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;
}
pthread_mutex_lock(&mClientsLock);
mClients->push_back(new SocketClient(c, true, mUseCmdNum));
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) {
int fd = (*it)->getSocket();
if (FD_ISSET(fd, &read_fds)) {
pendingList->push_back(*it);
}
}
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 and our sockets are
* connection-based, remove and destroy it */
if (!onDataAvailable(c) && mListen) {
/* Remove the client from our array */
SLOGV("going to zap %d for %s", c->getSocket(), mSocketName);
pthread_mutex_lock(&mClientsLock);
for (it = mClients->begin(); it != mClients->end(); ++it) {
if (*it == c) {
mClients->erase(it);
break;
}
}
pthread_mutex_unlock(&mClientsLock);
/* Remove our reference to the client */
c->decRef();
}
}
}
delete pendingList;
}
继续main函数,之后coldboot("/sys/block");
static void coldboot(const char *path)
{
DIR *d = opendir(path);
if(d) {
do_coldboot(d, 0);
closedir(d);
}
}
继而do_coldboot(d, 0);
static void do_coldboot(DIR *d, int lvl)
{
struct dirent *de;
int dfd, fd;
dfd = dirfd(d);
fd = openat(dfd, "uevent", O_WRONLY);
if(fd >= 0) {
write(fd, "add\n", 4);
close(fd);
}
while((de = readdir(d))) {
DIR *d2;
if (de->d_name[0] == '.')
continue;
if (de->d_type != DT_DIR && lvl > 0)
continue;
fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
if(fd < 0)
continue;
d2 = fdopendir(fd);
if(d2 == 0)
close(fd);
else {
do_coldboot(d2, lvl + 1);
closedir(d2);
}
}
}
这两个函数就是打开/sys/block目录处理一些事情,起到模拟添加uevent事件,引发相应动作,给vold打杂而已,级别比较低。
继续main函数的最后一条cl->startListener()方法,cl是继承自FrameworkListener类,我们看FL的init实现得知cl传递的参数为FL("vold", true); FL继承自SocketListener,调用其startListener方法,传递参数为(“vold”, true, true);此时mSocketName为vold,mListener为true不同于前文的那次调用传值,此时开始创建线程,侦听绑定名称为vold的套接字,开始工作。
个人拙见,开启的socket侦听,一为对下,跟kernel打交道,一为对上,跟framework层daemon打交道。
总说:当应用程序需要使用vold提供的Mass Storage服务时,需要通过相应的API(如android.os.StorageManager)调用IMountService接口,然后跨进程调用system_server进程中的MountService,后者又借助于NativeDaemonConnector向UNIX本地套接字中写入命令字符串。vold守护进程中的类通过继承SocketListener/FrameworkListener,实现了对这些命令字符串的接收,然后解析这些字符串,继而执行相应的命令动作,这些命令最终调用VolumeManager去完成任务。对于热插拔事件,则借助于uevent机制上报,由继承自NetlinkListener的NetlinkHandler去处理。最终也是由VolumeManager去完成对存储设备的卷的管理。