Chinaunix首页 | 论坛 | 博客
  • 博客访问: 714208
  • 博文数量: 192
  • 博客积分: 2653
  • 博客等级: 少校
  • 技术积分: 1623
  • 用 户 组: 普通用户
  • 注册时间: 2012-05-31 18:25
个人简介

How tough life is, how strong you should be!

文章分类

全部博文(192)

文章存档

2015年(1)

2014年(2)

2013年(37)

2012年(152)

分类: LINUX

2012-08-01 09:43:30

Lnux2.6内核udev机制(基于2.6.26

作者:guolele1990                                               2011112

         最近在研究输入子系统时关于应用层与handler层时有点迷惑,查找了很多资料,发现是设备节点问题,于是看起udev(嵌入式中的是mdev)机制,下面是这个机制的分析。

首先我们知道udev或者mdev都是应用程序,他们的作用就是起自动创建节点作用,这点是必须清楚,也就是它是工作在用户空间的,但是我们设备驱动,无论是event handler层还是driver 层都是工作在内核空间的,那么内核是怎样处理的?其实内核就是利用kobject_uevent来将这件事报告给用户空间,用户空间再调用相应程序(udevmdev)来创建设备文件(或叫设备节点)。

下面分析kobject_uevent,老风格,先上内核描述

/**

 * kobject_uevent - notify userspace by ending an uevent

 *

 * @action: action that is happening

 * @kobj: struct kobject that the action is happening to

 *

 * Returns 0 if kobject_uevent() is completed with success or the

 * corresponding error when it fails.

 */

这里就是说这函数是通过结束一个uevent来给用户空间报告,不过感觉翻译不对头,因为后面分析这不是结束这uevent,而是最终调用指定的uevent_helper或者是通过netlink发出这是一种特殊类型的socket,专门用于内核空间与用户空间的异步通信,比较有效率,就是通过网络发送通知)去。这函数就是kobject_uevent_env的包装。

kobject_uevent_env描述,更清楚

/**

 * kobject_uevent_env - send an uevent with environmental data

 *

 * @action: action that is happening

 * @kobj: struct kobject that the action is happening to

 * @envp_ext: pointer to environmental data

 *

 * Returns 0 if kobject_uevent() is completed with success or the

 * corresponding error when it fails.

 */

