Android uevent进程源码分析

Android Init进程源码分析中讲到init进程会依次执行被加入到待执行队列action_queue中的Action,在init.rc中我们有这么一段配置:

[plain]  view plain copy
  1. 11 on early-init  
  2. 12     # Set init and its forked children's oom_adj.  
  3. 13     write /proc/1/oom_adj -16  
  4. 14   
  5. 15     start ueventd  
  6. 16   
  7. 17 # create mountpoints  
  8. 18     mkdir /mnt 0775 root system  
对于early-init 这个section 在 Android Init进程源码分析 一文中已经介绍了在解析完init.rc文件后被添加到了action_queue队列

[cpp]  view plain copy
  1. action_for_each_trigger("early-init", action_add_queue_tail);  
因此Init进程会首先启动ueventd进程,关键字start用于启动一个服务进程,该关键字对应的执行函数为:

[cpp]  view plain copy
  1. KEYWORD(start,       COMMAND, 1, do_start)  
在解析命令行时,被添加到Action的commands链表中:

[cpp]  view plain copy
  1. cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);  
  2. cmd->func = kw_func(kw);  
  3. cmd->nargs = nargs;  
  4. memcpy(cmd->args, args, sizeof(char*) * nargs);  
  5. list_add_tail(&act->commands, &cmd->clist);  

ueventd进程的执行代码和Init进程的执行代码被编译到了同一个可执行程序Init中了,通过查看Android.mk文件即可以得到证实:

[cpp]  view plain copy
  1. LOCAL_SRC_FILES:= \  
  2.     builtins.c \  
  3.     init.c \  
  4.     devices.c \  
  5.     property_service.c \  
  6.     util.c \  
  7.     parser.c \  
  8.     logo.c \  
  9.     keychords.c \  
  10.     signal_handler.c \  
  11.     init_parser.c \  
  12.     ueventd.c \  
  13.     ueventd_parser.c  
  14.   
  15. ifeq ($(strip $(INIT_BOOTCHART)),true)  
  16. LOCAL_SRC_FILES += bootchart.c  
  17. LOCAL_CFLAGS    += -DBOOTCHART=1  
  18. endif  
  19.   
  20. ifeq ($(BOARD_HAVE_BLUETOOTH_BCM),true)  
  21. LOCAL_CFLAGS += \  
  22.     -DBOARD_HAVE_BLUETOOTH_BCM  
  23. endif  
  24.   
  25. ifneq (,$(filter userdebug eng,$(TARGET_BUILD_VARIANT)))  
  26. LOCAL_CFLAGS += -DALLOW_LOCAL_PROP_OVERRIDE=1  
  27. endif  
  28.   
  29. LOCAL_MODULE:= init  
  30.   
  31. LOCAL_FORCE_STATIC_EXECUTABLE := true  
  32. LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)  
  33. LOCAL_UNSTRIPPED_PATH := $(TARGET_ROOT_OUT_UNSTRIPPED)  
  34.   
  35. LOCAL_STATIC_LIBRARIES := libfs_mgr libcutils libc  
  36.   
  37. ifeq ($(HAVE_SELINUX),true)  
  38. LOCAL_STATIC_LIBRARIES += libselinux  
  39. LOCAL_C_INCLUDES += external/libselinux/include  
  40. LOCAL_CFLAGS += -DHAVE_SELINUX  
  41. endif  
  42.   
  43. include $(BUILD_EXECUTABLE)  
  44.   
  45. # Make a symlink from /sbin/ueventd to /init  
  46. SYMLINKS := $(TARGET_ROOT_OUT)/sbin/ueventd  
  47. $(SYMLINKS): INIT_BINARY := $(LOCAL_MODULE)  
  48. $(SYMLINKS): $(LOCAL_INSTALLED_MODULE) $(LOCAL_PATH)/Android.mk  
  49.     @echo "Symlink: $@ -> ../$(INIT_BINARY)"  
  50.     @mkdir -p $(dir $@)  
  51.     @rm -rf $@  
  52.     $(hide) ln -sf ../$(INIT_BINARY) $@  
  53.   
  54. ALL_DEFAULT_INSTALLED_MODULES += $(SYMLINKS)  
uevent以软链接的方式链接到了Init可执行程序,通过查看手机中的uevent程序可知:

因为ueventd 和 Init 在同一个可执行文件下,因此在启动ueventd进程时,进程入口函数依然是system\core\init\init.c文件中的main函数:

[cpp]  view plain copy
  1. int main(int argc, char **argv)  
  2. {  
  3.     int fd_count = 0;  
  4.     struct pollfd ufds[4];  
  5.     char *tmpdev;  
  6.     char* debuggable;  
  7.     char tmp[32];  
  8.     int property_set_fd_init = 0;  
  9.     int signal_fd_init = 0;  
  10.     int keychord_fd_init = 0;  
  11.     bool is_charger = false;  
  12.   
  13.     if (!strcmp(basename(argv[0]), "ueventd"))  
  14.         return ueventd_main(argc, argv);  
  15.   
  16.     .....  
  17. }  

basename 函数从启动的程序路径下截取应用名称,如果启动的应用为ueventd进程,则跳转到ueventd_main函数作为应用的入口函数:

[cpp]  view plain copy
  1. int ueventd_main(int argc, char **argv)  
  2. {  
  3.     struct pollfd ufd;  
  4.     int nr;  
  5.     char tmp[32];  
  6.   
  7.     /* 
  8.      * init sets the umask to 077 for forked processes. We need to 
  9.      * create files with exact permissions, without modification by 
  10.      * the umask. 
  11.      */  
  12.     umask(000);  
  13.   
  14.     /* Prevent fire-and-forget children from becoming zombies. 
  15.      * If we should need to wait() for some children in the future 
  16.      * (as opposed to none right now), double-forking here instead 
  17.      * of ignoring SIGCHLD may be the better solution. 
  18.      */  
  19.     signal(SIGCHLD, SIG_IGN);  
  20.   
  21.     open_devnull_stdio();  
  22.     klog_init();  
  23.   
  24.     INFO("starting ueventd\n");  
  25.   
  26.     /* Respect hardware passed in through the kernel cmd line. Here we will look 
  27.      * for androidboot.hardware param in kernel cmdline, and save its value in 
  28.      * hardware[]. */  
  29.     import_kernel_cmdline(0, import_kernel_nv);  
  30.   
  31.     get_hardware_name(hardware, &revision);  
  32.     //解析ueventd.rc配置文件  
  33.     ueventd_parse_config_file("/ueventd.rc");  
  34.     //解析ueventd.xxx.rc 配置文件  
  35.     snprintf(tmp, sizeof(tmp), "/ueventd.%s.rc", hardware);  
  36.     ueventd_parse_config_file(tmp);  
  37.     //设备节点初始化  
  38.     device_init();  
  39.       
  40.     ufd.events = POLLIN;  
  41.     ufd.fd = get_device_fd();  
  42.     //进入闭环监控模式  
  43.     while(1) {   
  44.         ufd.revents = 0;  
  45.         //监控uevent socket的连接,参数为-1表示无限超时,在没有事件发生时函数阻塞监控  
  46.         nr = poll(&ufd, 1, -1);  
  47.         if (nr <= 0)  
  48.             continue;  
  49.         if (ufd.revents == POLLIN)  
  50.                //设备事件处理  
  51.                handle_device_fd();  
  52.     }  
  53. }  
该函数前面几个步骤和init进程基本相同,这里就不在详细介绍了,可参考 Android Init进程源码分析 ,在初始化完标准输入输出、log、导入命令行参数后,将解析uevent.rc文件和一个同硬件特定硬件相关的ueventd.XXX.rc文件,ueventd.rc文件内容如下所示:

[plain]  view plain copy
  1. /dev/null                 0666   root       root  
  2. /dev/zero                 0666   root       root  
  3. /dev/full                 0666   root       root  
  4. /dev/ptmx                 0666   root       root  
  5. /dev/tty                  0666   root       root  
  6. /dev/random               0666   root       root  
  7. /dev/urandom              0666   root       root  
  8. /dev/ashmem               0666   root       root  
  9. /dev/binder               0666   root       root  
  10.   
  11. # Anyone can read the logs, but if they're not in the "logs"  
  12. # group, then they'll only see log entries for their UID.  
  13. /dev/log/*                0666   root       log  
  14.   
  15. # the msm hw3d client device node is world writable/readable.  
  16. /dev/msm_hw3dc            0666   root       root  
  17.   
  18. # gpu driver for adreno200 is globally accessible  
  19. /dev/kgsl                 0666   root       root  
  20.   
  21. # these should not be world writable  
  22. /dev/diag                 0660   radio      radio  
  23. /dev/diag_arm9            0660   radio      radio  
  24. /dev/android_adb          0660   adb        adb  
  25. /dev/android_adb_enable   0660   adb        adb  
  26. /dev/ttyMSM0              0600   bluetooth  bluetooth  
  27. /dev/uinput               0660   system     bluetooth  

在system\core\init\ueventd.c文件中,使用ueventd_parse_config_file函数来对ueventd.rc进行解析,接下来详细分析整个解析过程:

[cpp]  view plain copy
  1. int ueventd_parse_config_file(const char *fn)  
  2. {  
  3.     char *data;  
  4.     //读取文件内容  
  5.     data = read_file(fn, 0);  
  6.     if (!data) return -1;  
  7.     //解析整个rc文件内容  
  8.     parse_config(fn, data);  
  9.     DUMP();  
  10.     return 0;  
  11. }  

以上步骤和Init进程解析init.rc文件的步骤相同,不过这里调用的parse_config函数不同,该函数是专门用于解析ueventd.rc文件的,具体解析过程如下:

[cpp]  view plain copy
  1. static void parse_config(const char *fn, char *s)  
  2. {  
  3.     struct parse_state state;  
  4.     char *args[UEVENTD_PARSER_MAXARGS];  
  5.     int nargs;  
  6.     nargs = 0;  
  7.     state.filename = fn; //设置解析文件的路径  
  8.     state.line = 1;  
  9.     state.ptr = s;//文件内容  
  10.     state.nexttoken = 0;  
  11.     state.parse_line = parse_line_device; //设置每行解析回调函数  
  12.     for (;;) {  
  13.         //从文件内容中查找token,与init.rc文件类似  
  14.         int token = next_token(&state);   
  15.         switch (token) {  
  16.         //文件结束  
  17.         case T_EOF:  
  18.             state.parse_line(&state, 0, 0);  
  19.             return;  
  20.         //新的一行  
  21.         case T_NEWLINE:  
  22.             if (nargs) {  
  23.                 //调用行解析函数解析每一行  
  24.                 state.parse_line(&state, nargs, args);  
  25.                 nargs = 0;  
  26.             }  
  27.             break;  
  28.         case T_TEXT:  
  29.             if (nargs < UEVENTD_PARSER_MAXARGS) {  
  30.                 args[nargs++] = state.text;  
  31.             }  
  32.             break;  
  33.         }  
  34.     }  
  35. }  

函数首先查找指定的token,然后对不同的token做不同的处理,对于发现新行时,调用parse_line_device函数对每一行进行详细解析,该函数实现如下:

[cpp]  view plain copy
  1. static void parse_line_device(struct parse_state* state, int nargs, char **args)  
  2. {  
  3.     set_device_permission(nargs, args);  
  4. }  

函数直接调用set_device_permission来实现,ueventd.rc文件每一行的书写规则为:

非sysfs 设备文件:

|name|  |permission| |user| |group|

/dev/cam   0660   root       ca

sysfs 设备文件属性:
/sys/devices/virtual/input/input*   enable      0660  root   input

[cpp]  view plain copy
  1. void set_device_permission(int nargs, char **args)  
  2. {  
  3.     char *name;  
  4.     char *attr = 0;  
  5.     mode_t perm;  
  6.     uid_t uid;  
  7.     gid_t gid;  
  8.     int prefix = 0;  
  9.     char *endptr;  
  10.     int ret;  
  11.     char *tmp = 0;  
  12.   
  13.     if (nargs == 0)  
  14.         return;  
  15.   
  16.     if (args[0][0] == '#')  
  17.         return;  
  18. /*  |name|  |permission| |user| |group|  */  
  19.     name = args[0];  
  20.     if (!strncmp(name,"/sys/", 5) && (nargs == 5)) {  
  21.         INFO("/sys/ rule %s %s\n",args[0],args[1]);  
  22.         attr = args[1];  
  23.         args++;  
  24.         nargs--;  
  25.     }  
  26.     //参数检查  
  27.     if (nargs != 4) {  
  28.         ERROR("invalid line ueventd.rc line for '%s'\n", args[0]);  
  29.         return;  
  30.     }  
  31.     /* If path starts with mtd@ lookup the mount number. */  
  32.     if (!strncmp(name, "mtd@", 4)) {  
  33.         int n = mtd_name_to_number(name + 4);  
  34.         if (n >= 0)  
  35.             asprintf(&tmp, "/dev/mtd/mtd%d", n);  
  36.         name = tmp;  
  37.     } else {  
  38.         int len = strlen(name);  
  39.         if (name[len - 1] == '*') {  
  40.             prefix = 1;  
  41.             name[len - 1] = '\0';  
  42.         }  
  43.     }  
  44.     //权限检查  
  45.     perm = strtol(args[1], &endptr, 8);  
  46.     if (!endptr || *endptr != '\0') {  
  47.         ERROR("invalid mode '%s'\n", args[1]);  
  48.         free(tmp);  
  49.         return;  
  50.     }  
  51.     //从android_ids数组中查找uid  
  52.     ret = get_android_id(args[2]);  
  53.     if (ret < 0) {  
  54.         ERROR("invalid uid '%s'\n", args[2]);  
  55.         free(tmp);  
  56.         return;  
  57.     }  
  58.     uid = ret;  
  59.     //从android_ids数组中查找gid  
  60.     ret = get_android_id(args[3]);  
  61.     if (ret < 0) {  
  62.         ERROR("invalid gid '%s'\n", args[3]);  
  63.         free(tmp);  
  64.         return;  
  65.     }  
  66.     gid = ret;  
  67.     //为设备文件添加权限  
  68.     add_dev_perms(name, attr, perm, uid, gid, prefix);  
  69.     free(tmp);  
  70. }  

