uevent Principle Analysis

Summary:

    What is the main mechanism of this paper is to introduce uevent and process using uevent mechanism generating device node through code analysis. The paper will be divided into two parts, the first part we introduce some prior knowledge of the principles and uevent, while the second part - through the introduction of code to create device nodes uevent mechanism.

 Linux kernel: linux-2.6.22.6

 The plate was developed: JZ2440 V3 (S3C2440A)

statement:

    This article is watching Wei Dongshan teacher combined with some video and written blog content, so the content of the text may be other article, if you think my article constitutes a violation of you, you can tell me, I will be articles correct, and if there may not be the right place in the text, please correct me. Thank you.

Part 1: Principles of prior knowledge and uevent

    Here we begin to explain the preliminaries. Before explaining, let's look at a chart presentation framework uevent mechanisms:

uevent

 

  Many of us may not know: What uevent mechanism, uevent mechanism in the end what has been done? He is worthy of those aspects of our research?

    We did not use uevent mechanism when the driver is just learning, that we have not used in the program class_create and class_device_create function to automatically create device nodes in the user space for device drivers. Then we have to manually create device node using the mknod command in user space. And when we use class_create and class_device_create function, they will we create a device node in the user space without having to manually go we get the job done. And this is what we know for macro uevent mechanism. And we know that our device nodes are created for a device driver, and the device driver and device driver are connected to the bus bus in the form of the list, while the device - driver - the bus to the next level is the sysfs layer . This leads therefore we will introduce a combination of: sysfs + mdev . This combination will give us the mechanism to explain the principles uevent. We start to understand sysfs.

    sysfs is a memory-based virtual file system, there is provided kernel, mount under / sys directory (with a view to get mount  sysfs ON / sys of the type sysfs (rw, the nosuid, nodev, noexec, relatime)) , responsible for the device tree form and a driving device providing visual information to namespace User . Meanwhile sysfs a different perspective to show the current device access system for us:

  • / sys / block  historical issues, storage block device , provided with the device name (such as sda) to a symbolic link / sys / devices of
  • / Sys / bus by bus type classification, under a bus directory to find symbolic link connecting the bus devices, point / sys / devices. a bus drivers directory under the directory contains all of the symbolic link bus driver required for corresponding struct bus_type kernel of
  • / Sys / class classified by function device, such as input device under / sys / class / input, graphics device under / sys / class / graphics, the corresponding device is pointing to the next / sys / devices in a kernel corresponding to symlinked the struct class
  • / Sys / dev stratified press device driver (character devices / block device), provided major: minor name to / sys / devices corresponding to a symbolic link in struct device_driver kernel
  • / Sys / devices include all registration devices found on a variety of various physical bus. All physical topology of their devices on the bus to display, in addition to platform devices and system devices. hanging platform devices on a chip is generally high or low internal bus and various peripheral controllers, CPU can be directly addressed. system devices are not peripheral, he is the core of the internal structure of the chip, such as CPU, timer, etc., they generally do not have the relevant driver, but there will be some architecture-dependent code to configure the corresponding kernel struct device in their

    The above demonstrates the sys directory file bus, device, driver and corresponding class, and their relationship :

  • device used to describe various apparatus which stores all device information
  • driver for driving the device, which holds the list of all devices capable of being driven by it.
  • and a bridge connecting the CPU bus is a device which holds all the devices mounted on it and a drive chain for driving these devices in the list.
  • class is used to describe a class of device, which holds the list of all such devices the device.

    Here we introduce a bus, device, structure and driven still lower class. sysfs functional unified Linux-based device model, he has the following structure composed: kobject, kset, ktype. We can see from the above diagram is implemented on the basis of the uevent kobject the structure.

    kobject: unified device model the most basic objects.

