android 6.0 vold shutdown流程

这篇博客我们主要分析下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。



  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值