linux 设备文件节点的创建(二)以一个简单字符驱动为例分析创建过程

linux设备在注册的时候,创建相应的设备节点,这个过程其实是相当复杂的。下面从一个简单的例子开始,分析这个创建过程具体是什么样子的。

1 简单字符设备驱动

#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/device.h> 	//class_create
#define I_DEVICE_NAME			"iTestDev"
 
static int major;
static struct class *iTest_class;
static struct device *iTest_device;
 
static int iRead(struct file * file, const char __user * buffer, size_t count, loff_t * ppos)
{
	return -1;
}
 
static const struct file_operations stfops = {
	.owner	= THIS_MODULE,
	.read   = iRead,
};
 
static int __init iTest_Init(void)
{
	/* 主设备号设置为0表示由系统自动分配主设备号 */
	major = register_chrdev(0, I_DEVICE_NAME, &stfops);
 
	/* 创建iTest_class类 */
	iTest_class = class_create(THIS_MODULE, "iTestClass");
 
	/* 在iTest_class类下创建设备,并在/dev/目录下创建iTestDevice节点*/
	iTest_device = device_create(iTest_class, NULL, MKDEV(major, 0), NULL, "iTestDevice");
	return 0;
}
 
static void __exit iTest_Exit(void)
{
	unregister_chrdev(major, I_DEVICE_NAME);
	device_unregister(iTest_device);
	class_destroy(iTest_class);	
}
 
module_init(iTest_Init);
module_exit(iTest_Exit);
 
MODULE_LICENSE("GPL");

上面例子,设备节点的创建函数主要是class_create函数和device_create函数。

可以把上面程序编译成.ko文件,然后安装到内核,设备节点的创建主要分成两个部分。

1 在安装的过程中,首先在/sys/目录下面创建一系列的文件和目录节点。

2 然后会调用uevent,通知用户层的mdev,让mdev根据/sys/下面的dev的主次设备号,在/dev目录下创建设备节点,这样应用程序就可以通过该节点操作设备了。

2 /sys/目录节点创建源码分析

2.1 class_create

先来看class_create函数,该函数的主要作用是在/sys/class/下创建iTestClass目录:

                                                    

class_create

   -------->__class_create

struct class *__class_create(struct module *owner, const char *name,
			     struct lock_class_key *key)
{
	struct class *cls;
	int retval;

	cls = kzalloc(sizeof(*cls), GFP_KERNEL);
	if (!cls) {
		retval = -ENOMEM;
		goto error;
	}

	cls->name = name;
	cls->owner = owner;
	cls->class_release = class_create_release;

	retval = __class_register(cls, key);
	if (retval)
		goto error;

	return cls;

error:
	kfree(cls);
	return ERR_PTR(retval);
}

上面先分配了一个class结构,然后调用__class_register注册该结构:

int __class_register(struct class *cls, struct lock_class_key *key)
{
	struct subsys_private *cp;
	int error;

	pr_debug("device class '%s': registering\n", cls->name);

	cp = kzalloc(sizeof(*cp), GFP_KERNEL);//分配一个subsys_private结构,该结构主要用力啊构建设备文件树
	if (!cp)
		return -ENOMEM;
	klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
	INIT_LIST_HEAD(&cp->interfaces);
	kset_init(&cp->glue_dirs);
	__mutex_init(&cp->mutex, "subsys mutex", key);
	error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
	if (error) {
		kfree(cp);
		return error;
	}
-----------------------------------------------------(1)
	/* set the default /sys/dev directory for devices of this class */
	if (!cls->dev_kobj)
		cls->dev_kobj = sysfs_dev_char_kobj;//这边为空,dev_kobj指向/sys/dev/char

#if defined(CONFIG_BLOCK)
	/* let the block class directory show up in the root of sysfs */
	if (!sysfs_deprecated || cls != &block_class)
		cp->subsys.kobj.kset = class_kset;
#else   -----------------------------------------------------(2)
	cp->subsys.kobj.kset = class_kset; //当前类的父kset设置为class_kset,最后创建的目录在/sys/class下面
#endif
	cp->subsys.kobj.ktype = &class_ktype;
	cp->class = cls;
	cls->p = cp;
--------------------------------------------------------------(3)
	error = kset_register(&cp->subsys);//类目录的创建函数
	if (error) {
		kfree(cp);
		return error;
	}
	error = add_class_attrs(class_get(cls));//这边类没有class_attrs结构,不创建
	class_put(cls);
	return error;
}

