android init进程分析 ueventd

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

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

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

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

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

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

[cpp] view plain copy
 
  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.   
  8.  //解析和处理ueventd的rc文件  
  9.     import_kernel_cmdline(0, import_kernel_nv);  
  10.     get_hardware_name(hardware, &revision);  
  11.     ueventd_parse_config_file("/ueventd.rc");  
  12.     snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);  
  13.     ueventd_parse_config_file(tmp);  
  14.   
  15.  // 设备初始化  
  16.     device_init();  
  17.   
  18.   
  19.  // polling uevent消息,对设备进行管理  
  20.     ufd.events = POLLIN;  
  21.     ufd.fd = get_device_fd();  
  22.     while(1) {  
  23.         ufd.revents = 0;  
  24.         nr = poll(&ufd, 1, -1);  
  25.         if (nr <= 0)  
  26.             continue;  
  27.         if (ufd.revents == POLLIN)  
  28.                handle_device_fd(); // polling到消息,处理event消息  
  29.     }  
  30. }  

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

 

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

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

 

 

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

初始化函数为device_init,如下

 

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

 

 

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

 

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

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

 

 

uevent消息处理

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

[cpp] view plain copy
 
  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.   
  9.         msg[n] = '\0';  
  10.         msg[n+1] = '\0';  
  11.   
  12.         struct uevent uevent;  
  13.         parse_event(msg, &uevent);  
  14.   
  15.         handle_device_event(&uevent);  
  16.         handle_firmware_event(&uevent);  
  17.     }  
  18. }  

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

这里:

[cpp] view plain copy
 
  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.   
  6.     if (!strncmp(uevent->subsystem, "block", 5)) {  
  7.         handle_block_device_event(uevent);  
  8.     } else if (!strncmp(uevent->subsystem, "platform", 8)) {  
  9.         handle_platform_device_event(uevent);  
  10.     } else {  
  11.         handle_generic_device_event(uevent);  
  12.     }  
  13. }  

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

 

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

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

posted @ 2016-12-24 16:09  随风飘落的雨滴  阅读(1562)  评论(0编辑  收藏  举报