它的作用就是发送一个有参数的uevent到用户空间。

  1. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,  
  2.   
  3.                           char *envp_ext[])  
  4.   
  5. {  
  6.   
  7.          ……  
  8.   
  9.    
  10.   
  11.          /* search the kset we belong to */  
  12.   
  13.          top_kobj = kobj;  
  14.   
  15.          while (!top_kobj->kset && top_kobj->parent)  
  16.   
  17.                    top_kobj = top_kobj->parent;  
  18.   
  19.    
  20.   
  21.          if (!top_kobj->kset) {  
  22.   
  23.                    pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "  
  24.   
  25.                              "without kset!/n", kobject_name(kobj), kobj,  
  26.   
  27.                              __func__);  
  28.   
  29.                    return -EINVAL;  
  30.   
  31.          }  
  32.   
  33. /*这里要说明一下,这个uevent_ops其实就是对应内核里的(系统一共有 block_uevent_ops  memory_uevent_ops   device_uevent_ops   class_uevent_ops   module_uevent_ops )这几大类 kset_uevent_ops*/  
  34.   
  35. //这里是为device_uevent_ops,因为现在调用的是创建设备的文件,更深一层的就是一开始device_init时定义的kset,现在我们用的都是这kset所以也就指定了这一个。  
  36.   
  37.          kset = top_kobj->kset;  
  38.   
  39.          uevent_ops = kset->uevent_ops;  
  40.   
  41.    
  42.   
  43.          /* skip the event, if the filter returns zero. */  
  44.   
  45.          if (uevent_ops && uevent_ops->filter)  
  46.   
  47.                    if (!uevent_ops->filter(kset, kobj)) {  
  48.   
  49.                             pr_debug("kobject: '%s' (%p): %s: filter function "  
  50.   
  51.                                       "caused the event to drop!/n",  
  52.   
  53.                                       kobject_name(kobj), kobj, __func__);  
  54.   
  55.                             return 0;  
  56.   
  57.                    }  
  58.   
  59. //如果定义了uevent_ops里的name就调用,因为这里有,调用的翻回名字  
  60.   
  61.          /* originating subsystem */  
  62.   
  63.          if (uevent_ops && uevent_ops->name)  
  64.   
  65.                    subsystem = uevent_ops->name(kset, kobj);  
  66.   
  67.          else  
  68.   
  69.                    subsystem = kobject_name(&kset->kobj);  
  70.   
  71. …….  
  72.   
  73.          /* environment buffer */  
  74.   
  75.          ……  
  76.   
  77. //获得路径  
  78.   
  79.          /* complete object path */  
  80.   
  81.          devpath = kobject_get_path(kobj, GFP_KERNEL);  
  82.   
  83.          if (!devpath) {  
  84.   
  85.                    retval = -ENOENT;  
  86.   
  87.                    goto exit;  
  88.   
  89.          }  
  90.   
  91. //填充参数  
  92.   
  93.          /* default keys */  
  94.   
  95.          retval = add_uevent_var(env, "ACTION=%s", action_string);  
  96.   
  97.          if (retval)  
  98.   
  99.                    goto exit;  
  100.   
  101.          retval = add_uevent_var(env, "DEVPATH=%s", devpath);  
  102.   
  103.          if (retval)  
  104.   
  105.                    goto exit;  
  106.   
  107.          retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);  
  108.   
  109.          if (retval)  
  110.   
  111.                    goto exit;  
  112.   
  113.    
  114.   
  115.          /* keys passed in from the caller */  
  116.   
  117.          if (envp_ext) {  
  118.   
  119.                    for (i = 0; envp_ext[i]; i++) {  
  120.   
  121.                             retval = add_uevent_var(env, envp_ext[i]);  
  122.   
  123.                             if (retval)  
  124.   
  125.                                      goto exit;  
  126.   
  127.                    }  
  128.   
  129.          }  
  130.   
  131.    
  132.   
  133.          /* let the kset specific function add its stuff */  
  134.   
  135.          if (uevent_ops && uevent_ops->uevent) {  
  136.   
  137.                    retval = uevent_ops->uevent(kset, kobj, env);  
  138.   
  139.                    ……  
  140.   
  141.          }  
  142.   
  143.    
  144.   
  145.          /* 
  146.  
  147.           * Mark "add" and "remove" events in the object to ensure proper 
  148.  
  149.           * events to userspace during automatic cleanup. If the object did 
  150.  
  151.           * send an "add" event, "remove" will automatically generated by 
  152.  
  153.           * the core, if not already done by the caller. 
  154.  
  155.           */  
  156.   
  157.          if (action == KOBJ_ADD)  
  158.   
  159.                    kobj->state_add_uevent_sent = 1;  
  160.   
  161.          else if (action == KOBJ_REMOVE)  
  162.   
  163.                    kobj->state_remove_uevent_sent = 1;  
  164.   
  165. //这个uevent_seqnum是会在/sys/kernel/ /uevent_sequence里显示,是表示产生·过几个uevent  
  166.   
  167.          /* we will send an event, so request a new sequence number */  
  168.   
  169.          spin_lock(&sequence_lock);  
  170.   
  171.          seq = ++uevent_seqnum;  
  172.   
  173.          spin_unlock(&sequence_lock);                                                                                                                        
  174.   
  175.          retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);  
  176.   
  177.          if (retval)  
  178.   
  179.                    goto exit;  
  180.   
  181.    
  182.   
  183. //这个在新版里如果是采用网络通报的话,应用层里就采用socket来接受这event,这样比较有效率,不用整天去那读  
  184.   
  185. #if defined(CONFIG_NET)  
  186.   
  187.          /* send netlink message */  
  188.   
  189.          if (uevent_sock) {  
  190.   
  191.                    struct sk_buff *skb;  
  192.   
  193.                    size_t len;  
  194.   
  195.    
  196.   
  197.                    /* allocate message with the maximum possible size */  
  198.   
  199.                    len = strlen(action_string) + strlen(devpath) + 2;  
  200.   
  201.                    skb = alloc_skb(len + env->buflen, GFP_KERNEL);  
  202.   
  203.                    if (skb) {  
  204.   
  205.                             char *scratch;  
  206.   
  207.    
  208.   
  209.                             /* add header */  
  210.   
  211.                             scratch = skb_put(skb, len);  
  212.   
  213.                             sprintf(scratch, "%s@%s", action_string, devpath);  
  214.   
  215.    
  216.   
  217.                             /* copy keys to our continuous event payload buffer */  
  218.   
  219.                             for (i = 0; i < env->envp_idx; i++) {  
  220.   
  221.                                      len = strlen(env->envp[i]) + 1;  
  222.   
  223.                                      scratch = skb_put(skb, len);  
  224.   
  225.                                      strcpy(scratch, env->envp[i]);  
  226.   
  227.                             }  
  228.   
  229.    
  230.   
  231.                             NETLINK_CB(skb).dst_group = 1;  
  232.   
  233.                             netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);  
  234.   
  235.                    }  
  236.   
  237.          }  
  238.   
  239. #endif  
  240.   
  241. //这里是先判断uevent_helper是否为空,它是什么?他默认是/proc/sys/kernel/hotplug,但是我们可以通过echo /sbin/mdev > /proc/sys/kernel/hotplug来修改,这也是很多嵌入式初始化文件里要做的事  
  242.   
  243.          /* call uevent_helper, usually only enabled during early boot */  
  244.   
  245.          if (uevent_helper[0]) {  
  246.   
  247.                    char *argv [3];  
  248.   
  249.    
  250.   
  251.                    argv [0] = uevent_helper;  
  252.   
  253.                    argv [1] = (char *)subsystem;  
  254.   
  255.                    argv [2] = NULL;  
  256.   
  257.                    retval = add_uevent_var(env, "HOME=/");  
  258.   
  259.                    if (retval)  
  260.   
  261.                             goto exit;  
  262.   
  263.                    retval = add_uevent_var(env,  
  264.   
  265.                                                "PATH=/sbin:/bin:/usr/sbin:/usr/bin");  
  266.   
  267.                    if (retval)  
  268.   
  269.                             goto exit;  
  270.   
  271.    
  272.   
  273.                    call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);  
  274.   
  275.          }  
  276.   
  277.    
  278.   
  279. exit:  
  280.   
  281.          kfree(devpath);  
  282.   
  283.          kfree(env);  
  284.   
  285.          return retval;  
  286.   
  287. }  

