AndroidP之 Ueventd

写在前头

在阅读本文之前需要对 uevent 有一个基础的概念,更有助于理解 ueventd
可参考我此前写的文章Linux 设备模型之 Uevent

ueventd概述

Android 中的 ueventd 是一个守护进程,它通过netlink scoket监听内核生成的uevent消息,当ueventd收到这样的消息时,它通过采取适当的行动来处理它,通常是在/dev中创建设备节点,设置文件权限,设置selinux标签等。ueventd还可以处理内核请求的固件加载、创建块设备符号链接以及创建字符设备。

ueventd启动流程

从本质上讲,ueventd也是一个 init 进程,启动 ueventd也是执行了 init 中的代码。我们先来看下 ueventd是如何启动的。
从下面这段 init.rc 中的代码我们可以看到ueventd是在 early-init 阶段启动:

/*system/core/rootdir/init.rc*/  
on early-init  
    ...  
    start ueventd  
  
## Daemon processes to be run by init.  
service ueventd /sbin/ueventd  
    class core  
    critical  
    seclabel u:r:ueventd:s0  
    shutdown critical  

从 service 的定义可以知道ueventd即/sbin/ueventd,那么为什么前面说 ueventd也是一个 init 进程呢?我们来看下 init Android.mk 的定义:

/*system/core/init/Android.mk*/  
# Create symlinks.  
LOCAL_POST_INSTALL_CMD := $(hide) mkdir -p $(TARGET_ROOT_OUT)/sbin; \  
    ln -sf ../init $(TARGET_ROOT_OUT)/sbin/ueventd; 

这样就很清楚了,ueventd只是 init 的一个软连接。
看过 init 代码的朋友都清楚,在 init main 函数开头有三大条件判断,每一个判断都对应着一个独立的进程处理,首当其冲的就是本文中的主角 ueventd。

int main(int argc, char** argv) {  
    if (!strcmp(basename(argv[0]), "ueventd")) {  
        return ueventd_main(argc, argv);  
    }  
    ……  
}  

所以start ueventd也会执行init的main函数,从而执行ueventd的相关代码。
ueventd 的主要组成部分
本小节主要涉及文件有:

devices.cpp
devices.h
uevent.h
uevent_listener.cpp
uevent_listener.h
ueventd.cpp
ueventd.h
ueventd_parser.cpp
ueventd_parser.h

通过上文我们知道,ueventd_main()函数就是ueventd进程的主体,接下来我们就来看下ueventd_main函数,ueventd_main主要实现了以下几个功能:

  • 解析ueventd相关rc文件,管理设备节点权限;
  • Cold boot,递归扫描/sys目录,根据uevent文件,静态创建设备节点;
  • Hot plug,获取内核uevent事件,动态创建设备节点。
    ueventd_main()代码如下:
int ueventd_main(int argc, char** argv) {  
    …… 
    LOG(INFO) << "ueventd started!";  
  
    DeviceHandler device_handler = CreateDeviceHandler(); // 解析ueventd相关rc文件,管理设备节点权限;  
    UeventListener uevent_listener;  
  
    if (access(COLDBOOT_DONE, F_OK) != 0) {  
        ColdBoot cold_boot(uevent_listener, device_handler);  
        cold_boot.Run(); // 递归扫描/sys目录,根据uevent文件,静态创建设备节点;  
    }  
  
    // We use waitpid() in ColdBoot, so we can't ignore SIGCHLD until now.  
    signal(SIGCHLD, SIG_IGN);  
    // Reap and pending children that exited between the last call to waitpid() and setting SIG_IGN  
    // for SIGCHLD above.  
    while (waitpid(-1, nullptr, WNOHANG) > 0) {  
    }  
  
    uevent_listener.Poll([&device_handler](const Uevent& uevent) { //获取内核uevent事件,动态创建设备节点。  
        HandleFirmwareEvent(uevent);  
        device_handler.HandleDeviceEvent(uevent);  
        return ListenerAction::kContinue;  
    });  
  
    return 0;  
}  

接下来从这三个主要功能来介绍ueventd。

CreateDeviceHandler

先来看下CreateDeviceHandler()具体实现:

DeviceHandler CreateDeviceHandler() {  
    Parser parser;  
  
    std::vector subsystems;  
    parser.AddSectionParser("subsystem", std::make_unique(&subsystems));  
  
    using namespace std::placeholders;  
    std::vector sysfs_permissions;  
    std::vector dev_permissions;  
    parser.AddSingleLineParser("/sys/",  
                               std::bind(ParsePermissionsLine, _1, &sysfs_permissions, nullptr));  
    parser.AddSingleLineParser("/dev/",  
                               std::bind(ParsePermissionsLine, _1, nullptr, &dev_permissions));  
  
    parser.ParseConfig("/ueventd.rc");  
    parser.ParseConfig("/vendor/ueventd.rc");  
    parser.ParseConfig("/odm/ueventd.rc");  
  
    /* 
     * keep the current product name base configuration so 
     * we remain backwards compatible and allow it to override 
     * everything 
     * TODO: cleanup platform ueventd.rc to remove vendor specific 
     * device node entries (b/34968103) 
     */  
    std::string hardware = android::base::GetProperty("ro.hardware", "");  
    parser.ParseConfig("/ueventd." + hardware + ".rc");  
  
    auto boot_devices = fs_mgr_get_boot_devices();  
    return DeviceHandler(std::move(dev_permissions), std::move(sysfs_permissions),  
                         std::move(subsystems), std::move(boot_devices), true);  
}  

从代码上看,这里的实现很“直接”,主要是完成了对ueventd相关的rc解析以及对DeviceHandler的初始化。

Coldboot

当ueventd启动时,它会遍历所有在/sys注册的设备并将'add'写入它找到的每个'uevent'文件,这会导致内核生成并重新发送所有当前注册设备的uevent消息。这样做是因为当这些设备注册到sysfs时,ueventd根本还没有开始运行,没能接收他们的uevent消息并妥善处理,这样Android系统也是无法正常运行的,所以需要重新生成其uevent以便ueventd对其做处理。这个过程被称为
'冷启动',即cold_boot,cold_boot也是ueventd在开机过程中的最主要的工作。
Cold_boot的主体run函数:

if (access(COLDBOOT_DONE, F_OK) != 0) {  
    ColdBoot cold_boot(uevent_listener, device_handler); // 创建netlink socket,监听uevent
    cold_boot.Run();  
}  

void ColdBoot::Run() {  
    android::base::Timer cold_boot_timer;  
  
    RegenerateUevents();  // 递归扫/sys目录,对uevent写入add,重新生成uevent
  
    ForkSubProcesses();  // 对应生成子线程处理每一个uevent
  
    DoRestoreCon();      // 重新生成selinux context
  
    WaitForSubProcesses();  // 等待子线程处理完成
  
    // 处理完成后生成"/dev/.coldboot_done"
    close(open(COLDBOOT_DONE, O_WRONLY | O_CREAT | O_CLOEXEC, 0000));  
    LOG(INFO) << "Coldboot took " << cold_boot_timer.duration().count() / 1000.0f << " seconds";  
}  

RegenerateUevents

RegenerateUevents会递归的往/sys目录下已经注册的设备的uevent文件写入“add”,kernel会为此重新生成uevent。

void ColdBoot::RegenerateUevents() {  
    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {  
        HandleFirmwareEvent(uevent);  
  
        uevent_queue_.emplace_back(std::move(uevent));  
        return ListenerAction::kContinue;  
    });  
}  
  
static const char* kRegenerationPaths[] = {"/sys/class", "/sys/block", "/sys/devices"};  
  
void UeventListener::RegenerateUevents(const ListenerCallback& callback) const {  
    for (const auto path : kRegenerationPaths) {  
        if (RegenerateUeventsForPath(path, callback) == ListenerAction::kStop) return;  
    }  
}  

ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,                                                         const ListenerCallback& callback) const {  
    int dfd = dirfd(d);  
  
    int fd = openat(dfd, "uevent", O_WRONLY); // 打开uevent文件  
    if (fd >= 0) {  
        write(fd, "add\n", 4);  // 往uevent文件写入“add”
        close(fd);  
  
        Uevent uevent;  
        while (ReadUevent(&uevent)) {  //从socket中获取uevent事件
            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;  
        }  
    }  
    ......  
}  

在ueventd做coldboot的时候,init进程也在同步等待cold_boot的完成。

