udev&uevent之二 -- uevent

/*
 * kobject_uevent - notify userspace by sending 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.
 */
int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
        return kobject_uevent_env(kobj, action, NULL);
}
EXPORT_SYMBOL_GPL(kobject_uevent);


/*
 * 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_env() is completed with success or the
 * corresponding error when it fails.
 */
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
{
	struct kobj_uevent_env *env;
	const char *action_string = kobject_actions[action];
	const char *devpath = NULL;
	const char *subsystem;
	struct kobject *top_kobj;
	struct kset *kset;
	const struct kset_uevent_ops *uevent_ops;
	int i = 0;
	int retval = 0;
#ifdef CONFIG_NET
	struct uevent_sock *ue_sk;
#endif

	pr_debug("kobject: '%s' (%p): %s\n", kobject_name(kobj), kobj, __func__);

	/* search the kset we belong to */
	//找到其所属的 kset容器,如果没找到就从其父kobj找,一直持续下去,直到父kobj不存在
	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;
	}

	//在本例中是devices_kset容器, 后面将列出devices_kset的定义
	kset = top_kobj->kset;
	uevent_ops = kset->uevent_ops;

	//回调 uevent_ops->filter()例程,本例中是dev_uevent_filter()例程,先检查是否uevent suppress
	//如果uevent suppress,则直接返回 
	/* skip the event, if uevent_suppress is set*/
	if (kobj->uevent_suppress) {
		pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
			     "caused the event to drop!\n", kobject_name(kobj), kobj, __func__);
		return 0;
	}
	/* skip the event, if the filter returns zero. */
	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;
		}
	//回调 uevent_ops-> name (),本例中是dev_uevent_name()例程,获取bus或class的名字
	/* originating subsystem */
	if (uevent_ops && uevent_ops->name)
		subsystem = uevent_ops->name(kset, kobj);
	else
		subsystem = kobject_name(&kset->kobj);
	if (!subsystem) {
		pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "
			 "event to drop!\n", kobject_name(kobj), kobj, __func__);
		return 0;
	}

	//获得用于存放环境变量的buffer
	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
		return -ENOMEM;
	//获取该kobj在sysfs的路径,通过遍历其父kobj来获得
	/* complete object path */
	devpath = kobject_get_path(kobj, GFP_KERNEL);
	if (!devpath) {
		retval = -ENOENT;
		goto exit;
	}

	//添加 ACTION环境变量,本例是“add”命令
	/* default keys */
	retval = add_uevent_var(env, "ACTION=%s", action_string);
	if (retval)
		goto exit;
	//添加 DEVPATH环境变量,本例是/sys/devices/platform/s3c2410-rtc/rtc/rtc0
	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
	if (retval)
		goto exit;
	//添加 SUBSYSTEM 环境变量,本例中是“rtc”
	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
	if (retval)
		goto exit;

	/* keys passed in from the caller */
	if (envp_ext) {
		for (i = 0; envp_ext[i]; i++) {
			retval = add_uevent_var(env, "%s", envp_ext[i]);
			if (retval)
				goto exit;
		}
	}

	//回调 uevent_ops->uevent(),本例中是dev_uevent()例程,输出一些环境变量,
	/* let the kset specific function add its stuff */
	if (uevent_ops && uevent_ops->uevent) {
		retval = uevent_ops->uevent(kset, kobj, env);
		if (retval) {
			pr_debug("kobject: '%s' (%p): %s: uevent() returned "
				 "%d\n", kobject_name(kobj), kobj, __func__, retval);
			goto exit;
		}
	}

	/*
	 * Mark "add" and "remove" events in the object to ensure proper
	 * events to userspace during automatic cleanup. If the object did
	 * send an "add" event, "remove" will automatically generated by
	 * the core, if not already done by the caller.
	 */
	if (action == KOBJ_ADD)
		kobj->state_add_uevent_sent = 1;
	else if (action == KOBJ_REMOVE)
		kobj->state_remove_uevent_sent = 1;

	mutex_lock(&uevent_sock_mutex);
	//增加event序列号的值,并输出到环境变量的buffer。该序列号可以从/sys/kernel/uevent_seqnum属性文件读取, 
	//uevent_seqnum属性文件及/sys/kernel/目录是怎样产生的呢?
	/* we will send an event, so request a new sequence number */
	retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);
	if (retval) {
		mutex_unlock(&uevent_sock_mutex);
		goto exit;
	}

	//如果配置了网络,那么就会通过netlink socket 向用户空间发送环境标量,而用户空间则通过
	//netlink socket 接收,然后采取一些列的动作。这种机制目前用在udev中,也就是pc机系统中.