(1)把cls->dev_kobj设置为sysfs_dev_char_kobj,后面函数会用到该结构,在/sys/dev/char/下面创建该设备,sysfs_dev_char_kobj是char目录的kobject

(2)subsys.kobj.kset指向class_kset,其中class_kset是/sys/class目录的kset,通过这个关系,最后创建的设备类目录在class目录下面

(3)该函数在/sys/class目录下面创建iTestClass目录。

class_create

   -------->__class_create

       ----------->__class_register

              ------------->kset_register

int kset_register(struct kset *k)
{
	int err;

	if (!k)
		return -EINVAL;

	kset_init(k);
	err = kobject_add_internal(&k->kobj);//创建类目录
	if (err)
		return err;
	kobject_uevent(&k->kobj, KOBJ_ADD);//向用户层发送事件,通知在/dev下面创建设备节点
	return 0;
}

kobject_add_internal中完成类目录的创建,kobject_uevent后面再分析。

kset_register

    ----------->kobject_add_internal

static int kobject_add_internal(struct kobject *kobj)
{
	int error = 0;
	struct kobject *parent;

	if (!kobj)
		return -ENOENT;

	if (!kobj->name || !kobj->name[0]) {
		WARN(1, "kobject: (%p): attempted to be registered with empty "
			 "name!\n", kobj);
		return -EINVAL;
	}

	parent = kobject_get(kobj->parent);//没有指定parent,所以parent为空

	/* join kset if set, use it as parent if we do not already have one */
	if (kobj->kset) {
		if (!parent)-----------------------------------------(1)
			parent = kobject_get(&kobj->kset->kobj);
		kobj_kset_join(kobj);
		kobj->parent = parent;
	}

	pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
		 kobject_name(kobj), kobj, __func__,
		 parent ? kobject_name(parent) : "<NULL>",
		 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
-----------------------------------------------(2)
	error = create_dir(kobj);
	if (error) {
		kobj_kset_leave(kobj);
		kobject_put(parent);
		kobj->parent = NULL;

		/* be noisy on error issues */
		if (error == -EEXIST)
			WARN(1, "%s failed for %s with "
			     "-EEXIST, don't try to register things with "
			     "the same name in the same directory.\n",
			     __func__, kobject_name(kobj));
		else
			WARN(1, "%s failed for %s (error: %d parent: %s)\n",
			     __func__, kobject_name(kobj), error,
			     parent ? kobject_name(parent) : "'none'");
	} else
		kobj->state_in_sysfs = 1;

	return error;
}

(1)由于一开始kobj->parent为空,所以取kobj->kset->kobj作为parent,设置给kobj->parent

所以设置结果为class->subsys.kobj->parent=class_kset->kobj,sysfs文件系统中,每个目录都有一个koject结构,这样新建类的kobject就指向了class_kset的kobject,形成了目录层次关系。

(2)接着在create_dir函数中,为该新建类目录iTestClass创建sysfs_dirent,并把sysfs_dirent和kobject关联起来,把sysfs_dirent的s_parent指向class_kset的sysfs_dirent,并链接到其孩子节点中,类目录iTestClass创建完毕。具体关于sysfs的目录组织关系,可以参照该系列文章:

https://blog.csdn.net/oqqYuJi12345678/article/details/101849978

最终各数据结构之间的关系大概如下所示:

2.2 device_create 

device_create在/sys目录下创建 的目录和文件较多,最终/sys目录下面的dev文件也由其创建,应用层mdev依赖该文件在/dev目录下面创建设备节点。

看一下该函数的参数:

struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)

class为上面创建的类,parent这边设置为空,devt是设备号,该设备号会存放在dev->devt中