struct kobject {
 	const char *name;  //name,该Kobject的名称,同时也是sysfs中的目录名称。
			    //由于Kobject添加到Kernel时,需要根据名字注册到sysfs中,之后就不能再直接修改该字段。
			   //如果需要修改Kobject的名字,需要调用kobject_rename接口,该接口会主动处理sysfs的相关事宜。
        struct list_head    entry; //entry,用于将Kobject加入到Kset中的list_head。 
        struct kobject      *parent; //parent,指向parent kobject,以此形成层次结构(在sysfs就表现为目录结构)。
        struct kset     *kset; //kset,该kobject属于的Kset。可以为NULL。
				//如果存在,且没有指定parent,则会把Kset作为parent
				//(别忘了Kset是一个特殊的Kobject)。
        struct kobj_type    *ktype;  //ktype,该Kobject属于的kobj_type。每个Kobject必须有一个ktype,或者Kernel会提示错误。
        struct sysfs_dirent *sd;   //sd,该Kobject在sysfs中的表示。
 
        struct kref     kref;  //kref,"struct kref”类型(在include/linux/kref.h中定义)的变量,为一个可用于原子操作的引用计数。
        unsigned int state_initialized:1; //state_initialized,指示该Kobject是否已经初始化,
					  //以在Kobject的Init,Put,Add等操作时进行异常校验。
        unsigned int state_in_sysfs:1;   //state_in_sysfs,指示该Kobject是否已在sysfs中呈现,以便在自动注销时从sysfs中移除。	
        unsigned int state_add_uevent_sent:1;  // state_add_uevent_sent/state_remove_uevent_sent,记录是否已经向用户空间发送ADD uevent,
						//如果有,且没有发送remove uevent,则在自动注销时,补发REMOVE uevent,
						//以便让用户空间正确处理。
        unsigned int state_remove_uevent_sent:1;
        unsigned int uevent_suppress:1;  //uevent_suppress,如果该字段为1,则表示忽略所有上报的uevent事件。
    };

 Note: UEVENT provides a "user space notification" feature to achieve, through this function, when the kernel has increased Kobject, delete, modify, and other actions will notify the user space.

    Ktype: Representative Kobject (strictly speaking, the data structure containing the Kobject) properties of a set of operations (due to the versatility, a plurality of operation set Kobject may share the same property, and therefore independent of the ktype).

struct kobj_type {

void (*release)(struct kobject *kobj); //release,通过该回调函数,可以将包含该种类型kobject的数据结构的内存空间释放掉。

const struct sysfs_ops *sysfs_ops; //sysfs_ops,该种类型的Kobject的sysfs文件系统接口。

struct attribute **default_attrs; //default_attrs,该种类型的Kobject的atrribute列表

//(所谓attribute,就是sysfs文件系统中的一个文件)。

//将会在Kobject添加到内核时,一并注册到sysfs中。

const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);

//child_ns_type/namespace,和文件系统(sysfs)的命名空间有关

const void *(*namespace)(struct kobject *kobj);

};

    In fact implemented here is similar to the kobject derived, containing different kobj_type of kobject can be seen as different subclasses. It is achieved by carrying out the same functions polymorphism. In this design, each embedded Kobject a data structure (e.g. kset, device, device_driver etc.), must achieve their kobj_type, and define where the callback function.

    Kset: a special Kobject (as it will appear in the directory "/ sys /" file system), which is used to set similar Kobject (Kobject These properties may be the same, can be different attributes).

struct kset {

struct list_head list; //list用于保存该kset下所有的kobject的链表。

spinlock_t list_lock; //list自旋锁

struct kobject kobj; //kobj,该kset自己的kobject(kset是一个特殊的kobject,也会在sysfs中以目录的形式体现)。

const struct kset_uevent_ops *uevent_ops; //uevent_ops,该kset的uevent操作函数集。

//当任何Kobject需要上报uevent时,都要调用它所从属的kset的uevent_ops,添加环境变量,

//或者过滤event(kset可以决定哪些event可以上报)。

//因此,如果一个kobject不属于任何kset时,是不允许发送uevent的。

};

    The kset of uevent operation callback function is:

struct kset_uevent_ops {

int (* const filter)(struct kset *kset, struct kobject *kobj);

//filter,当任何Kobject需要上报uevent时,它所属的kset可以通过该接口过滤,

//阻止不希望上报的event,从而达到从整体上管理的目的。

const char *(* const name)(struct kset *kset, struct kobject *kobj);

//name,该接口可以返回kset的名称。如果一个kset没有合法的名称,

//则其下的所有Kobject将不允许上报uvent

int (* const uevent)(struct kset *kset, struct kobject *kobj,

struct kobj_uevent_env *env);

//uevent,当任何Kobject需要上报uevent时,它所属的kset可以通过该接口统一为这些event添加环境变量。

//因为很多时候上报uevent时的环境变量都是相同的,因此可以由kset统一处理,就不需要让每个Kobject独自添加了。

};

    Note that the association Kset and ktype , kobject members will use their own kset find kset belongs before setting their ktype is kobj.ktype. When not specified kset members, will be to build relationships with ktype.

     Because kobject call is uevent kset manipulation functions to which it belongs, so kset can control its behavior. If kobject not belong to any kset, you can not send uevent.

 

Understand summary, Ktype Kobject and the whole mechanism.

    Kobject core function is: maintaining a reference count, when the count is reduced to zero, is automatically released (herein spoken by the module is responsible kobject) Kobject meomry occupied space. This determines Kobject must be dynamically allocated (the only way to release dynamic)
    And Kobject most usage scenarios, is embedded in a large data structure (e.g. Kset, device_driver, etc.), these large data structure, must also be dynamically allocated, the dynamic release. So what time is it released? Is embedded Kobject release. But Kobject release is done automatically by the Kobject module (in reference count is 0), then how together with the release of large data structures containing them then? 
    Then Ktype comes in handy. We know, Ktype the release callback function is responsible for freeing Kobject (or even a data structure that contains Kobject of) memory space, then Ktype its internal functions, by whom to achieve it? Is the module where the data structure from the top! Only because it became clear Kobject which is embedded in the data structure, and by Kobject pointer and its own type of data structure, data structure to find the pointer to the top of the need to release, and then release it. 
    In talking about this, it is much clearer. Therefore, each Kobject embedded data structures, e.g. kset, device, device_driver etc., must achieve a ktype, and define where the callback function. Similarly, sysfs-related operations, too, must be ktype transit, because sysfs see Kobject, but the real subject file operations, data is embedded Kobject upper structure! 
    By the way, Kobject object-oriented thinking is the ultimate expression of the Linux kernel, but the advantages of C language is not here, so need to use the Linux kernel compare clever (and very long-winded) means to realize.

 

mdev principle

    Above we analyzed the sysfs, Here we begin to analyze mdev, we understand his relationship with sysfs by analyzing mdev. mdev can be found in the code package busybox, located busybox / util-linux / mdev.c file, he is called by uevent_helper function. In mdev mainly do two things:

the first thing:

    Mdev -s command execution, (mdev) scanning / sys / block (block stored at the device / sys / block directory, later kernel version 2.6.25, also stored in the block device / sys / class / block scan .mdev directory / sys / block for backward compatibility) dev file attribute and / sys / class two directories, dev retrieved from the properties file to the device number (dev file attribute "major: minor \ n" stored in the form of device number) , and a directory name that contains the attribute dev device_name file as the device name (ie dev directory containing the file attribute called device_name, and that part of the directory between / sys / class and device_name called subsystem. that is, each attribute dev where the path to the file can be expressed as / sys / class / subsystem / device_name / dev), create the appropriate device files in the / dev directory . For example, cat / sys / class / tty / tty0 / dev will be 4: 0, subsystem is tty, device_name is tty0.

The second thing:

    When mdev because uevnet event (formerly known as hotplug event) is called, mdev by passing a uevent events to get to its environment variables: path to the device caused by the action of uevent event and the device is located device path. Then determine what action caused the event uevent Yes. And make the appropriate action depending on the operation. If the action is add, that is a new device is added to the system, regardless of whether the device is a virtual device or the actual physical device, mdev will get through the dev attribute file in the device path path to the device number, then device path path to the last directory (dev directory that contains the file attribute) as the device name, creates a corresponding device file / dev directory. If the action is remote, that is, the device is removed from the system, delete / device path to the last directory path name under the dev directory as the file name of the device. If the action is neither add nor remove, mdev do nothing.

    , If we want to or removed from the system in the equipment added to the system, automatically creating and deleting device files, then you must do the following three points by the mdev As is clear from the above:

1. At a certain subsystem directory / sys / class of

2. Create a device name to device_name as name,

3 and must also contain a file attribute dev device_name In this directory, the file attribute dev: output device ID "major minor \ n" form.

    From the above we can know the content, sysfs to do the preparatory work for the uevent mechanisms that create the appropriate directory, and then mdev is based on sysfs to achieve the creation of device nodes by calling sysfs directories or files created.

Second part: describes the use of codes in conjunction with mechanism for creating device nodes uevent

    Now we have to combine code analysis uevent mechanism, while we will have to analyze this mechanism from class_create and class_device_create these two functions to analyze how this process is implemented. We now first analyze class_create:

    The following is a hierarchical relationship class_create function :

class_create(THIS_MODULE,"buttonsdrv");

    class_register(cls);

        kobject_set_name(&cls->subsys.kobj, "%s", cls->name); //将类的名字led_class赋值给对应的kset

subsys_set_kset(cls, class_subsys);

subsystem_register(&cls->subsys);

    kset_register(s); //创建class设备类目录

kset_add(k);

            kobject_add(&k->kobj);

kobject_shadow_add(kobj, NULL);

            parent = kobject_get(kobj->parent); // parent即class_kset.kobj,即/sysfs/class对应的目录

    list_add_tail(&kobj->entry,&kobj->kset->list);

    create_dir(kobj, shadow_parent); //创建一个class设备类目录

sysfs_create_dir(kobj, shadow_parent); //该接口是sysfs文件系统接口,代表创建一个目录,不再展开。

    From the above we can see that the corresponding kobject in sysfs is a directory (dir), when we register a kobject, calls kobject_add (& k-> kobj); and thereafter create class device directory. While we can see class_create function is done quasi working directory is class_device_create function.

    The picture from the start of the article describes the kobject we know when status changes (eg, add, remove, etc.), the user is informed that space, user space after receiving event notifications can be handled accordingly.
    The uevent the incident reported to the user space in two ways:
  1. By kmod module, called directly executable programs or scripts user space.
  2. netlink communication mechanism, the event delivery from kernel space to user space.

    The paper mainly explain by kmod module, called directly executable programs or scripts user space . And it provides the following API (the API is implemented in the "lib / kobject_uevent.c" file) by kobject.h, uevent module:

int kobject_uevent(struct kobject *kobj, enum kobject_action action);

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,

char *envp[]);


int add_uevent_var(struct kobj_uevent_env *env, const char *format, ...);


int kobject_action_type(const char *buf, size_t count,enum kobject_action *type);

    Here we from class_device_create function begin to analyze, to see how he went kobject_uevent function. We see a hierarchical relationship class_device_create function

class_device_create(buttonsdrv_class,NULL,MKDEV(auto_major,0),NULL,"buttonsdrv");

class_device_register(class_dev);

class_device_add(class_dev);

class_dev = class_device_get(class_dev);

parent_class = class_get(class_dev->class);

parent_class_dev = class_device_get(class_dev->parent);

kobject_set_name(&class_dev->kobj, "%s", class_dev->class_id);

kobject_add(&class_dev->kobj);

class_device_create_file(class_dev, attr);

class_device_add_groups(class_dev);

make_deprecated_class_device_links(class_dev);

kobject_uevent(&class_dev->kobj, KOBJ_ADD);

    From the previous code and see class_create class_device_create do a lot of similar work - is to create a directory, and when to kobject_uevent functions after they not the same, let's analyze kobject_uevent function

/**

* 通过终端事件通知用户层

*

* @action: 发生的事件 (通常是 KOBJ_ADD 和 KOBJ_REMOVE)

* @kobj: 事件发生的kobject 结构体

*

*/

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

{

return kobject_uevent_env(kobj, action, NULL);

}

    He called kobject_uevent_env function, while the above description, we know you want to send an event, then what are the events of that?

    We see linux-3.5 / include / linux / kobject.h