#if defined(CONFIG_NET)
	/* send netlink message */
	//如果配置了net,则会在kobject_uevent_init()例程中将全局变量uevent_sock 初始化为
	//NETLINK_KOBJECT_UEVENT 类型的socket
	list_for_each_entry(ue_sk, &uevent_sock_list, list) {
		struct sock *uevent_sock = ue_sk->sk;
		struct sk_buff *skb;
		size_t len;

		if (!netlink_has_listeners(uevent_sock, 1))
			continue;

		/* allocate message with the maximum possible size */
		len = strlen(action_string) + strlen(devpath) + 2;
		skb = alloc_skb(len + env->buflen, GFP_KERNEL);
		if (skb) {
			char *scratch;

			/* add header */
			scratch = skb_put(skb, len);
			sprintf(scratch, "%s@%s", action_string, devpath);

			/* copy keys to our continuous event payload buffer */
			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;
			//通过网络接口进行广播, 将事件通知用户空间
			retval = netlink_broadcast_filtered(uevent_sock, skb, 0, 1, GFP_KERNEL, kobj_bcast_filter, kobj); 
			/* ENOBUFS should be handled in userspace */
			if (retval == -ENOBUFS || retval == -ESRCH)
				retval = 0;
		} else
			retval = -ENOMEM;
	}
#endif
	mutex_unlock(&uevent_sock_mutex);

	//对于嵌入式系统来说,busybox采用的是mdev,在系统启动脚本rcS 中会使用
	//echo/sbin/mdev > /proc/sys/kernel/hotplug命令,而这个 hotplug文件通过一定的方法映射到了
	//uevent_helper[]数组,所以uevent_helper[] = “/sbin/mdev” 。所以对于采用busybox的嵌入式系统来说会执行里面的代码,
	//而pc机不会。也就是说内核会call用户空间的/sbin/mdev这个应用程序来做动作

	/* call uevent_helper, usually only enabled during early boot */
	if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {
		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;
		//调用用户空间应用程序, 将事件通知到用户空间, UMH_WAIT_EXEC表明等待应用程序处理完                 
		retval = call_usermodehelper(argv[0], argv, env->envp, UMH_WAIT_EXEC); //调用argv[0]指定的用户空间的应用程序
	}

exit:
	kfree(devpath);
	kfree(env);
	return retval;
}
EXPORT_SYMBOL_GPL(kobject_uevent_env);






 
//core_initcall(ksysfs_init);
static int __init ksysfs_init(void)
    //获得一个kobj,无parent,故生成/sys/kernel目录
+-- kernel_kobj = kobject_create_and_add("kernel", NULL);
    //在/sys/kernel目录下生成一些属性文件(包含在属性组里)
+-- sysfs_create_group(kernel_kobj, &kernel_attr_group); //kernel_attr_group...
    //生成/sys/kernel/notes二进制属性文件,可用来读取二进制 kernel.notes section
+-- if (notes_size > 0) {
        notes_attr.size = notes_size;
        error = sysfs_create_bin_file(kernel_kobj, & notes_attr);
        if (error)
            goto group_exit;
    }

static struct attribute_group kernel_attr_group = {
        .attrs = kernel_attrs,
};