static Result wait_for_coldboot_done_action(const BuiltinArguments& args) { 

    Timer t;  
  
    LOG(VERBOSE) << "Waiting for " COLDBOOT_DONE "...";  
  
    // Historically we had a 1s timeout here because we weren't otherwise  
    // tracking boot time, and many OEMs made their sepolicy regular  
    // expressions too expensive (http://b/19899875).  
  
    // Now we're tracking boot time, just log the time taken to a system  
    // property. We still panic if it takes more than a minute though,  
    // because any build that slow isn't likely to boot at all, and we'd  
    // rather any test lab devices fail back to the bootloader.  
    if (wait_for_file(COLDBOOT_DONE, 60s) < 0) {  
        LOG(FATAL) << "Timed out waiting for " COLDBOOT_DONE;  
    }  
  
    property_set("ro.boottime.init.cold_boot_wait", std::to_string(t.duration().count()));  
    return Success();  
}  

从代码中我们可以看到,init会等待60s,若ueventd在60s内不能完成cold_boot的处理,那么系统将会reboot to bootloader。

ForkSubProcesses

ForkSubProcesses主要工作就是并行处理ueventd消息,并在对应目录生成device文件,

void ColdBoot::ForkSubProcesses() {  
    for (unsigned int i = 0; i < num_handler_subprocesses_; ++i) {  
        auto pid = fork();  
        if (pid < 0) {  
            PLOG(FATAL) << "fork() failed!";  
        }  
  
        if (pid == 0) {  
            UeventHandlerMain(i, num_handler_subprocesses_);//处理uevent信息,生成node  
        }  
  
        subprocess_pids_.emplace(pid);  
    }  
}  
  
void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {  
    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {  
        auto& uevent = uevent_queue_[i];  
         //处理uevent信息,生成node,最终调用mknod处理
        device_handler_.HandleDeviceEvent(uevent);  
    }  
    _exit(EXIT_SUCCESS);  
}  

DoRestoreCon

void ColdBoot::DoRestoreCon() {  
    selinux_android_restorecon("/sys", SELINUX_ANDROID_RESTORECON_RECURSE);  
    device_handler_.set_skip_restorecon(false);  
}  

启动过程中过程中一个重要的部分是SELinux restorecon的处理。由于ueventd创建了许多设备文件,而这些文件都有子设备文件,因此都需针对每个设备递归调用selinux_android_restorecon(...)。在cold_boot期间简单地在/ sys上递归执行restorecon更有效率,而不是在处理它的uevent时在每个设备上执行restorecon。

WaitForSubProcesses

若子线程出现crash时,crash信息反馈到ueventd中,init中会接收到此信号并重启ueventd,若出现多次,ueventd作为critical service,将会reboot to bootloader。
若子线程出现block导致没有及时退出,在init等待60s后也将reboot to bootloader。

void ColdBoot::WaitForSubProcesses() {  
    while (!subprocess_pids_.empty()) {  
        int status;  
        pid_t pid = TEMP_FAILURE_RETRY(waitpid(-1, &status, 0)); //断尝试进行操作。 结果:操作成功。或则发生错误返回-1,并把错误类型保存'errno'   
        if (pid == -1) {  
            PLOG(ERROR) << "waitpid() failed";  
            continue;  
        }  
  
        auto it = std::find(subprocess_pids_.begin(), subprocess_pids_.end(), pid);  
        if (it == subprocess_pids_.end()) continue;  
  
        if (WIFEXITED(status)) { //子进程是否为正常退出的,如果是,它会返回一个非零值  
            if (WEXITSTATUS(status) == EXIT_SUCCESS) { // 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值  
                subprocess_pids_.erase(it);  
            } else {  
                LOG(FATAL) << "subprocess exited with status " << WEXITSTATUS(status);  
            }  
        } else if (WIFSIGNALED(status)) {  
            LOG(FATAL) << "subprocess killed by signal " << WTERMSIG(status);  
        }  
    }  
}  

小结

综上:cold_boot过程经历了以下过程:
1)ueventd通过监听netlink socketuevent事件,将这些将这些uevents写入event队列中,执行/ sys遍历重新生成uevent,
2)ueventd 为每一个uevent单独fork子进程处理器uevent事件,创建device node。
3)与处理uevents的子进程并行,ueventd的主线程调用
selinux_android_restorecon()递归地在/ sys / class,/ sys / block和/ sys / devices上处理。
4)一旦restorecon操作完成,主线程调用waitpid()等待all子进程处理程序完成并退出。
当处理完上面这些事件,ueventd就会将cold_boot标记为完成,即创建"/dev/.coldboot_done"。

hotplug

在完成cold_boot之后,ueventd开始轮询处理热插拔事件,并将处理在cold_boot期间发生的事件。