enum kobject_action {

KOBJ_ADD, //ADD/REMOVE,Kobject(或上层数据结构)的添加/移除事件。

KOBJ_REMOVE,

KOBJ_CHANGE, //CHANGE,Kobject(或上层数据结构)的状态或者内容发生改变。

//CHANGE,如果设备驱动需要上报的事件不再上面事件的范围内,

//或者是自定义的事件,可以使用该event,并携带相应的参数。

KOBJ_MOVE, //MOVE,Kobject(或上层数据结构)更改名称或者更改Parent(意味着在sysfs中更改了目录结构)。

KOBJ_ONLINE, //ONLINE/OFFLINE,Kobject(或上层数据结构)的上线/下线事件,其实是是否使能。

KOBJ_OFFLINE,

KOBJ_MAX

};

    Let us then analyze kobject_uevent_env function :

/**

* 发送一个带有环境变量的事件

*

* @action: 发生的事件(通常为KOBJ_MOVE)

* @kobj: 事件发生的kobject结构体

* @envp_ext: 环境变量数据指针

*

*/

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,

char *envp_ext[])

{

action_string = action_to_string(action);


/* 查找当前kobject或其parent是否从属于某个kset;如果都不从属于某个kset,则返回错误。(说明一个kobject若没有加入kset,是不会上报uevent的) */

top_kobj = kobj;

while (!top_kobj->kset && top_kobj->parent) {

top_kobj = top_kobj->parent;

}

if (!top_kobj->kset) {

pr_debug("kobject attempted to send uevent without kset!\n");

return -EINVAL;

}


kset = top_kobj->kset;

uevent_ops = kset->uevent_ops;


/* 如果所属的kset有uevent_ops->filter,则调用该函数,若该函数返回0,则过滤此次上报。(kset 可以通过filter接口过滤不希望上报的event) */

if (uevent_ops && uevent_ops->filter)

if (!uevent_ops->filter(kset, kobj)) {

pr_debug("kobject filter function caused the event to drop!\n");

return 0;

}


/*判断所属的kset是否有合法的名称,若uevent_ops->name存在就用其返回的名称作为subsystem;若uevent_ops->name不存在就用kset本身的kobject的名称作为subsystem;若没有合法的名称,则不上报uevent */

if (uevent_ops && uevent_ops->name)

subsystem = uevent_ops->name(kset, kobj);

else

subsystem = kobject_name(&kset->kobj);

if (!subsystem) {

pr_debug("unset subsytem caused the event to drop!\n");

return 0;

}


/* 分配一个此次上报的环境变量 */

envp = kzalloc(NUM_ENVP * sizeof (char *), GFP_KERNEL);

if (!envp)

return -ENOMEM;


/*分配一个此次上报的用于保存环境变量的buffer, */

buffer = kmalloc(BUFFER_SIZE, GFP_KERNEL);

if (!buffer) {

retval = -ENOMEM;

goto exit;

}


/* 获得该kobject在sysfs中路径 */

devpath = kobject_get_path(kobj, GFP_KERNEL);

if (!devpath) {

retval = -ENOENT;

goto exit;

}


/* uevent_helper的环境变量*/

envp[i++] = "HOME=/";

envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";


/* 添加环境变量 */

scratch = buffer;

envp [i++] = scratch;

scratch += sprintf(scratch, "ACTION=%s", action_string) + 1;

envp [i++] = scratch;

scratch += sprintf (scratch, "DEVPATH=%s", devpath) + 1;

envp [i++] = scratch;

scratch += sprintf(scratch, "SUBSYSTEM=%s", subsystem) + 1;

for (j = 0; envp_ext && envp_ext[j]; j++)

envp[i++] = envp_ext[j];

/* just reserve the space, overwrite it after kset call has returned */

envp[i++] = seq_buff = scratch;

scratch += strlen("SEQNUM=18446744073709551616") + 1;


/* 如果 uevent_ops->uevent 存在,调用该接口,添加kset统一的环境变量到env指针 */

if (uevent_ops && uevent_ops->uevent) {

retval = uevent_ops->uevent(kset, kobj,

&envp[i], NUM_ENVP - i, scratch,

BUFFER_SIZE - (scratch - buffer));

if (retval) {

pr_debug ("%s - uevent() returned %d\n",

__FUNCTION__, retval);

goto exit;

}

}


/* 调用add_uevent_var接口,添加格式为"SEQNUM=%llu”的序列号 */

spin_lock(&sequence_lock);

seq = ++uevent_seqnum;

spin_unlock(&sequence_lock);

sprintf(seq_buff, "SEQNUM=%llu", (unsigned long long)seq);



/* 以uevent_helper、 subsystem 以及添加了标准环境变量(HOME=/,PATH=/sbin:/bin:/usr/sbin:/usr/bin)的env指针为参数,调用kmod模块提供的call_usermodehelper函数,上报uevent。 */

if (uevent_helper[0]) {

char *argv [3];


argv [0] = uevent_helper;

argv [1] = (char *)subsystem;

argv [2] = NULL;

call_usermodehelper (argv[0], argv, envp, 0);

}

}

    uevent module kmod reported by uevent, it will pass call_usermodehelper function, call the executable file in the user space (or script, for short uevent helper) handle the event. Which uevent helper uevent_helper path stored in the array. You can compile the kernel configuration items by CONFIG_UEVENT_HELPER_PATH, statically assign uevent helper.
    But in this way it will be for each event fork a process, as the number of devices supported by the kernel, system startup in this way would be fatal (can lead to memory overflow, etc.). Therefore, only the earlier version of the kernel will be used in this way, and now the kernel is not recommended to use this way. Therefore, when compiling the kernel, you need to configure the item blank. After the system start, most of the equipment has been ready, as needed, to reassign a uevent helper, so that the process of detection system running hot plug events.
    This may be achieved by writing to the helper path "/ sys / kernel / uevent_helper" file. In fact, the form of the kernel sysfs file system, the user to open the array uevent_helper space for access to modify the user space program specific reference "./kernel/ksysfs.c" corresponding code.

    Add echo "/ sbin / mdev" in /etc/init.d/rcS script> / proc / sys / kernel / hotplug, you will find the cat / sys / kernel / uevent_helper that is / sbin / mdev. Executable Path Description / proc / sys / kernel / hotplug is eventually written to / sys / kernel / uevent_helper in. To manually echo "/ kernel / main"> uevent_helper ( before / sbin / mdev will be overwritten), when lsmod, when rmmod, / sys / kernel / uevent_helper in / kernel / main will be executed, indicating that an event has been reported to the user space.