static struct attribute * kernel_attrs[] = {
        &fscaps_attr.attr,
#if defined(CONFIG_HOTPLUG)
        &uevent_seqnum_attr.attr,   //用于获取event序列号
        &uevent_helper_attr.attr,   //uevent_helper_attr... 用于存取用户提供的程序
#endif
#ifdef CONFIG_PROFILING
        &profiling_attr.attr,
#endif
#ifdef CONFIG_KEXEC
        &kexec_loaded_attr.attr,
        &kexec_crash_loaded_attr.attr,
        &kexec_crash_size_attr.attr,
        &vmcoreinfo_attr.attr,
#endif
        NULL
};

#define KERNEL_ATTR_RW(_name) \
static struct kobj_attribute _name##_attr = \
        __ATTR(_name, 0644, _name##_show, _name##_store)


/* uevent helper program, used during early boot */
static ssize_t uevent_helper_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
        return sprintf(buf, "%s\n", uevent_helper);
}
static ssize_t uevent_helper_store(struct kobject *kobj, struct kobj_attribute *attr,
                                   const char *buf, size_t count)
{
        if (count+1 > UEVENT_HELPER_PATH_LEN)
                return -ENOENT;
        memcpy(uevent_helper, buf, count);
        uevent_helper[count] = '\0';
        if (count && uevent_helper[count-1] == '\n')
                uevent_helper[count-1] = '\0';
        return count;
}
KERNEL_ATTR_RW(uevent_helper);
//devices_kset->uevent_ops的初始化
static int __init kernel_init(void * unused)
+-- do_basic_setup();
    +-- driver_init();
        +-- devtmpfs_init();
        +-- devices_init();
            +-- devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
                +-- kset = kset_create(name, uevent_ops, parent_kobj);
                    +-- kset = kzalloc(sizeof(*kset), GFP_KERNEL);
                    +-- kobject_set_name(&kset->kobj, name);
                        /*
                         * kset->uevent_ops被初始化为uevent_ops, uevent_ops为kset_create()
                         * 传入的参数实际上就是device_uevent_ops
                         * static const struct kset_uevent_ops device_uevent_ops = {
                         *        .filter =       dev_uevent_filter,
                         *        .name =         dev_uevent_name,
                         *        .uevent =       dev_uevent,
                         * };
                         */
                    +-- kset->uevent_ops = uevent_ops;      
                    +-- kset->kobj.parent = parent_kobj;
                    +-- kset->kobj.ktype = &kset_ktype;
                    +-- kset->kobj.kset = NULL;
                +-- error = kset_register(kset);
            +-- dev_kobj = kobject_create_and_add("dev", NULL);
            +-- sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
            +-- sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);  
        +-- buses_init();
        +-- classes_init();
        +-- firmware_init();
        +-- hypervisor_init();  
        +-- platform_bus_init();
        +-- cpu_dev_init();
        +-- memory_dev_init();


//1. dev_uevent_filter
//可以让程序发送uevent事件之前做些检查和过滤,然后再决定是否发送uevent事件
static int dev_uevent_filter(struct kset *kset, struct kobject *kobj)
{
        struct kobj_type *ktype = get_ktype(kobj);

        if (ktype == &device_ktype) {
                struct device *dev = to_dev(kobj);
                //bus或class如果都没设置,那么也不会发送uevent事件
                if (dev->bus)
                        return 1;
                if (dev->class)
                        return 1;
        }
        return 0;
}

static struct kobj_type device_ktype = {
        .release        = device_release,
        .sysfs_ops      = &dev_sysfs_ops,
        .namespace      = device_namespace,
};

//2.dev_uevent_name
//用于获取 bus或class的name,bus优先
static const char *dev_uevent_name(struct kset *kset, struct kobject *kobj)
{
        struct device *dev = to_dev(kobj);

        if (dev->bus)
                return dev->bus->name;    //如果设置了bus,则返回bus的name
        if (dev->class)
                return dev->class->name;  //如果没有设置bus而设置了class ,则返回 class 的name
        return NULL;
}


