android init进程分析 ueventd

须知少年凌云志,曾许人间第一流。这篇文章主要讲述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. }   
【android init进程分析 ueventd】如果有协处理器, 还要下载协处理器的firmware,这里是处理协处理器要下载firmware的指令,fork一个子进程处理。

    推荐阅读