device_create

    ----------->device_create_vargs

          --------------->device_register

int device_register(struct device *dev)
{
	device_initialize(dev);
	return device_add(dev);
}
void device_initialize(struct device *dev)
{
	dev->kobj.kset = devices_kset;
	kobject_init(&dev->kobj, &device_ktype);
	INIT_LIST_HEAD(&dev->dma_pools);
	mutex_init(&dev->mutex);
	lockdep_set_novalidate_class(&dev->mutex);
	spin_lock_init(&dev->devres_lock);
	INIT_LIST_HEAD(&dev->devres_head);
	device_pm_init(dev);
	set_dev_node(dev, -1);
}

在device_initialize函数中,需要注意的是dev->kobj.kset设置为devices_kset,devices_kset是系统初始化时建立的/sys/devices目录的kset,核心函数是device_add:

int device_add(struct device *dev)
{
	struct device *parent = NULL;
	struct kobject *kobj;
	struct class_interface *class_intf;
	int error = -EINVAL;

	dev = get_device(dev);
	if (!dev)
		goto done;

	if (!dev->p) {
		error = device_private_init(dev);
		if (error)
			goto done;
	}

	/*
	 * for statically allocated devices, which should all be converted
	 * some day, we need to initialize the name. We prevent reading back
	 * the name, and force the use of dev_name()
	 */
	if (dev->init_name) {
		dev_set_name(dev, "%s", dev->init_name);
		dev->init_name = NULL;
	}

	/* subsystems can specify simple device enumeration */
	if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
		dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);

	if (!dev_name(dev)) {
		error = -EINVAL;
		goto name_error;
	}

	pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
-------------------------------------------------------------(1)
	parent = get_device(dev->parent);
	kobj = get_device_parent(dev, parent);
	if (kobj)
		dev->kobj.parent = kobj;

	/* use parent numa_node */
	if (parent)
		set_dev_node(dev, dev_to_node(parent));

	/* first, register with generic layer. */
	/* we require the name to be set before, and pass NULL */
--------------------------------------------------------------(2)
	error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
	if (error)
		goto Error;

	/* notify platform of device entry */
	if (platform_notify)
		platform_notify(dev);
----------------------------------------------------------------(3)
	error = device_create_file(dev, &uevent_attr);
	if (error)
		goto attrError;

	if (MAJOR(dev->devt)) {
------------------------------------------------------------------(4)
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;
--------------------------------------------------------------------(5)
		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;

		devtmpfs_create_node(dev);
	}
------------------------------------------------------------------(6)
	error = device_add_class_symlinks(dev);
	if (error)
		goto SymlinkError;
	error = device_add_attrs(dev);
	if (error)
		goto AttrsError;
	error = bus_add_device(dev);//跟bus总线设备相关,暂不分析
	if (error)
		goto BusError;
	error = dpm_sysfs_add(dev);//跟device power manager 文件的创建相关,暂不分析
	if (error)
		goto DPMError;
	device_pm_add(dev);//跟device power manager 文件的创建相关,暂不分析

。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
}

(1)dev->parent为空,则会在/sys/devices/下面建立virtual目录,该目录作为该dev的parent,看一下get_device_parent函数:

