android 6.0 vold shutdown流程
2016-05-18 10:10
351 查看
这篇博客我们主要分析下vold在关机时候的流程,先看如下代码:
其中getPath函数返回的是storage/emulated/0目录,我们再来看看KillProcessesUsingPath函数:
主要还是调用了killProcessesWithOpenFiles函数,把占用这个文件的pid kill,直到没有pid占用这个文件。killProcessesWithOpenFiles我们后续分析。
继续回到VolumeManager的shutdown函数,下面就是各个Disk的destroy。
主要看下destroyAllVolumes函数,如下,其实就是调用各个volume的destroy函数
volume的destroy函数又会调用到unmount函数。
unmount函数又会调用到doUnmount函数,下面我们就来看下外部sd卡的PublicVolume的doUnmount函数:
上面的函数就是卸载各种文件系统,我们主要看下ForceUnmount函数
这个函数先执行unmount 操作,如果操作出错,就调用killProcessWithOpenFiles来杀占用该存储卡文件的pid。每次调用这个函数后继续umount操作,如果还不行,kill进程的信号会越来越高,最后到SIGKILL信号。
下面我们主要看下killProcessesWithOpenFiles函数。
三、杀占用sd卡文件的进程
下面我们主要分析killProcessesWithOpenFiles函数
下面我们先看下checkFileDescriptorSymLinks函数
下面我们来看下readSymlink函数和pathMatchesMountPoint函数
pathMatchesMountPoint函数
这样在killProcessesWithOpenFiles函数中,
1. checkFileDescriptorSymLinks
先看各个proc/pid/fd下面各个符号链接所指向的文件是否有在外置sd卡中的。如果有的话,通过cmdline把进程名和占用的sd卡文件打印出来
2.checkFileMaps
看各个pid的内存映射 proc/pid/maps是否有占用sd卡文件
3.查看proc/pid/cmd proc/pid/root proc/pid/exe下是否有占用sd卡文件
如果在这个pid中有的话,就把这个pid杀了,并且count + 1
我们再来看下proc/pid/fd下的文件,这是vold的pid下各个fd很多都指向socket pipe等
下面是setttings 进程下的fd,指向很多都是文件
root@lte26007:/proc/1667/fd # ls -l
lrwx------ system system 1980-01-01 14:19 0 -> /dev/null
lrwx------ system system 1980-01-01 14:19 1 -> /dev/null
l-wx------ system system 1980-01-01 14:19 10 -> /dev/cpuctl/tasks
lr-x------ system system 1980-01-01 14:19 11 -> /system/priv-app/Settings/Settings.apk
l-wx------ system system 1980-01-01 14:19 12 -> /dev/cpuctl/bg_non_interactive/tasks
lrwx------ system system 1980-01-01 14:19 13 -> socket:[34994]
lr-x------ system system 1980-01-01 14:19 14 -> pipe:[11126]
l-wx------ system system 1980-01-01 14:19 15 -> pipe:[11126]
lrwx------ system system 1980-01-01 14:19 16 -> anon_inode:[eventfd]
lrwx------ system system 1980-01-01 14:19 17 -> anon_inode:[eventpoll]
lrwx------ system system 1980-01-01 13:33 18 -> anon_inode:[eventfd]
lrwx------ system system 1980-01-01 14:19 19 -> anon_inode:[eventpoll]
lrwx------ system system 1980-01-01 14:19 2 -> /dev/null
lrwx------ system system 1980-01-01 14:19 20 -> /data/data/com.android.settings/databases/lock_screen_data_usage.db
lr-x------ system system 1980-01-01 14:19 21 -> pipe:[34995]
l-wx------ system system 1980-01-01 14:19 22 -> pipe:[34995]
lrwx------ system system 1980-01-01 14:19 3 -> socket:[3344]
l-wx------ system system 1980-01-01 14:19 4 -> /sys/kernel/debug/tracing/trace_marker
lr-x------ system system 1980-01-01 14:19 5 -> /system/framework/framework-res.apk
lr-x------ system system 1980-01-01 14:19 6 -> /system/framework/core-libart.jar
lr-x------ system system 1980-01-01 14:19 7 -> /dev/urandom
lrwx------ system system 1980-01-01 14:19 8 -> /dev/binder
lr-x------ system system 1980-01-01 14:19 9 -> /dev/__properties__
一、接收shutdown命令
这是vold接收MountService的命令,我们主要看下shutdown命令int CommandListener::VolumeCmd::runCommand(SocketClient *cli, int argc, char **argv) { dumpArgs(argc, argv, -1); if (argc < 2) { cli->sendMsg(ResponseCode::CommandSyntaxError, "Missing Argument", false); return 0; } VolumeManager *vm = VolumeManager::Instance(); std::lock_guard<std::mutex> lock(vm->getLock()); // TODO: tease out methods not directly related to volumes std::string cmd(argv[1]); if (cmd == "reset") { return sendGenericOkFail(cli, vm->reset()); } else if (cmd == "shutdown") { return sendGenericOkFail(cli, vm->shutdown()); }
二、执行shutdown
接收到shutdown命令后我们调用了VolumeManager的shutdown函数。int VolumeManager::shutdown() { mInternalEmulated->destroy(); for (auto disk : mDisks) { disk->destroy(); } mDisks.clear(); return 0; }
2.1 内部volume的卸载
VolumeManager的shutdown函数先调用了内部volume的destroy函数,最后会调用到status_t EmulatedVolume::doUnmount() { if (mFusePid > 0) { kill(mFusePid, SIGTERM); TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0)); mFusePid = 0; } KillProcessesUsingPath(getPath()); ForceUnmount(mFuseDefault); ForceUnmount(mFuseRead); ForceUnmount(mFuseWrite); rmdir(mFuseDefault.c_str()); rmdir(mFuseRead.c_str()); rmdir(mFuseWrite.c_str()); mFuseDefault.clear(); mFuseRead.clear(); mFuseWrite.clear(); return OK; }
其中getPath函数返回的是storage/emulated/0目录,我们再来看看KillProcessesUsingPath函数:
status_t KillProcessesUsingPath(const std::string& path) { const char* cpath = path.c_str(); if (Process::killProcessesWithOpenFiles(cpath, SIGINT) == 0) { return OK; } sleep(5); if (Process::killProcessesWithOpenFiles(cpath, SIGTERM) == 0) { return OK; } sleep(5); if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) { return OK; } sleep(5); // Send SIGKILL a second time to determine if we've // actually killed everyone with open files if (Process::killProcessesWithOpenFiles(cpath, SIGKILL) == 0) { return OK; } PLOG(ERROR) << "Failed to kill processes using " << path; return -EBUSY; }
主要还是调用了killProcessesWithOpenFiles函数,把占用这个文件的pid kill,直到没有pid占用这个文件。killProcessesWithOpenFiles我们后续分析。
继续回到VolumeManager的shutdown函数,下面就是各个Disk的destroy。
int VolumeManager::shutdown() { mInternalEmulated->destroy(); for (auto disk : mDisks) { disk->destroy(); } mDisks.clear(); return 0; }
2.2各个Disk的卸载
我们来看Disk的destroy函数:status_t Disk::destroy() { CHECK(mCreated); destroyAllVolumes(); mCreated = false; notifyEvent(ResponseCode::DiskDestroyed); return OK; }
主要看下destroyAllVolumes函数,如下,其实就是调用各个volume的destroy函数
void Disk::destroyAllVolumes() { for (auto vol : mVolumes) { vol->destroy(); } mVolumes.clear(); }
volume的destroy函数又会调用到unmount函数。
status_t VolumeBase::destroy() { CHECK(mCreated); if (mState == State::kMounted) { unmount(); setState(State::kBadRemoval); } else { setState(State::kRemoved); } notifyEvent(ResponseCode::VolumeDestroyed); status_t res = doDestroy(); mCreated = false; return res; }
unmount函数又会调用到doUnmount函数,下面我们就来看下外部sd卡的PublicVolume的doUnmount函数:
status_t PublicVolume::doUnmount() { if (mFusePid > 0) { kill(mFusePid, SIGTERM); TEMP_FAILURE_RETRY(waitpid(mFusePid, nullptr, 0)); mFusePid = 0; } ForceUnmount(kAsecPath); ForceUnmount(mFuseDefault);//卸载fuse文件系统 ForceUnmount(mFuseRead); ForceUnmount(mFuseWrite); ForceUnmount(mRawPath);//卸载sd卡的挂载地址 rmdir(mFuseDefault.c_str()); rmdir(mFuseRead.c_str()); rmdir(mFuseWrite.c_str()); rmdir(mRawPath.c_str()); mFuseDefault.clear(); mFuseRead.clear(); mFuseWrite.clear(); mRawPath.clear(); return OK; }
上面的函数就是卸载各种文件系统,我们主要看下ForceUnmount函数
status_t ForceUnmount(const std::string& path) { const char* cpath = path.c_str(); if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { return OK; } PLOG(WARNING) << "Failed to unmount 1 " << path; // Apps might still be handling eject request, so wait before // we start sending signals sleep(5); Process::killProcessesWithOpenFiles(cpath, SIGINT); sleep(5); if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { return OK; } PLOG(WARNING) << "Failed to unmount 2 " << path; Process::killProcessesWithOpenFiles(cpath, SIGTERM); sleep(5); if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { return OK; } PLOG(WARNING) << "Failed to unmount 3 " << path; Process::killProcessesWithOpenFiles(cpath, SIGKILL); sleep(5); if (!umount2(cpath, UMOUNT_NOFOLLOW) || errno == EINVAL || errno == ENOENT) { return OK; } PLOG(ERROR) << "Failed to unmount " << path; return -errno; }
这个函数先执行unmount 操作,如果操作出错,就调用killProcessWithOpenFiles来杀占用该存储卡文件的pid。每次调用这个函数后继续umount操作,如果还不行,kill进程的信号会越来越高,最后到SIGKILL信号。
下面我们主要看下killProcessesWithOpenFiles函数。
三、杀占用sd卡文件的进程
下面我们主要分析killProcessesWithOpenFiles函数int Process::killProcessesWithOpenFiles(const char *path, int signal) { int count = 0; DIR* dir; struct dirent* de; if (!(dir = opendir("/proc"))) {//打开proc目录 SLOGE("opendir failed (%s)", strerror(errno)); return count; } while ((de = readdir(dir))) { int pid = getPid(de->d_name);//获取pid值 char name[PATH_MAX]; if (pid == -1)//不是pid的子目录continue,继续遍历目录 continue; getProcessName(pid, name, sizeof(name));//从proc/pid/cmdline获取进程名 char openfile[PATH_MAX]; if (checkFileDescriptorSymLinks(pid, path, openfile, sizeof(openfile))) { SLOGE("Process %s (%d) has open file %s", name, pid, openfile); } else if (checkFileMaps(pid, path, openfile, sizeof(openfile))) { SLOGE("Process %s (%d) has open filemap for %s", name, pid, openfile); } else if (checkSymLink(pid, path, "cwd")) { SLOGE("Process %s (%d) has cwd within %s", name, pid, path); } else if (checkSymLink(pid, path, "root")) { SLOGE("Process %s (%d) has chroot within %s", name, pid, path); } else if (checkSymLink(pid, path, "exe")) { SLOGE("Process %s (%d) has executable path within %s", name, pid, path); } else { continue; } if (signal != 0) { SLOGW("Sending %s to process %d", strsignal(signal), pid); kill(pid, signal); count++; } } closedir(dir); return count; }
下面我们先看下checkFileDescriptorSymLinks函数
int Process::checkFileDescriptorSymLinks(int pid, const char *mountPoint, char *openFilename, size_t max) { // compute path to process's directory of open files char path[PATH_MAX]; sprintf(path, "/proc/%d/fd", pid);//指定proc/pid/fd这个目录 DIR *dir = opendir(path); if (!dir) return 0; // remember length of the path int parent_length = strlen(path); // append a trailing '/' path[parent_length++] = '/'; struct dirent* de; while ((de = readdir(dir))) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..") || strlen(de->d_name) + parent_length + 1 >= PATH_MAX) continue; // append the file name, after truncating to parent directory path[parent_length] = 0; strcat(path, de->d_name);//把目录变成proc/pid/fd/各个文件fd char link[PATH_MAX]; //下面这个判断条件的意思是读取各个fd(符号链接)所指向的实际目录,然后第二个条件看看这个实际目录和挂载地址是否匹配 if (readSymLink(path, link, sizeof(link)) && pathMatchesMountPoint(link, mountPoint)) { if (openFilename) { memset(openFilename, 0, max); strlcpy(openFilename, link, max);//把fd指向的实际地址传给openFilename } closedir(dir); return 1; } } closedir(dir); return 0; }
下面我们来看下readSymlink函数和pathMatchesMountPoint函数
int Process::readSymLink(const char *path, char *link, size_t max) { struct stat s; int length; if (lstat(path, &s) < 0) return 0; if ((s.st_mode & S_IFMT) != S_IFLNK)//如果不是符号链接,返回 return 0; // we have a symlink length = readlink(path, link, max- 1);//读取符号链接所指向的实际地址 if (length <= 0) return 0; link[length] = 0;//字符串结束符 return 1; }
pathMatchesMountPoint函数
int Process::pathMatchesMountPoint(const char* path, const char* mountPoint) { int length = strlen(mountPoint); if (length > 1 && strncmp(path, mountPoint, length) == 0) {//挂载地址和前面实际地址前面地址相同 // we need to do extra checking if mountPoint does not end in a '/' if (mountPoint[length - 1] == '/') return 1; // if mountPoint does not have a trailing slash, we need to make sure // there is one in the path to avoid partial matches. return (path[length] == 0 || path[length] == '/');//如果实际地址在之前的路径是/或者结束了 返回1 } return 0; }
这样在killProcessesWithOpenFiles函数中,
1. checkFileDescriptorSymLinks
先看各个proc/pid/fd下面各个符号链接所指向的文件是否有在外置sd卡中的。如果有的话,通过cmdline把进程名和占用的sd卡文件打印出来
2.checkFileMaps
看各个pid的内存映射 proc/pid/maps是否有占用sd卡文件
3.查看proc/pid/cmd proc/pid/root proc/pid/exe下是否有占用sd卡文件
如果在这个pid中有的话,就把这个pid杀了,并且count + 1
我们再来看下proc/pid/fd下的文件,这是vold的pid下各个fd很多都指向socket pipe等
root@lte26007:/proc/173/fd # ls -l lrwx------ root root 1980-01-01 14:17 0 -> /dev/null lrwx------ root root 1980-01-01 14:17 1 -> /dev/null lrwx------ root root 1980-01-01 14:17 10 -> socket:[7465] lrwx------ root root 1980-01-01 14:17 11 -> socket:[7468] lr-x------ root root 1980-01-01 14:17 12 -> pipe:[3160] lrwx------ root root 1980-01-01 14:17 13 -> socket:[10392] lrwx------ root root 1980-01-01 14:17 14 -> socket:[10394] l-wx------ root root 1980-01-01 14:17 15 -> pipe:[3160] lrwx------ root root 1980-01-01 14:17 2 -> /dev/null lrwx------ root root 1980-01-01 13:32 3 -> socket:[3142] lrwx------ root root 1980-01-01 14:17 4 -> socket:[3148] lr-x------ root root 1980-01-01 14:17 5 -> pipe:[3149] l-wx------ root root 1980-01-01 14:17 6 -> pipe:[3149] lr-x------ root root 1980-01-01 14:17 7 -> pipe:[3159] l-wx------ root root 1980-01-01 14:17 8 -> pipe:[3159] lr-x------ root root 1980-01-01 14:17 9 -> /dev/__properties__
下面是setttings 进程下的fd,指向很多都是文件
root@lte26007:/proc/1667/fd # ls -l
lrwx------ system system 1980-01-01 14:19 0 -> /dev/null
lrwx------ system system 1980-01-01 14:19 1 -> /dev/null
l-wx------ system system 1980-01-01 14:19 10 -> /dev/cpuctl/tasks
lr-x------ system system 1980-01-01 14:19 11 -> /system/priv-app/Settings/Settings.apk
l-wx------ system system 1980-01-01 14:19 12 -> /dev/cpuctl/bg_non_interactive/tasks
lrwx------ system system 1980-01-01 14:19 13 -> socket:[34994]
lr-x------ system system 1980-01-01 14:19 14 -> pipe:[11126]
l-wx------ system system 1980-01-01 14:19 15 -> pipe:[11126]
lrwx------ system system 1980-01-01 14:19 16 -> anon_inode:[eventfd]
lrwx------ system system 1980-01-01 14:19 17 -> anon_inode:[eventpoll]
lrwx------ system system 1980-01-01 13:33 18 -> anon_inode:[eventfd]
lrwx------ system system 1980-01-01 14:19 19 -> anon_inode:[eventpoll]
lrwx------ system system 1980-01-01 14:19 2 -> /dev/null
lrwx------ system system 1980-01-01 14:19 20 -> /data/data/com.android.settings/databases/lock_screen_data_usage.db
lr-x------ system system 1980-01-01 14:19 21 -> pipe:[34995]
l-wx------ system system 1980-01-01 14:19 22 -> pipe:[34995]
lrwx------ system system 1980-01-01 14:19 3 -> socket:[3344]
l-wx------ system system 1980-01-01 14:19 4 -> /sys/kernel/debug/tracing/trace_marker
lr-x------ system system 1980-01-01 14:19 5 -> /system/framework/framework-res.apk
lr-x------ system system 1980-01-01 14:19 6 -> /system/framework/core-libart.jar
lr-x------ system system 1980-01-01 14:19 7 -> /dev/urandom
lrwx------ system system 1980-01-01 14:19 8 -> /dev/binder
lr-x------ system system 1980-01-01 14:19 9 -> /dev/__properties__
四、总结
这篇博客,主要分析了vold在手机关机的流程,先进行内存volume的卸载,然后再是各个disk的卸载。其中我们详细分析了在卸载sd卡的时候,会去kill各个占用外置sd卡的pid。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories