转自:http://blog.csdn.net/freshui/article/details/2132299

(懒人最近想起我还有csdn好久没打理了,这个Android init躺在我的草稿箱中快5年了,稍微改改发出来吧)

ueventd主要是负责设备节点的创建、权限设定等一些列工作。服务通过使用uevent,监控驱动发送的消息,做进一步处理。
ueventd实际和init是同一个binary,只是走了不同分支,可参看前一部分。

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

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

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

  1. int ueventd_main(int argc, char **argv)
  2. {
  3. // 和init一样,没有std输入输出
  4. open_devnull_stdio();
  5. // 初始化kernel log,让ueventd的log,通过kernel printk的log输出到串口中
  6. klog_init();
  7. //解析和处理ueventd的rc文件
  8. import_kernel_cmdline(0, import_kernel_nv);
  9. get_hardware_name(hardware, &revision);
  10. ueventd_parse_config_file("/ueventd.rc");
  11. snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);
  12. ueventd_parse_config_file(tmp);
  13. // 设备初始化
  14. device_init();
  15. // polling uevent消息,对设备进行管理
  16. ufd.events = POLLIN;
  17. ufd.fd = get_device_fd();
  18. while(1) {
  19. ufd.revents = 0;
  20. nr = poll(&ufd, 1, -1);
  21. if (nr <= 0)
  22. continue;
  23. if (ufd.revents == POLLIN)
  24. handle_device_fd(); // polling到消息,处理event消息
  25. }
  26. }

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

  1. /dev/ttyUSB2       0666   radio   radio
  2. /dev/ts0710mux*           0640   radio      radio
  3. /dev/ppp                  0666   radio      vpn
  4. # sysfs properties
  5. /sys/devices/virtual/input/input*   enable      0666  system   system
  6. /sys/devices/virtual/input/input*   poll_delay  0666  system   system

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

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

初始化函数为device_init,如下

  1. void device_init(void)
  2. {
  3. suseconds_t t0, t1;
  4. struct stat info;
  5. int fd;
  6. sehandle = NULL;
  7. if (is_selinux_enabled() > 0) {
  8. sehandle = selinux_android_file_context_handle();
  9. }
  10. /* is 256K enough? udev uses 16MB! */
  11. device_fd = uevent_open_socket(256*1024, true);
  12. if(device_fd < 0)
  13. return;
  14. fcntl(device_fd, F_SETFD, FD_CLOEXEC);
  15. fcntl(device_fd, F_SETFL, O_NONBLOCK);
  16. if (stat(coldboot_done, &info) < 0) {
  17. t0 = get_usecs();
  18. coldboot("/sys/class");
  19. coldboot("/sys/block");
  20. coldboot("/sys/devices");
  21. t1 = get_usecs();
  22. fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);
  23. close(fd);
  24. log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));
  25. } else {
  26. log_event_print("skipping coldboot, already done\n");
  27. }
  28. }

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

  1. static void do_coldboot(DIR *d)
  2. {
  3. struct dirent *de;
  4. int dfd, fd;
  5. dfd = dirfd(d);
  6. fd = openat(dfd, "uevent", O_WRONLY);
  7. if(fd >= 0) {
  8. write(fd, "add\n", 4);
  9. close(fd);
  10. handle_device_fd();
  11. }
  12. while((de = readdir(d))) {
  13. DIR *d2;
  14. if(de->d_type != DT_DIR || de->d_name[0] == '.')
  15. continue;
  16. fd = openat(dfd, de->d_name, O_RDONLY | O_DIRECTORY);
  17. if(fd < 0)
  18. continue;
  19. d2 = fdopendir(fd);
  20. if(d2 == 0)
  21. close(fd);
  22. else {
  23. do_coldboot(d2);
  24. closedir(d2);
  25. }
  26. }
  27. }

write(fd, "add\n", 4)激活内核,重发add事件的uevent,handle_device_fd();针对event消息,做响应的处理。

uevent消息处理

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

  1. void handle_device_fd()
  2. {
  3. char msg[UEVENT_MSG_LEN+2];
  4. int n;
  5. while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {
  6. if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */
  7. continue;
  8. msg[n] = '\0';
  9. msg[n+1] = '\0';
  10. struct uevent uevent;
  11. parse_event(msg, &uevent);
  12. handle_device_event(&uevent);
  13. handle_firmware_event(&uevent);
  14. }
  15. }

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

这里:

  1. static void handle_device_event(struct uevent *uevent)
  2. {
  3. if (!strcmp(uevent->action,"add") || !strcmp(uevent->action, "change"))
  4. fixup_sys_perms(uevent->path);
  5. if (!strncmp(uevent->subsystem, "block", 5)) {
  6. handle_block_device_event(uevent);
  7. } else if (!strncmp(uevent->subsystem, "platform", 8)) {
  8. handle_platform_device_event(uevent);
  9. } else {
  10. handle_generic_device_event(uevent);
  11. }
  12. }

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

  1. static void handle_firmware_event(struct uevent *uevent)
  2. {
  3. pid_t pid;
  4. int ret;
  5. if(strcmp(uevent->subsystem, "firmware"))
  6. return;
  7. if(strcmp(uevent->action, "add"))
  8. return;
  9. /* we fork, to avoid making large memory allocations in init proper */
  10. pid = fork();
  11. if (!pid) {
  12. process_firmware_event(uevent);
  13. exit(EXIT_SUCCESS);
  14. }
  15. }