static struct kobject *get_device_parent(struct device *dev,
					 struct device *parent)
{
	if (dev->class) {
		static DEFINE_MUTEX(gdp_mutex);
		struct kobject *kobj = NULL;
		struct kobject *parent_kobj;
		struct kobject *k;

#ifdef CONFIG_BLOCK
		/* block disks show up in /sys/block */
		if (sysfs_deprecated && dev->class == &block_class) {
			if (parent && parent->class == &block_class)
				return &parent->kobj;
			return &block_class.p->subsys.kobj;
		}
#endif

		/*
		 * If we have no parent, we live in "virtual".
		 * Class-devices with a non class-device as parent, live
		 * in a "glue" directory to prevent namespace collisions.
		 */
-------------------------------------(1.1)
		if (parent == NULL)
			parent_kobj = virtual_device_parent(dev);
		else if (parent->class && !dev->class->ns_type)
			return &parent->kobj;
		else
			parent_kobj = &parent->kobj;

		mutex_lock(&gdp_mutex);

		/* find our class-directory at the parent and reference it */
		spin_lock(&dev->class->p->glue_dirs.list_lock);
		list_for_each_entry(k, &dev->class->p->glue_dirs.list, entry)
			if (k->parent == parent_kobj) {
				kobj = kobject_get(k);
				break;
			}
		spin_unlock(&dev->class->p->glue_dirs.list_lock);
		if (kobj) {
			mutex_unlock(&gdp_mutex);
			return kobj;
		}

		/* or create a new class-directory at the parent device */
-----------------------------------------(1.2)
		k = class_dir_create_and_add(dev->class, parent_kobj);
		/* do not emit an uevent for this simple "glue" directory */
		mutex_unlock(&gdp_mutex);
		return k;
	}

	/* subsystems can specify a default root directory for their devices */
	if (!parent && dev->bus && dev->bus->dev_root)
		return &dev->bus->dev_root->kobj;

	if (parent)
		return &parent->kobj;
	return NULL;
}

(1.1)由于parent为空,所以调用virtual_device_parent创建一个parent:

struct kobject *virtual_device_parent(struct device *dev)
{
	static struct kobject *virtual_dir = NULL;

	if (!virtual_dir)
		virtual_dir = kobject_create_and_add("virtual",
						     &devices_kset->kobj);

	return virtual_dir;
}

可以看到virtual_device_parent函数在/sys/devices/目录下面创建了一个virtual,该目录存放在静态变量中,只有第一次进该函数的时候才会创建。并返回该目录的kobject结构。

(1.2)然后利用上面的virtual目录,在virtual目录下面再创建一个iTestClass的子目录。

static struct kobject *
class_dir_create_and_add(struct class *class, struct kobject *parent_kobj)
{
	struct class_dir *dir;
	int retval;

	dir = kzalloc(sizeof(*dir), GFP_KERNEL);
	if (!dir)
		return NULL;

	dir->class = class;
	kobject_init(&dir->kobj, &class_dir_ktype);

	dir->kobj.kset = &class->p->glue_dirs;

	retval = kobject_add(&dir->kobj, parent_kobj, "%s", class->name);//在/sys/devices/virtual目录下面创建iTestClass子目录
	if (retval < 0) {
		kobject_put(&dir->kobj);
		return NULL;
	}
	return &dir->kobj; //返回该子目录的kobject
}

可以看到确实有该目录存在:

                                                

最终get_device_parent函数返回/sys/devices/virtual/iTestClass目录的kobject,并把其赋值给dev->kobj.parent

(2)利用1中创建的/sys/devices/virtual/iTestClass目录的kobject作为parent,在该目录下面创建iTestDevice子目录

(3)在上面创建的/sys/devices/virtual/iTestClass/iTestDevice目录下面创建属性文件uevent

device_create_file就是对sysfs_add_file_mode的封装,sysfs_add_file_mode函数的详解参考这篇文章:

https://blog.csdn.net/oqqYuJi12345678/article/details/101849978

所创节点如下:

(4)在/sys/devices/virtual/iTestClass/iTestDevice目录下面创建属性文件dev,该dev文件很重要,mdev在/dev目录下面创建设备节点就是依据该属性文件中读取到的设备号。可以看下该属性文件的方法:

static struct device_attribute devt_attr =
	__ATTR(dev, S_IRUGO, show_dev, NULL);
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
			char *buf)
{
	return print_dev_t(buf, dev->devt);
}

show_dev就是打印出该节点的设备号。

                                    

可以看到我们这边建立的主设备号为250,次设备号为0.

(5)device_create_sys_dev_entry函数新建/sys/dev/250:00链接,链接到/sys/devices/virtual/iTestClass/iTestDevice目录。

static struct kobject *device_to_dev_kobj(struct device *dev)
{
	struct kobject *kobj;

