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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章