//3. dev_uevent
//用于输出一定的环境变量
static int dev_uevent(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env)
{
	struct device *dev = kobj_to_dev(kobj);
	int retval = 0;

	/* add device node properties if present */
	//本例中rtc0有devt,所以会输出下面的环境变量
	if (MAJOR(dev->devt)) {
		const char *tmp;
		const char *name;
		umode_t mode = 0;

		add_uevent_var(env, "MAJOR=%u", MAJOR(dev->devt));
		add_uevent_var(env, "MINOR=%u", MINOR(dev->devt));
		name = device_get_devnode(dev, &mode, &tmp);
		if (name) {
			add_uevent_var(env, "DEVNAME=%s", name);
			kfree(tmp);
			if (mode)
				add_uevent_var(env, "DEVMODE=%#o", mode & 0777);
		}
	}

	if (dev->type && dev->type->name)
		add_uevent_var(env, "DEVTYPE=%s", dev->type->name);

	if (dev->driver)
		add_uevent_var(env, "DRIVER=%s", dev->driver->name);

	/* Add common DT information about the device */
	of_device_uevent(dev, env);

	/* have the bus specific function add its stuff */
	//若果设置了platform_bus_type,则调用platform_uevent()例程, 可参考platform.c
	if (dev->bus && dev->bus->uevent) {
		retval = dev->bus->uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: bus uevent() returned %d\n", dev_name(dev), __func__, retval);
	}

	/* have the class specific function add its stuff */
	//本例中设置了 class ,走class->dev_uevent()
	if (dev->class && dev->class->dev_uevent) {
		retval = dev->class->dev_uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: class uevent() "
				     "returned %d\n", dev_name(dev), __func__, retval);
	}

	/* have the device type specific function add its stuff */
	//如果设置了dev->type, 走dev->type->uevent()
	if (dev->type && dev->type->uevent) {
		retval = dev->type->uevent(dev, env);
		if (retval)
			pr_debug("device: '%s': %s: dev_type uevent() "
				     "returned %d\n", dev_name(dev), __func__, retval);
	}

	return retval;
}


//要实现hotplug机制,需要有用户空间的程序配合才行。对于pc机的linux系统,采用的是udevd服务程序,
//其通过监听NETLINK_KOBJECT_UEVENT获得内核发出的uevent事件和环境变量,然后再查找匹配的udev rules,
//根据找到的rules做动作,udev的具体实现原理可参照网上的一些文章
//在每个注册的device文件夹下会生成一个uevent属性文件,其作用就是实现手动触发hotplug机制。
//可以向其中写入“add”和“remove”等命令,以添加和移除设备。在系统启动后注册了很多device,
//但用户空间还没启动,所以这些事件并没有处理,udevd服务启动后,会扫描/sys目录里所有的uevent属性文件,
//向其写入"add”命令,从而触发uevent事,这样udevd服务程序就有机会处理这些事件了。在嵌入式系统中使用的是mdev,
//是udev的简化版本,在启动脚本rcS中会有这样一句命令/sbin/mdev -s,其作用就是刚刚讲到的,扫描/sys目录里所有的
//uevent属性文件,向其写入"add”命令,触发uevent事件,从而mdev有机会处理这些事件。

//每当内核注册设备或驱动时都会产生uevent事件,这样用户空间的udev或mdev就有机会捕捉到这些事件,根据匹配的规则作一定的处理,
//比如在/dev目录下生成设备节点或使用modprobe加载驱动程序,等等。从而实现自动生成设备节点、加载驱动程序等等这些热拔插机制。

//udev  -- systemd (url = git://anongit.freedesktop.org/systemd/systemd)    systemd/src/udev/*
//mdev  -- busybox (url = git://busybox.net/busybox.git)
//vold  -- android (url = http://android.googlesource.com/platform/system/vold.git)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值