如果有协处理器, 还要下载协处理器的firmware,这里是处理协处理器要下载firmware的指令,fork一个子进程处理。

android init进程分析 ueventd的更多相关文章

  1. android init进程分析 init脚本解析和处理

    (懒人近期想起我还有csdn好久没打理了.这个android init躺在我的草稿箱中快5年了.略微改改发出来吧) RC文件格式 rc文件是linux中常见的启动载入阶段运行的文件.rc是run co ...

  2. Android init进程概述

    init进程,其程序位于根文件系统中,在kernle自行启动后,其中的 start_kernel 函数把根文件系统挂载到/目录后,在 rest_init 函数中通过 kernel_thread(ker ...

  3. Android Init进程命令的执行和服务的启动

    这里开始分析init进程中配置文件的解析,在配置文件中的命令的执行和服务的启动. 首先init是一个可执行文件,它的对应的Makfile是init/Android.mk. Android.mk定义了i ...

  4. ANDROID init进程

    init简要 init是Android上启动的第一个用户态进程. 执行序列是: start_kernel() -> rest_init() -> kernel_init() -> i ...

  5. 构建根文件系统之init进程分析

    busybox是ls.cp等命令的集合. 执行ls时,实际上是执行了busybox ls 执行cp时,实际上是执行了busybox cp 分析init程序之前,再让我们回想一下我们的目标:u-boot ...

  6. Android 8.1 源码_启动篇(一) -- 深入研究 init(转 Android 9.0 分析)

    前言 init进程,它是一个由内核启动的用户级进程,当Linux内核启动之后,运行的第一个进程是init,这个进程是一个守护进程,确切的说,它是Linux系统中用户控件的第一个进程,所以它的进程号是1 ...

  7. Android 7.0 启动篇 — init原理(二)(转 Android 9.0 分析)

    ========================================================          ================================== ...

  8. Android 7.0 启动篇 — init原理(一)(转 Android 9.0 分析)

    ========================================================          ================================== ...

  9. 第4阶段——制作根文件系统之分析init进程(2)

    本节目标: (1) 了解busybox(init进程和命令都放在busybox中) (2) 创建SI工程,分析busybox源码来知道init进程做了哪些事情 (3)  分析busybox中init进 ...

随机推荐

  1. python day2:python 初识(二)

    大纲: 一.运算符 1.算数运算符 notice: 除法运算在python2.7和python3.x 的不同 2.比较运算符 3.赋值运算符 4.逻辑运算符 5.成员运算符 二.基本数据类型和方法介绍 ...

  2. maven 打包

    使用命令行形式打包 1.配置maven环境变量,在变量path中加入maven路径. 2.在要打包的项目目录下使用:Ctrl+shift+鼠标右键点击,点击 在此处打开命令行窗口. 在打开的命令行窗口 ...

  3. Jsoup 使用教程:输入

    使用背景: 使用网络爬虫(或者手动复制),从别的网站上下载下来的内容,都是一堆的html,很多标签.样式 等等都可能是你所不需要的,或者 想要变成你想要的样式.那么该怎么办呢? 我们知道,每一个网页都 ...

  4. String()与 toString()

    我们知道String()与 .toString()都是可以转换为字符串类型,但是String()与 .toString()的还是有区别的 1..toString()可以将所有的的数据都转换为字符串,但 ...

  5. 【UOJ#67】新年的毒瘤 Tarjan 割点

    #67. 新年的毒瘤 UOJ直接黏贴会炸...    还是戳这里吧: http://uoj.ac/problem/67#tab-statement Solution 看到这题的标签就进来看了一眼. 想 ...

  6. bzoj 4318 OSU!

    期望dp. 考虑问题的简化版:一个数列有n个数,每位有pi的概率为1,否则为0.求以每一位结尾的全为1的后缀长度的期望. 递推就好了. l1[i]=(l1[i-1]+1)*p[i]+0*(1-p[i] ...

  7. 关于Scala的一些感想(一)

    最近在完成自己的开源项目Application-center的时候,使用了Scala编程语言. 在使用了一段时间下来以后,有一些不是很"清晰"的感受,说实话我自己还没有很好的整理清 ...

  8. 关于WEB项目的一点想法

    有点失落.迷茫,差点在上班的时候发了火.原因是之前离职的一位同事,在代码里不加注释,而且百般偷懒,致使很多应该的验证没有验证,很多应该考虑到的情况没有考虑.因为是老员工,我相比他来说是新员工.气势上总 ...

  9. grafana日志分析界面及导出的json文件

    日志分析面板导出的json文件,效果图如下: 下载地址:http://files.cnblogs.com/files/xiaoming279/%E9%9D%A2%E6%9D%BF.zip 主机面板 主 ...

  10. 看完《Thinking in Java》后,我觉得自己就是一个不懂编程的小孩子,如何快速摆脱这种自卑感

    我虽然不懂java也不懂程序员,但我理解这种心情.当看到自己还算自信的专业领域中一部超越自己水平很多的作品或比自己优秀太多的人,难免会感到震惊,继而进行自我否定.就像我曾经非常喜欢写作,在杂志和校报上 ...