uevent_listener.Poll([&device_handler](const Uevent& uevent) {  
    HandleFirmwareEvent(uevent);  
    device_handler.HandleDeviceEvent(uevent);  
    return ListenerAction::kContinue;  
});  

ueventd对uevent的处理

前面已简略的提到,ueventd接收kernel传来的uevent所用的netlink socket是在初始化coldboot时所创建的,处理uevent是在ForSubProcesses中实现的,本小节将就代码铺开这些部分,包括netlink socket的创建,uevent的监听,uevent的处理三个部分。

netlink socket的创建

直接从代码看创建流程:

system/core/init/ueventd.cpp  
int ueventd_main(int argc, char** argv) {  
    ......  
    DeviceHandler device_handler = CreateDeviceHandler();  
    UeventListener uevent_listener;  
  
    if (access(COLDBOOT_DONE, F_OK) != 0) {  
        ColdBoot cold_boot(uevent_listener, device_handler);  // 初始化coldboot
        cold_boot.Run();  
    }  
    ......   
}  
  
system/core/init/ueventd.cpp  
ColdBoot(UeventListener& uevent_listener, DeviceHandler& device_handler)  
    : uevent_listener_(uevent_listener),  // 初始化uevent_listener
      device_handler_(device_handler),  
      num_handler_subprocesses_(std::thread::hardware_concurrency() ?: 4) {}  
  
system/core/init/uevent_listener.cpp  
UeventListener::UeventListener() {  // 调用默认构造函数
    // is 2MB enough? udev uses 128MB!  
    device_fd_.reset(uevent_open_socket(2 * 1024 * 1024, true));  // 调用创建uevent socket的系统函数,
    if (device_fd_ == -1) {  
        LOG(FATAL) << "Could not open uevent socket";  
    }  
  
    fcntl(device_fd_, F_SETFL, O_NONBLOCK);  // 设置socket文件标志为nonblock
}  
  
system/core/libcutils/uevent.cpp  
int uevent_open_socket(int buf_sz, bool passcred) {  
    ......  
  
    // 创建netlink socket
    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);  
    if (s < 0) return -1;  
    ......   
}  

对应流程图为:


AndroidP之 Ueventd_第1张图片
ueventd netlink socket的创建

uevent的监听与接收

从netlink socket的创建中我们知道在调用ueventListener的默认构造函数时UeventListener()将socket fd赋值给了device_fd_,那么ueventd什么时候再去操作这个device_fd_。即何时监听uevent事件。
以下是uevent的监听与接收的流程图:

AndroidP之 Ueventd_第2张图片
uevent的监听

从流程图我们可以看到,uevent的监听与接收是在cold_boot阶段RegenerateUevents流程中处理的,这里我们重点看下第8和第9步,” 8. uevent_kernel_multicast_recv(device_fd_, msg, UEVENT_MSG_LEN);”对应着去netlink socket获取kernel产生的uevent信息,将信息存至msg返回;” 9. ParseEvent(msg, uevent)”对应着将msg信息解析,将解析出来的msg存至uevent结构体中。

static void ParseEvent(const char* msg, Uevent* uevent) { 

    uevent->partition_num = -1;  
    uevent->major = -1;  
    uevent->minor = -1;  
    uevent->action.clear();  
    uevent->path.clear();  
    uevent->subsystem.clear();  
    uevent->firmware.clear();  
    uevent->partition_name.clear();  
    uevent->device_name.clear();  
    // currently ignoring SEQNUM  
    while (*msg) {  
        if (!strncmp(msg, "ACTION=", 7)) {  
            msg += 7;  
            uevent->action = msg;  
        } else if (!strncmp(msg, "DEVPATH=", 8)) {  
            msg += 8;  
            uevent->path = msg;  
        } else if (!strncmp(msg, "SUBSYSTEM=", 10)) {  
            msg += 10;  
            uevent->subsystem = msg;  
        } else if (!strncmp(msg, "FIRMWARE=", 9)) {  
            msg += 9;  
            uevent->firmware = msg;  
        } else if (!strncmp(msg, "MAJOR=", 6)) {  
            msg += 6;  
            uevent->major = atoi(msg);  
        } else if (!strncmp(msg, "MINOR=", 6)) {  
            msg += 6;  
            uevent->minor = atoi(msg);  
        } else if (!strncmp(msg, "PARTN=", 6)) {  
            msg += 6;  
            uevent->partition_num = atoi(msg);  
        } else if (!strncmp(msg, "PARTNAME=", 9)) {  
            msg += 9;  
            uevent->partition_name = msg;  
        } else if (!strncmp(msg, "DEVNAME=", 8)) {  
            msg += 8;  
            uevent->device_name = msg;  
        }  
  
        // advance to after the next \0  
        while (*msg++)  
            ;  
    }  
  
    if (LOG_UEVENTS) {  
        LOG(INFO) << "event { '" << uevent->action << "', '" << uevent->path << "', '"  
                  << uevent->subsystem << "', '" << uevent->firmware << "', " << uevent->major  
                  << ", " << uevent->minor << " }";  
    }  
}  