	if (dev->class)
		kobj = dev->class->dev_kobj;
	else
		kobj = sysfs_dev_char_kobj;

	return kobj;
}
static int device_create_sys_dev_entry(struct device *dev)
{
	struct kobject *kobj = device_to_dev_kobj(dev);//返回class->dev_kobj,而该结构是在class_create函数中设置的/sys/dev/char/目录的kobject
	int error = 0;
	char devt_str[15];

	if (kobj) {
		format_dev_t(devt_str, dev->devt);//设置新建链接的名字,这边是250:0
		error = sysfs_create_link(kobj, &dev->kobj, devt_str);//创建链接函数
	}

	return error;
}

下面看一下链接的具体创建过程:

int sysfs_create_link(struct kobject *kobj, struct kobject *target,
		      const char *name)
{
	return sysfs_do_create_link(kobj, target, name, 1);
}
static int sysfs_do_create_link(struct kobject *kobj, struct kobject *target,
				const char *name, int warn)
{
	struct sysfs_dirent *parent_sd = NULL;

	if (!kobj)
		parent_sd = &sysfs_root;
	else
		parent_sd = kobj->sd;//获取父目录的kobj的 sysfs_dirent作为父sysfs_dirent

	if (!parent_sd)
		return -EFAULT;

	return sysfs_do_create_link_sd(parent_sd, target, name, warn);
}

可以看到,上面把dev->kobj作为目标,也就是链接指向的目标目录为/sys/devices/virtual/iTestClass/iTestDevice,

static int sysfs_do_create_link_sd(struct sysfs_dirent *parent_sd,
				   struct kobject *target,
				   const char *name, int warn)
{
	struct sysfs_dirent *target_sd = NULL;
	struct sysfs_dirent *sd = NULL;
	struct sysfs_addrm_cxt acxt;
	enum kobj_ns_type ns_type;
	int error;

	BUG_ON(!name || !parent_sd);

	/* target->sd can go away beneath us but is protected with
	 * sysfs_assoc_lock.  Fetch target_sd from it.
	 */
	spin_lock(&sysfs_assoc_lock);
	if (target->sd)
		target_sd = sysfs_get(target->sd);//获取目标目录的sysfs_dirent,即iTestDevice目录
	spin_unlock(&sysfs_assoc_lock);

	error = -ENOENT;
	if (!target_sd)
		goto out_put;

	error = -ENOMEM;
//新建名字为250:0的sysfs_dirent
	sd = sysfs_new_dirent(name, S_IFLNK|S_IRWXUGO, SYSFS_KOBJ_LINK);
	if (!sd)
		goto out_put;

	ns_type = sysfs_ns_type(parent_sd);
	if (ns_type)
		sd->s_ns = target->ktype->namespace(target);
	sd->s_symlink.target_sd = target_sd; //设置该链接的目标目录,即iTestDevice
	target_sd = NULL;	/* reference is now owned by the symlink */

	sysfs_addrm_start(&acxt, parent_sd);
	/* Symlinks must be between directories with the same ns_type */
	if (!ns_type ||
	    (ns_type == sysfs_ns_type(sd->s_symlink.target_sd->s_parent))) {
		if (warn)
			error = sysfs_add_one(&acxt, sd);//把该链接和父目录/sys/dev/char/关联起来
		else
			error = __sysfs_add_one(&acxt, sd);
	} else {
		error = -EINVAL;
		WARN(1, KERN_WARNING
			"sysfs: symlink across ns_types %s/%s -> %s/%s\n",
			parent_sd->s_name,
			sd->s_name,
			sd->s_symlink.target_sd->s_parent->s_name,
			sd->s_symlink.target_sd->s_name);
	}
	sysfs_addrm_finish(&acxt);

	if (error)
		goto out_put;

	return 0;

 out_put:
	sysfs_put(target_sd);
	sysfs_put(sd);
	return error;
}

可以看到确实有250:0链接:

(6)

        (6.1)首先在/sys/devices/virtual/iTestClass/iTestDevice下面创建subsystem链接,指向/sys/class/iTestClass/,

        (6.2)然后在/sys/class/iTestClass目录下创建iTestDevice链接,指向/sys/devices/virtual/iTestClass/iTestDevice

