android 6.0 vold shutdown流程
来源:互联网 发布:水果店会员卡软件 编辑:程序博客网 时间:2024/05/21 06:32
这篇博客我们主要分析下vold在关机时候的流程,先看如下代码:
一、接收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 -llrwx------ root root 1980-01-01 14:17 0 -> /dev/nulllrwx------ root root 1980-01-01 14:17 1 -> /dev/nulllrwx------ 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/nulllrwx------ 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 -llrwx------ system system 1980-01-01 14:19 0 -> /dev/nulllrwx------ system system 1980-01-01 14:19 1 -> /dev/nulll-wx------ system system 1980-01-01 14:19 10 -> /dev/cpuctl/taskslr-x------ system system 1980-01-01 14:19 11 -> /system/priv-app/Settings/Settings.apkl-wx------ system system 1980-01-01 14:19 12 -> /dev/cpuctl/bg_non_interactive/taskslrwx------ 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/nulllrwx------ system system 1980-01-01 14:19 20 -> /data/data/com.android.settings/databases/lock_screen_data_usage.dblr-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_markerlr-x------ system system 1980-01-01 14:19 5 -> /system/framework/framework-res.apklr-x------ system system 1980-01-01 14:19 6 -> /system/framework/core-libart.jarlr-x------ system system 1980-01-01 14:19 7 -> /dev/urandomlrwx------ system system 1980-01-01 14:19 8 -> /dev/binderlr-x------ system system 1980-01-01 14:19 9 -> /dev/__properties__
四、总结
这篇博客,主要分析了vold在手机关机的流程,先进行内存volume的卸载,然后再是各个disk的卸载。其中我们详细分析了在卸载sd卡的时候,会去kill各个占用外置sd卡的pid。
- android 6.0 vold shutdown流程
- android vold流程分析
- android vold asec 流程
- Android vold 工作流程分析
- Android存储子系统流程--vold
- Android 7.0 Vold工作流程
- android vold初始化及sd卡挂载流程
- Android热插拔事件处理流程--Vold
- Android热插拔事件处理流程--Vold
- Android热插拔事件处理流程--Vold
- Android热插拔事件处理流程--Vold
- Android热插拔事件处理流程--Vold
- Android热插拔事件处理流程--Vold
- android vold初始化及sd卡挂载流程
- Android热插拔事件处理流程--Vold
- android vold初始化及sd卡挂载流程
- android vold初始化及sd卡挂载流程
- android vold:图解VOLD
- HANA数据库为何如此之快
- CodeForces 505BMr. Kitayuta's Colorful Graph
- Linux2.6进程切换经典分析
- Android动画
- Android Service学习之AIDL, Parcelable和远程服务
- android 6.0 vold shutdown流程
- ExecutorService的execute和submit方法区别
- Android Studio目录结构浅析
- C#线程池多线程Socket通讯 服务器端和客户端示例
- find
- C#Winform删除DataGridView中被选中的行
- 区块链的未来潜力
- jsp 取 map<String,List>值
- Android 判断imageview角度并旋转