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。



1 0
原创粉丝点击