static int device_add_class_symlinks(struct device *dev)
{
	int error;

	if (!dev->class)
		return 0;
-----------------------------------------(6.1)
	error = sysfs_create_link(&dev->kobj,
				  &dev->class->p->subsys.kobj,
				  "subsystem");
	if (error)
		goto out;

	if (dev->parent && device_is_not_partition(dev)) {
		error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
					  "device");
		if (error)
			goto out_subsys;
	}

#ifdef CONFIG_BLOCK
	/* /sys/block has directories and does not need symlinks */
	if (sysfs_deprecated && dev->class == &block_class)
		return 0;
#endif

	/* link in the class directory pointing to the device */
-------------------------------------------------(6.2)
	error = sysfs_create_link(&dev->class->p->subsys.kobj,
				  &dev->kobj, dev_name(dev));
	if (error)
		goto out_device;

	return 0;

out_device:
	sysfs_remove_link(&dev->kobj, "device");

out_subsys:
	sysfs_remove_link(&dev->kobj, "subsystem");
out:
	return error;
}

 最终的目录结构如下图:

                                     

3 内核通知用户层在/dev目录下面创建节点

上面在/sys/class/下创建iTestClass目录时候调用的kset_register函数中,以及后面的device_add函数中都调用到了kobject_uevent函数,该函数通知应用层在/dev目录下面创建相应的设备节点。当然创建设备节点是基于/sys/目录下面的dev节点的主次设备号来实现的。所以这边只在device_add函数的kobject_uevent事件中才会真正在/dev目录下面创建节点,因为device_add在/sys/目录下面为该设备创建了dev节点。下面来看一下该函数的具体实现:

int kobject_uevent(struct kobject *kobj, enum kobject_action action)
{
	return kobject_uevent_env(kobj, action, NULL);
}
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_err("kobject: '%s' (%p): %s\n",
		// kobject_name(kobj), kobj, __func__);

	/* search the kset we belong to */
-------------------------------------------(1)
	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;

	/* 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;
		}

	/* originating subsystem */
-----------------------------------------------------(2)
	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;
	}

	/* environment buffer */
	env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
	if (!env)
		return -ENOMEM;

	/* complete object path */
----------------------------------------------------------(3)
	devpath = kobject_get_path(kobj, GFP_KERNEL);
	if (!devpath) {
		retval = -ENOENT;
		goto exit;
	}

	/* default keys */
	retval = add_uevent_var(env, "ACTION=%s", action_string);
	pr_err(" action_string=%s \n",action_string);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "DEVPATH=%s", devpath);
	pr_err(" devpath=%s \n",devpath);
	if (retval)
		goto exit;
	retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
	pr_err(" subsystem=%s \n",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;
		}
	}

	/* 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);
	/* 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;
	}
---------------------------------------------------------(4)
#if defined(CONFIG_NET)
	/* send netlink message */
	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;
		//	pr_err("netlink_broadcast_filtered\n");
			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);

	/* 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;
---------------------------------------------------------------(5)
		retval = call_usermodehelper(argv[0], argv,
					     env->envp, UMH_WAIT_EXEC);
	}

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

(1)获取该kobject所属的上层kset集合,即如果我们需要上报新添加的kobject结构的路径为/class/iTestClass,则由前面可知,其上层kset集合所属的目录为class

(2)由1获取得到的kset集合,即为该kobject的subsystem

(3)获取kobject的路径,比如kobject指代目录iTestClass,则该路径为/class/iTestClass

(4)使用netlink机制向用户层上报事件

(5)直接在内核态调用用户程序mdev,来完成事件处理。uevent_helper中存放的即是该事件的应用层处理函数路径。

内核调用应用层程序,可以参考这篇文章:

https://blog.csdn.net/oqqYuJi12345678/article/details/103019627

关于kset和kobject的关系,可以参考这篇文章:

https://blog.csdn.net/oqqYuJi12345678/article/details/103094014

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值