Lnux2.6内核udev机制(基于2.6.26)
作者:guolele1990 2011年1月12日
最近在研究输入子系统时关于应用层与handler层时有点迷惑,查找了很多资料,发现是设备节点问题,于是看起udev(嵌入式中的是mdev)机制,下面是这个机制的分析。
首先我们知道udev或者mdev都是应用程序,他们的作用就是起自动创建节点作用,这点是必须清楚,也就是它是工作在用户空间的,但是我们设备驱动,无论是event handler层还是driver 层都是工作在内核空间的,那么内核是怎样处理的?其实内核就是利用kobject_uevent来将这件事报告给用户空间,用户空间再调用相应程序(udev、mdev)来创建设备文件(或叫设备节点)。
下面分析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到用户空间。
- int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
-
- char *envp_ext[])
-
- {
-
- ……
-
-
-
-
-
- top_kobj = kobj;
-
- while (!top_kobj->kset && top_kobj->parent)
-
- top_kobj = top_kobj->parent;
-
-
-
- if (!top_kobj->kset) {
-
- pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
-
- "without kset!/n", kobject_name(kobj), kobj,
-
- __func__);
-
- return -EINVAL;
-
- }
-
-
-
-
-
- kset = top_kobj->kset;
-
- uevent_ops = kset->uevent_ops;
-
-
-
-
-
- if (uevent_ops && uevent_ops->filter)
-
- if (!uevent_ops->filter(kset, kobj)) {
-
- pr_debug("kobject: '%s' (%p): %s: filter function "
-
- "caused the event to drop!/n",
-
- kobject_name(kobj), kobj, __func__);
-
- return 0;
-
- }
-
-
-
-
-
- if (uevent_ops && uevent_ops->name)
-
- subsystem = uevent_ops->name(kset, kobj);
-
- else
-
- subsystem = kobject_name(&kset->kobj);
-
- …….
-
-
-
- ……
-
-
-
-
-
- devpath = kobject_get_path(kobj, GFP_KERNEL);
-
- if (!devpath) {
-
- retval = -ENOENT;
-
- goto exit;
-
- }
-
-
-
-
-
- retval = add_uevent_var(env, "ACTION=%s", action_string);
-
- if (retval)
-
- goto exit;
-
- retval = add_uevent_var(env, "DEVPATH=%s", devpath);
-
- if (retval)
-
- goto exit;
-
- retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
-
- if (retval)
-
- goto exit;
-
-
-
-
-
- if (envp_ext) {
-
- for (i = 0; envp_ext[i]; i++) {
-
- retval = add_uevent_var(env, envp_ext[i]);
-
- if (retval)
-
- goto exit;
-
- }
-
- }
-
-
-
-
-
- if (uevent_ops && uevent_ops->uevent) {
-
- retval = uevent_ops->uevent(kset, kobj, env);
-
- ……
-
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- if (action == KOBJ_ADD)
-
- kobj->state_add_uevent_sent = 1;
-
- else if (action == KOBJ_REMOVE)
-
- kobj->state_remove_uevent_sent = 1;
-
-
-
-
-
- spin_lock(&sequence_lock);
-
- seq = ++uevent_seqnum;
-
- spin_unlock(&sequence_lock);
-
- retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);
-
- if (retval)
-
- goto exit;
-
-
-
-
-
- #if defined(CONFIG_NET)
-
-
-
- if (uevent_sock) {
-
- struct sk_buff *skb;
-
- size_t len;
-
-
-
-
-
- len = strlen(action_string) + strlen(devpath) + 2;
-
- skb = alloc_skb(len + env->buflen, GFP_KERNEL);
-
- if (skb) {
-
- char *scratch;
-
-
-
-
-
- scratch = skb_put(skb, len);
-
- sprintf(scratch, "%s@%s", action_string, devpath);
-
-
-
-
-
- for (i = 0; i < env->envp_idx; i++) {
-
- len = strlen(env->envp[i]) + 1;
-
- scratch = skb_put(skb, len);
-
- strcpy(scratch, env->envp[i]);
-
- }
-
-
-
- NETLINK_CB(skb).dst_group = 1;
-
- netlink_broadcast(uevent_sock, skb, 0, 1, GFP_KERNEL);
-
- }
-
- }
-
- #endif
-
-
-
-
-
- if (uevent_helper[0]) {
-
- char *argv [3];
-
-
-
- argv [0] = uevent_helper;
-
- argv [1] = (char *)subsystem;
-
- argv [2] = NULL;
-
- retval = add_uevent_var(env, "HOME=/");
-
- if (retval)
-
- goto exit;
-
- retval = add_uevent_var(env,
-
- "PATH=/sbin:/bin:/usr/sbin:/usr/bin");
-
- if (retval)
-
- goto exit;
-
-
-
- call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC);
-
- }
-
-
-
- exit:
-
- kfree(devpath);
-
- kfree(env);
-
- return retval;
-
- }
而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.其中udev、mdev是内核通过vfork创建一个进程执行的。具体是汇编,比较难懂。
-
-
-
-
-
-
-
-
- asm( "add r0, %0, %1/n/t"
-
- "mov r1, %2/n/t"
-
- "mov r2, %3/n/t"
-
- "bl memmove/n/t"
-
- "mov r8, #0/n/t"
-
- "mov r9, %0/n/t"
-
- "mov sp, r0/n/t"
-
- "b ret_to_user"
-
- :
-
- : "r" (current_thread_info()),
-
- "Ir" (THREAD_START_SP - sizeof(regs)),
-
- "r" (®s),
-
- "Ir" (sizeof(regs))
-
- : "r0", "r1", "r2", "r3", "ip", "lr", "memory");
还有几个具体也不分析了,大概了解一下它的主要流程就好了。
阅读(1220) | 评论(0) | 转发(0) |