从ParseEvent的实现可以看到,每一个uevent结构体都是重新创建并从msg中获取相关信息赋值的。
在”7. ReadUevent()”后,说”后”也不太准确,确切的说是没接收生成一个新的uevent之后,都会调用callback函数,而callback函数就是一开始在RegenerateUevents()传进来的,在这个callback中可以看到会将这uevent结构体存至uevent链表中即uevent_queue_,至此uevent的监听与接收处理完成。

void ColdBoot::RegenerateUevents() {  
    uevent_listener_.RegenerateUevents([this](const Uevent& uevent) {  
        HandleFirmwareEvent(uevent);  
  
        uevent_queue_.emplace_back(std::move(uevent));  
        return ListenerAction::kContinue;  
    });  
}  
  
ListenerAction UeventListener::RegenerateUeventsForDir(DIR* d,  
                                                       const ListenerCallback& callback) const {  
    int dfd = dirfd(d);  
  
    int fd = openat(dfd, "uevent", O_WRONLY);  
    if (fd >= 0) {  
        write(fd, "add\n", 4);  
        close(fd);  
  
        Uevent uevent;  
        while (ReadUevent(&uevent)) {  
            if (callback(uevent) == ListenerAction::kStop) return ListenerAction::kStop;  
        }  
    }  
    .....  
}  

uevent的处理

当接收到uevent且存储到ueventd中的uevent_queue_后,何时开始对这些uevent进行处理呢?
先来看流程图:


AndroidP之 Ueventd_第3张图片
uevent的处理

从流程图可以看到最终是通过调用mknod去创建device,我看来看下其中一些主要处理代码:

void ColdBoot::UeventHandlerMain(unsigned int process_num, unsigned int total_processes) {  
    for (unsigned int i = process_num; i < uevent_queue_.size(); i += total_processes) {  
        auto& uevent = uevent_queue_[i];  
        device_handler_.HandleDeviceEvent(uevent); //从uevent_queue_中一一获取uevent,开始创建device  
    }  
    _exit(EXIT_SUCCESS);  
}  
  
void DeviceHandler::HandleDeviceEvent(const Uevent& uevent) {  
    ......  
    // 根据设备类型的不同对devpath进行处理  
    if (uevent.subsystem == "block") {  
        block = true;  
        devpath = "/dev/block/" + Basename(uevent.path);  
  
        if (StartsWith(uevent.path, "/devices")) {  
            links = GetBlockDeviceSymlinks(uevent);  
        }  
    } else if (const auto subsystem =  
                   std::find(subsystems_.cbegin(), subsystems_.cend(), uevent.subsystem);  
               subsystem != subsystems_.cend()) {  
        devpath = subsystem->ParseDevPath(uevent);  
    } else if (uevent.subsystem == "usb") {  
        if (!uevent.device_name.empty()) {  
            devpath = "/dev/" + uevent.device_name;  
        } else {  
            // This imitates the file system that would be created  
            // if we were using devfs instead.  
            // Minors are broken up into groups of 128, starting at "001"  
            int bus_id = uevent.minor / 128 + 1;  
            int device_id = uevent.minor % 128 + 1;  
            devpath = StringPrintf("/dev/bus/usb/%03d/%03d", bus_id, device_id);  
        }  
    } else if (StartsWith(uevent.subsystem, "usb")) {  
        // ignore other USB events  
        return;  
    } else {  
        devpath = "/dev/" + Basename(uevent.path); // 默认情况下,路径前都加上"/dev"  
    }  
  
    mkdir_recursive(Dirname(devpath), 0755);  
  
    HandleDevice(uevent.action, devpath, block, uevent.major, uevent.minor, links);  
}  

从代码中可以看到此前存储至uevent_queue_的uevent是在UeventHandlerMain(i, num_handler_subprocesses_)这个处理里面提取出来的。

你可能感兴趣的:(AndroidP之 Ueventd)