您的位置:首页 > 移动开发 > Android开发

android 6.0 vold shutdown流程

2016-05-18 10:10 351 查看
这篇博客我们主要分析下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 -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。


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android vold