call_usermodehelper,里setup就是将一些信息放在struct subprocess_info结构里,方便调用call_usermodehelper_exec,关于这函数,上一描述

/**

 * call_usermodehelper_exec - start a usermode application

 * @sub_info: information about the subprocessa

 * @wait: wait for the application to finish and return status.

 *        when -1 don't wait at all, but you get no useful error back when

 *        the program couldn't be exec'ed. This makes it safe to call

 *        from interrupt context.

 *

 * Runs a user-space application.  The application is started

 * asynchronously if wait is not set, and runs as a child of keventd.

 * (ie. it runs with full root capabilities).

 */

这函数的就是调用用户模式的应用程序,也就是调用mdev或者udev.其中udevmdev是内核通过vfork创建一个进程执行的。具体是汇编,比较难懂。

  1. /* 
  2.  
  3.           * We were successful.  We won't be returning to our caller, but 
  4.  
  5.           * instead to user space by manipulating the kernel stack. 
  6.  
  7.           */  
  8.   
  9. asm(         "add          r0, %0, %1/n/t"  
  10.   
  11.                    "mov         r1, %2/n/t"  
  12.   
  13.                    "mov         r2, %3/n/t"  
  14.   
  15.                    "bl    memmove/n/t"        /* copy regs to top of stack */  
  16.   
  17.                    "mov         r8, #0/n/t"        /* not a syscall */  
  18.   
  19.                    "mov         r9, %0/n/t"       /* thread structure */  
  20.   
  21.                    "mov         sp, r0/n/t"        /* reposition stack pointer */  
  22.   
  23.                    "b     ret_to_user"  
  24.   
  25.                    :  
  26.   
  27.                    : "r" (current_thread_info()),  
  28.   
  29.                      "Ir" (THREAD_START_SP - sizeof(regs)),  
  30.   
  31.                      "r" (®s),  
  32.   
  33.                      "Ir" (sizeof(regs))  
  34.   
  35.                    : "r0""r1""r2""r3""ip""lr""memory");  
  

还有几个具体也不分析了,大概了解一下它的主要流程就好了。

阅读(1174) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~