Android8.0.0-r4 init进程分析 ueventd

本文深入解析了ueventd的工作原理及流程,包括设备节点的创建、权限设定等内容,并详细介绍了ueventd.rc配置文件的作用及处理流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ueventd主要是负责设备节点的创建、权限设定等一些列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。

ueventd实际和init是同一个binary,只是走了不同分支,可参看前一部分。

ueventd的整体代码比较简单,主要是三部分:

  •  解析ueventd.rc
  •  初始化设备信息
  •  循环polling uevent消息

主函数及相关功能如下如下:

1.ueventd.ueventd_main

   代码路径:/system/core/init/ueventd.cpp
    (http://androidxref.com/8.0.0_r4/xref/system/core/init/ueventd.cpp)

39int ueventd_main(int argc, char **argv)
40{
41    /*
42     * init sets the umask to 077 for forked processes. We need to
43     * create files with exact permissions, without modification by
44     * the umask.
45     */
46    umask(000);
47
48    /* Prevent fire-and-forget children from becoming zombies.
49     * If we should need to wait() for some children in the future
50     * (as opposed to none right now), double-forking here instead
51     * of ignoring SIGCHLD may be the better solution.
52     */
53    signal(SIGCHLD, SIG_IGN);
54
55    InitKernelLogging(argv);
56
57    LOG(INFO) << "ueventd started!";
58
59    selinux_callback cb;
60    cb.func_log = selinux_klog_callback;
61    selinux_set_callback(SELINUX_CB_LOG, cb);
62
63    ueventd_parse_config_file("/ueventd.rc");
64    ueventd_parse_config_file("/vendor/ueventd.rc");
65    ueventd_parse_config_file("/odm/ueventd.rc");
66
67    /*
68     * keep the current product name base configuration so
69     * we remain backwards compatible and allow it to override
70     * everything
71     * TODO: cleanup platform ueventd.rc to remove vendor specific
72     * device node entries (b/34968103)
73     */
74    std::string hardware = android::base::GetProperty("ro.hardware", "");
75    ueventd_parse_config_file(android::base::StringPrintf("/ueventd.%s.rc", hardware.c_str()).c_str());
76
77    device_init();
78
79    pollfd ufd;
80    ufd.events = POLLIN;
81    ufd.fd = get_device_fd();
82
83    while (true) {
84        ufd.revents = 0;
85        int nr = poll(&ufd, 1, -1);
86        if (nr <= 0) {
87            continue;
88        }
89        if (ufd.revents & POLLIN) {
90            handle_device_fd();
91        }
92    }
93
94    return 0;
95}

2.处理和解析ueventd.rc

   代码路径:/system/core/rootdir/ueventd.rc
    (http://androidxref.com/8.0.0_r4/xref/system/core/rootdir/ueventd.rc)

这部分相比init.rc来说,巨简单,没什么特别的。主要是通过rc文件,来控制目录节点的权限。如:

11/dev/null                 0666   root       root
12/dev/zero                 0666   root       root
13/dev/full                 0666   root       root
14/dev/ptmx                 0666   root       root
15/dev/tty                  0666   root       root
16/dev/random               0666   root       root
17/dev/urandom              0666   root       root
18# Make HW RNG readable by group system to let EntropyMixer read it.
19/dev/hw_random            0440   root       system
20/dev/ashmem               0666   root       root
21/dev/binder               0666   root       root
22/dev/hwbinder             0666   root       root
23/dev/vndbinder            0666   root       root

详情应该不需要展开,基本都能了解。

3.devices.device_init

   代码路径:/system/core/init/devices.cpp
    (http://androidxref.com/8.0.0_r4/xref/system/core/init/devices.cpp)

设备初始化

 kernel在加载设备时,会通过socket发送uevent事件给userspace, 在init里,通过接受这些uevent事件,来创建设备的节点。主要函数是device_init()

初始化函数为device_init,如下

1036void device_init(const char* path, coldboot_callback fn) {
1037    if (!sehandle) {
1038        sehandle = selinux_android_file_context_handle();
1039    }
1040    // open uevent socket and selinux status only if it hasn't been
1041    // done before
1042    if (device_fd == -1) {
1043        /* is 256K enough? udev uses 16MB! */
1044        device_fd.reset(uevent_open_socket(256 * 1024, true));
1045        if (device_fd == -1) {
1046            return;
1047        }
1048        fcntl(device_fd, F_SETFL, O_NONBLOCK);
1049        selinux_status_open(true);
1050    }
1051
1052    if (access(COLDBOOT_DONE, F_OK) == 0) {
1053        LOG(VERBOSE) << "Skipping coldboot, already done!";
1054        return;
1055    }
1056
1057    Timer t;
1058    coldboot_action_t act;
1059    if (!path) {
1060        act = coldboot("/sys/class", fn);
1061        if (!should_stop_coldboot(act)) {
1062            act = coldboot("/sys/block", fn);
1063            if (!should_stop_coldboot(act)) {
1064                act = coldboot("/sys/devices", fn);
1065            }
1066        }
1067    } else {
1068        act = coldboot(path, fn);
1069    }
1070
1071    // If we have a callback, then do as it says. If no, then the default is
1072    // to always create COLDBOOT_DONE file.
1073    if (!fn || (act == COLDBOOT_FINISH)) {
1074        close(open(COLDBOOT_DONE, O_WRONLY|O_CREAT|O_CLOEXEC, 0000));
1075    }
1076
1077    LOG(INFO) << "Coldboot took " << t;
1078}

4.uevent.open_uevent_socket
   代码路径:/system/core/libcutils/uevent.c
    (http://androidxref.com/8.0.0_r4/xref/system/core/libcutils/uevent.c)

open_uevent_socket();

这是打开uevent的socket。这里的uevent是用到netlink中的内核事件向用户态通知(NETLINK_KOBJECT_UEVENT)功能,是内核和用户态进行双向数据传输的非常好的方式,除了eventd外,netd和vold也是使用uevent的。
初始化函数中,比较要注意的coldboot这个函数,字面意思是冷启动,它的作用是,对那些在uventd启动前,已经add上的驱动,再重新操作一下,让他们再发一次event消息,上层号针对性处理。

这里对 /sys/class,/sys/block和/sys/devices下的设备遍历一遍:

104int uevent_open_socket(int buf_sz, bool passcred)
105{
106    struct sockaddr_nl addr;
107    int on = passcred;
108    int s;
109
110    memset(&addr, 0, sizeof(addr));
111    addr.nl_family = AF_NETLINK;
112    addr.nl_pid = getpid();
113    addr.nl_groups = 0xffffffff;
114
115    s = socket(PF_NETLINK, SOCK_DGRAM | SOCK_CLOEXEC, NETLINK_KOBJECT_UEVENT);
116    if(s < 0)
117        return -1;
118
119    /* buf_sz should be less than net.core.rmem_max for this to succeed */
120    if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &buf_sz, sizeof(buf_sz)) < 0) {
121        close(s);
122        return -1;
123    }
124
125    setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
126
127    if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
128        close(s);
129        return -1;
130    }
131
132    return s;
133}
write(fd, "add\n", 4)激活内核,重发add事件的uevent,handle_device_fd();针对event消息,做响应的处理。

5.devices.handle_device_fd
   代码路径:/system/core/init/devices.cpp
    (http://androidxref.com/8.0.0_r4/xref/system/core/init/devices.cpp)

uevent消息处理

初始化好了之后,daemon程序只要polling新的event事件即可,polling到了,就调用handle_device_fd();来处理,可以看看这个函数:

947coldboot_action_t handle_device_fd(coldboot_callback fn)
948{
949    coldboot_action_t ret = handle_device_fd_with(
950        [&](uevent* uevent) -> coldboot_action_t {
951            if (selinux_status_updated() > 0) {
952                struct selabel_handle *sehandle2;
953                sehandle2 = selinux_android_file_context_handle();
954                if (sehandle2) {
955                    selabel_close(sehandle);
956                    sehandle = sehandle2;
957                }
958            }
959
960            // default is to always create the devices
961            coldboot_action_t act = COLDBOOT_CREATE;
962            if (fn) {
963                act = fn(uevent);
964            }
965
966            if (act == COLDBOOT_CREATE || act == COLDBOOT_STOP) {
967                handle_device_event(uevent);
968                handle_firmware_event(uevent);
969            }
970
971            return act;
972        });
973
974    return ret;
975}

handle_device_event功能就是接受内核发的event消息,然后parser此消息,处理对应的消息事件。

820static void handle_device_event(struct uevent *uevent)
821{
822    if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change") || !strcmp(uevent->action, "online"))
823        fixup_sys_perms(uevent->path, uevent->subsystem);
824
825    if (!strncmp(uevent->subsystem, "block", 5)) {
826        handle_block_device_event(uevent);
827    } else if (!strncmp(uevent->subsystem, "platform", 8)) {
828        handle_platform_device_event(uevent);
829    } else {
830        handle_generic_device_event(uevent);
831    }
832}

handle_firmware_event主要功能,就是根据发过来的uevent,创建/删除设备节点,同时以ueventd.rc中描述的权限设置更新一些节点权限。

901static void handle_firmware_event(uevent* uevent) {
902    if (strcmp(uevent->subsystem, "firmware")) return;
903    if (strcmp(uevent->action, "add")) return;
904
905    // Loading the firmware in a child means we can do that in parallel...
906    // (We ignore SIGCHLD rather than wait for our children.)
907    pid_t pid = fork();
908    if (pid == 0) {
909        Timer t;
910        process_firmware_event(uevent);
911        LOG(INFO) << "loading " << uevent->path << " took " << t;
912        _exit(EXIT_SUCCESS);
913    } else if (pid == -1) {
914        PLOG(ERROR) << "could not fork to process firmware event for " << uevent->firmware;
915    }
916}
如果有协处理器, 还要下载协处理器的firmware,这里是处理协处理器要下载firmware的指令,fork一个子进程处理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值