Here we look at the Busybox is how to create a device node.

    Turn mdev played, the foregoing description is to create a directory or file in sysfs file system and device applications to access the file needs to be created in the / dev / directory. The work done by the mdev.
    mdev principle is to explain the rules of naming device files /etc/mdev.conf definition file, and creates device files in accordance with the requirements under the rules of environment variables. mdev.conf specified by the user layer, and therefore more flexible. This article is not intended to expand the analysis of the mdev configuration script. Knowledge can see my translation: mdev.conf translation

    Mdev appropriate program in Busybox / util-linux / mdev.c

int mdev_main(int argc UNUSED_PARAM, char **argv)

    xchdir("/dev");

    if (argv[1] && strcmp(argv[1], "-s")//系统启动时mdev –s才会执行这个分支

    else

    action = getenv("ACTION");

    env_path = getenv("DEVPATH");

    G.subsystem = getenv("SUBSYSTEM");

    snprintf(temp, PATH_MAX, "/sys%s", env_path);//到/sysfs/devices/led目录

    make_device(temp, /*delete:*/ 0);

    strcpy(dev_maj_min, "/dev"); //读出dev属性文件,得到设备号

    open_read_close(path, dev_maj_min + 1, 64);    

    ….

    mknod(node_name, rule->mode | type, makedev(major, minor)) //最终mknod创建节点

    Eventually we will track mknod to create devices in / dev / directory.

references:

Linux unified device model sysfs, udev, and behind them
the Linux device model (2) _Kobject
create device files and mdev Linux
kernel sends uevent the API, users spatially resolved uevent
Linux device model (3) _Uevent
uevent mechanism of the device model

Published 407 original articles · won praise 150 · views 380 000 +

Guess you like

Origin blog.csdn.net/ds1130071727/article/details/102854649