首先检查参数的合法性,并根据参数查找uid、gid,对不同的用户和组的uid、gid已经事先配置在数组android_ids中了,如下:

[cpp]  view plain copy
  1. static const struct android_id_info android_ids[] = {  
  2.     { "root",      AID_ROOT, },  
  3.     { "system",    AID_SYSTEM, },  
  4.     { "radio",     AID_RADIO, },  
  5.     { "bluetooth", AID_BLUETOOTH, },  
  6.     { "graphics",  AID_GRAPHICS, },  
  7.     { "input",     AID_INPUT, },  
  8.     { "audio",     AID_AUDIO, },  
  9.     { "camera",    AID_CAMERA, },  
  10.     { "log",       AID_LOG, },  
  11.     { "compass",   AID_COMPASS, },  
  12.     { "mount",     AID_MOUNT, },  
  13.     { "wifi",      AID_WIFI, },  
  14.     { "dhcp",      AID_DHCP, },  
  15.     { "adb",       AID_ADB, },  
  16.     { "install",   AID_INSTALL, },  
  17.     { "media",     AID_MEDIA, },  
  18.     { "drm",       AID_DRM, },  
  19.     { "mdnsr",     AID_MDNSR, },  
  20.     { "nfc",       AID_NFC, },  
  21.     { "drmrpc",    AID_DRMRPC, },  
  22.     { "shell",     AID_SHELL, },  
  23.     { "cache",     AID_CACHE, },  
  24.     { "diag",      AID_DIAG, },  
  25.     { "net_bt_admin", AID_NET_BT_ADMIN, },  
  26.     { "net_bt",    AID_NET_BT, },  
  27.     { "sdcard_r",  AID_SDCARD_R, },  
  28.     { "sdcard_rw", AID_SDCARD_RW, },  
  29.     { "media_rw",  AID_MEDIA_RW, },  
  30.     { "vpn",       AID_VPN, },  
  31.     { "keystore",  AID_KEYSTORE, },  
  32.     { "usb",       AID_USB, },  
  33.     { "mtp",       AID_MTP, },  
  34.     { "gps",       AID_GPS, },  
  35.     { "inet",      AID_INET, },  
  36.     { "net_raw",   AID_NET_RAW, },  
  37.     { "net_admin", AID_NET_ADMIN, },  
  38.     { "net_bw_stats", AID_NET_BW_STATS, },  
  39.     { "net_bw_acct", AID_NET_BW_ACCT, },  
  40.     { "misc",      AID_MISC, },  
  41.     { "nobody",    AID_NOBODY, },  
  42. };  

这些uid、gid都是以宏的形式被定义:

[cpp]  view plain copy
  1. #define AID_ROOT             0  /* traditional unix root user */  
  2. #define AID_SYSTEM        1000  /* system server */  
  3. #define AID_RADIO         1001  /* telephony subsystem, RIL */  
  4. #define AID_BLUETOOTH     1002  /* bluetooth subsystem */  

通过调用get_android_id函数在数组android_ids中查找对应的uid、gid

[cpp]  view plain copy
  1. static int get_android_id(const char *id)  
  2. {  
  3.     unsigned int i;  
  4.     for (i = 0; i < ARRAY_SIZE(android_ids); i++)  
  5.         if (!strcmp(id, android_ids[i].name))  
  6.             return android_ids[i].aid;  
  7.     return 0;  
  8. }  

函数实现比较简单,通过遍历数组,并匹配数组元素的name属性来查找指定name的uid或gid。

最后通过add_dev_perms函数来设置设备文件的操作权限,该函数定义在system\core\init\devices.c文件中,在该文件中声明了三个链表:

[cpp]  view plain copy
  1. static list_declare(sys_perms);  
  2. static list_declare(dev_perms);  
  3. static list_declare(platform_names);  

add_dev_perms函数就是将解析得到的设备及设备属性,添加到指定的链表中,

使用解析得到的内容来创建一个perm_node变量,并根据条件添加到sys_perms或dev_perms链表中。

[cpp]  view plain copy
  1. int add_dev_perms(const char *name, const char *attr,  
  2.                   mode_t perm, unsigned int uid, unsigned int gid,  
  3.                   unsigned short prefix) {  
  4.     //创建perm_node  
  5.     struct perm_node *node = calloc(1, sizeof(*node));  
  6.     if (!node)  
  7.         return -ENOMEM;  
  8.   
  9.     node->dp.name = strdup(name);  
  10.     if (!node->dp.name)  
  11.         return -ENOMEM;  
  12.   
  13.     if (attr) {  
  14.         node->dp.attr = strdup(attr);  
  15.         if (!node->dp.attr)  
  16.             return -ENOMEM;  
  17.     }  
  18.     //设置perm_node的成员属性  
  19.     node->dp.perm = perm;  
  20.     node->dp.uid = uid;  
  21.     node->dp.gid = gid;  
  22.     node->dp.prefix = prefix;  
  23.     //根据attr 来选择添加到sys_perms或dev_perms链表中  
  24.     if (attr)  
  25.         list_add_tail(&sys_perms, &node->plist);  
  26.     else  
  27.         list_add_tail(&dev_perms, &node->plist);  
  28.     return 0;  
  29. }  

至此ueventd.rc文件的解析工作完成了,uevent进程接下来将调用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. #ifdef HAVE_SELINUX  
  7.     struct selinux_opt seopts[] = {  
  8.         { SELABEL_OPT_PATH, "/file_contexts" }  
  9.     };  
  10.     if (is_selinux_enabled() > 0)  
  11.         sehandle = selabel_open(SELABEL_CTX_FILE, seopts, 1);  
  12. #endif  
  13.     /* is 64K enough? udev uses 16MB! */  
  14.     //创建NETLINK socket,用于监听内核发送过来的uevent消息  
  15.     device_fd = uevent_open_socket(64*1024, true);  
  16.     if(device_fd < 0)  
  17.         return;  
  18.     //设置socket相关属性  
  19.     fcntl(device_fd, F_SETFD, FD_CLOEXEC);  
  20.     fcntl(device_fd, F_SETFL, O_NONBLOCK);  
  21.     //查看"/dev/.coldboot_done" 文件信息  
  22.     if (stat(coldboot_done, &info) < 0) {  
  23.         t0 = get_usecs();  
  24.         coldboot("/sys/class");  
  25.         coldboot("/sys/block");  
  26.         coldboot("/sys/devices");  
  27.         t1 = get_usecs();  
  28.         fd = open(coldboot_done, O_WRONLY|O_CREAT, 0000);  
  29.         close(fd);  
  30.         log_event_print("coldboot %ld uS\n", ((long) (t1 - t0)));  
  31.     } else {  
  32.         log_event_print("skipping coldboot, already done\n");  
  33.     }  
  34. }  

函数首先调用uevent_open_socket 来创建PF_NETLINK socket 并绑定到指定地址上:

[cpp]  view plain copy
  1. int uevent_open_socket(int buf_sz, bool passcred)  
  2. {  
  3.     struct sockaddr_nl addr;  
  4.     int on = passcred;  
  5.     int s;  
  6.       
  7.     memset(&addr, 0, sizeof(addr));  
  8.     addr.nl_family = AF_NETLINK;  
  9.     addr.nl_pid = getpid();  
  10.     addr.nl_groups = 0xffffffff;  
  11.     //创建socket  
  12.     s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);  
  13.     if(s < 0)  
  14.         return -1;  
  15.     //设置该socket属性  
  16.     setsockopt(s, SOL_SOCKET, SO_RCVBUFFORCE, &buf_sz, sizeof(buf_sz));  
  17.     setsockopt(s, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));  
  18.     //绑定该socket  
  19.     if(bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {  
  20.         close(s);  
  21.         return -1;  
  22.     }  
  23.     return s;  
  24. }  

ueventd进程接下来将通过系统调用poll函数来监控该socket,如下所示:

[cpp]  view plain copy
  1. ufd.events = POLLIN;  
  2. ufd.fd = get_device_fd();  
  3.   
  4. while(1) {  
  5.     ufd.revents = 0;  
  6.     nr = poll(&ufd, 1, -1);  
  7.     if (nr <= 0)  
  8.         continue;  
  9.     if (ufd.revents == POLLIN)  
  10.            handle_device_fd();  
  11. }  

函数get_device_fd()返回创建的socket句柄值,并设置到ufd中,最后ueventd进程进入闭环监控模式,使用poll函数监控ufd,同时将第三个参数设置为-1,表示只有在监控的socket上有事件发生时,该函数才能返回。当热插入某一设备时,Linux内核将通过NETLINKsocket 发送uevent事件,此时poll函数得以返回,并调用handle_device_fd()函数来出来设备变化事件:

[cpp]  view plain copy
  1. void handle_device_fd()  
  2. {  
  3.     char msg[UEVENT_MSG_LEN+2];  
  4.     int n;  
  5.     //从socket中读取消息内容  
  6.     while ((n = uevent_kernel_multicast_recv(device_fd, msg, UEVENT_MSG_LEN)) > 0) {  
  7.         //如果读取的内容长度大于1024,继续读取  
  8.         if(n >= UEVENT_MSG_LEN)   /* overflow -- discard */  
  9.             continue;  
  10.   
  11.         msg[n] = '\0';  
  12.         msg[n+1] = '\0';  
  13.         //将uevent消息解析成uevent类型的事件  
  14.         struct uevent uevent;  
  15.         parse_event(msg, &uevent);  
  16.         //处理uevent事件  
  17.         handle_device_event(&uevent);  
  18.         handle_firmware_event(&uevent);  
  19.     }  
  20. }  

当有设备事件发生时,poll函数返回,并从socket中读取内核发送过来的消息内容,并将该消息解析成uevent事件,同时调用handle_device_event函数和handle_firmware_event函数来分别处理设备事件或firmware事件

[cpp]  view plain copy
  1. static void handle_device_event(struct uevent *uevent)  
  2. {  
  3.     //如果是设备添加事件  
  4.     if (!strcmp(uevent->action,"add"))  
  5.         fixup_sys_perms(uevent->path);  
  6.     //块设备事件  
  7.     if (!strncmp(uevent->subsystem, "block", 5)) {  
  8.         handle_block_device_event(uevent);  
  9.     //平台设备事件  
  10.     } else if (!strncmp(uevent->subsystem, "platform", 8)) {  
  11.         handle_platform_device_event(uevent);  
  12.     //通用设备事件  
  13.     } else {  
  14.         handle_generic_device_event(uevent);  
  15.     }  
  16. }  

 

[cpp]  view plain copy
  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.     //创建一个线程来专门执行firmware事件  
  10.     /* we fork, to avoid making large memory allocations in init proper */  
  11.     pid = fork();  
  12.     if (!pid) {  
  13.         process_firmware_event(uevent);  
  14.         exit(EXIT_SUCCESS);  
  15.     }  
  16. }  

具体的处理过程这里不在详细分析,读者有兴趣请自行分析!至此就介绍完了整个ueventd进程的工作,

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值