I recently learned the operating mechanism of the device model and trained on books and online materials. my appearance & amp; 20284; has changed my network permission.
I recently learned the operating mechanism of the device model and trained books and online materials. it seems that I have modified my network weight. so I wrote down and sorted out my ideas.
Since the hardware information and logical operations of the previous driver are written together, one driver can only adapt to one platform. To improve the portability of the driver, the device model is introduced.
Now the question is: how does the device model work?
I. theory
This involves two important structures: kobject and kset.
1.1 kobject
Kobject is the basic structure of a device object. Many kobject objects are connected together to form a hierarchical topology, which is equivalent to the steel frame of the building and is responsible for the connection between objects.
Struct kobject; struct kobject {const char * name; // struct list_head entry; // The entry to the kernel linked list, through container_of () struct kobject * parent; // parent object, used to construct the hierarchical relationship struct kset * kset of the kobject object; // The kset set to which the kobject object belongs. objects of the same type will be added to the same set, struct kobj_type * ktype; // properties file and its operation function handle struct sysfs_dirent * sd ;//?? Directory structure struct kref; // object reference count, used to calculate the life cycle unsigned int state_initialized: 1; // whether to initialize unsigned int state_in_sysfs: 1; // whether it appears in the file tree: unsigned int state_add_uevent_sent: 1; // unsigned int state_remove_uevent_sent: 1; unsigned int uevent_suppress: 1; // whether to send notification events };
The corresponding operation functions are as follows:
Kobject_set_name (struct kobject * kobj, const char * fmt ,...); kobject_init (struct kobject * kobj, struct kobj_type * ktype); kobject_add (struct kobject * kobj, struct kobject * parent, const char * fmt ,...); // 1 ). ensure the hierarchy of kobject. 2) create the corresponding directory kobject_del (struct kobject * kobj) in sysfs );
The kobject_add () function adds a new kobject object to the corresponding level of the building and creates a directory in sysfs. Attribute files provide users with a way to interact with the driver model. In the previous driver model, the interaction between the application layer and the driver layer relies on device files. The typical interaction process is:
Open the device file --> read and write to the device file --> Close the device file
When reading and writing a device, the data is transmitted to the driver processing function at the kernel layer. With the property file, you can have another interaction method:
Cat/sys/(corresponding directory of the device model)/(corresponding attribute file)
Echo 'XX'>/sys/(corresponding directory of the device model)/(corresponding attribute file)
The read and write operations on the attribute file will eventually call the operation function (show or store) under the "corresponding" ktype member of the kobject object ). In this way, you can communicate with the kobject object in the kernel through operations on the attribute file. (This is shown in the following example)
1.2 kset
Kset is a container with all kobject objects of the same type, which is equivalent to a floor of the building.
Struct kset {struct list_head list; // The same type of kobject linked list spinlock_t list_lock; // struct kobject kobj; // The kobject object struct kset_uevent_ops * uevent_ops; // action function for notifying the Event}; struct kset_uevent_ops {// call function 1 and function 3 int (* filter) (struct kset * kset in kobject_uevent, struct kobject * kobj); // Function 1 const char * (* name) (struct kset * kset, struct kobject * kobj); int (* uevent) (struct kset * kset, struct kobject * kobj, // function 3 struct kobj_uevent_env * env );};
In some cases, we need to notify the application layer of the changes to a kobject object (such as hot swapping events, and then find and load the corresponding driver at the application layer), then we need to call the kobject_uevent () function.
This function will find the kset set to which the kobject object belongs. Then, call the filter function () and uevent function () under the uevent_ops member respectively (). Call_usermodehelper () will be called (). In call_usermodehelper, a user space program will be taken into the kernel space for execution based on the specified path, so as to complete the event notification. The function trunk is as follows (note the English note)
Kobject_uevent (struct kobject * kobj, enum kobject_action action); {.../* search the kset we belong to */top_kobj = kobj; while (! Top_kobj-> kset & top_kobj-> parent) top_kobj = top_kobj-> parent ;...... kset = top_kobj-> kset; uevent_ops = kset-> uevent_ops;/* 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 ;}...... /* let the kset specific function add its stuff */if (uevent_ops & uevent_ops-> uevent) {retval = uevent_ops-> uevent (kset, kobj, env ); // complete the private event if (retval) {pr_debug ("kobject: '% s' (% p): % s: uevent () of the kset object () returned "" % d \ n ", kobject_name (kobj), kobj, _ func __, retval ); goto exit ;}}............ /* call the user space program */argv [0] = uevent_helper; // specify the application layer program path retval = call_usermodehelper (argv [0], argv, env-> envp, UMH_WAIT_EXEC );......}
The communication between kobject_uevent and the upper layer is actually to create a thread for the application layer program in the kernel. This application is specified by the uevent_helper variable. So how can we modify this kernel variable? this involves another point.
There are some global variables in the kernel running process. these global variables determine the kernel running mode. During Linux kernel writing, these variables and kernel information are virtualized into a file in the "/proc/sys/kernel/" directory to set aside their interfaces for the user layer. Of course, it may also be in the "/sys/kernel/" directory, and they have some duplicates in this function. For details about the proc directory, refer to "man proc.
Now let's continue to discuss the changes to the uevent_helper variable. we found uevent_helper under "/sys/kernel" and hotplug under "/proc/sys/kernel. Both of these modifications can be modified to the uevent_helper variable in the kernel.
II. example
Now let's create a specific test program to test it.
# Include
# Include
# Include
# Include
# Include
# Define NAME_PARENT "dem_parent" // parent object # define NAME_CHILD "dem_child" // child object # define NAME_SET "dem_set" // kset set to which the child object belongs # define NAME_CHATTR "child_attr" // The Property File static struct kobject * parent of the child object; static struct kobject * child; static struct kset * c_kset; static int flag = 0; static ssize_t attr_show (struct kobject * kobj, struct attribute * attr, char * buf) {ssize_t size = 0; size = sprintf (buf, "% d \ n", Flag); return size;} static ssize_t attr_store (struct kobject * kobj, struct attribute * attr, const char * buf, size_t len) {// printk ("kobject: % x, kchild: % x \ n ", kobj, child); export ld_flag = flag; flag = buf [0]-'0 '; // notify the application layer of the information transmitted from the property file, and then verify the hot swapping switch (flag) {case 0: kobject_uevent (kobj, KOBJ_ADD); break; case 1: kobject_uevent (kobj, KOBJ_REMOVE); break; case 2: kobject_uevent (kobj, KOBJ_CHANGE); break; case 3: k Object_uevent (kobj, KOBJ_MOVE); break; case 4: kobject_uevent (kobj, KOBJ_ONLINE); break; case 5: kobject_uevent (kobj, KOBJ_OFFLINE); break; default: break ;} return old_flag;} // property file of the child object static struct attribute kchild_attr [] = {{. name = NAME_CHATTR ,. mode = S_IRUGO | S_IWUGO, }}; // static struct sysfs_ops kchild_ops = {. show = attr_show ,. store = attr_store,}; static struct kobj_type kchild_ty Pe = {. sysfs_ops = & kchild_ops,}; static int _ init demo_init (void) {printk ("load vision: % s \ n", _ TIME __); // Create a kobject object as the child object's parent object parent = kobject_create_and_add (NAME_PARENT, NULL); if (NULL = parent) {printk ("error: % s, % d \ n ", _ FILE __, _ LINE _); goto ERR_KPARENT;} // if the child object requires event notification, it must belong to a kset set c_kset = kset_create_and_add (NAME_SET, NULL, parent); if (NULL = c_kset) {printk ("error: % s, % d \ n ",_ _ FILE __, _ LINE _); goto ERR_KSET;} child = kzarloc (sizeof (* child), GFP_KERNEL); if (! Child) {printk ("error: % s, % d \ n", _ FILE __, _ LINE _); goto ERR_KCHILD;} intretval; child-> kset = c_kset; retval = kobject_init_and_add (child, & kchild_type, parent, NAME_CHILD); if (retval) {printk ("error: % s, % d \ n ", _ FILE __, _ LINE _); goto ERR_CHILDADD;} // create the property FILE retval = sysfs_create_file (child, & kchild_attr) corresponding to the child object; OUT: return retval; ERR_CHILDADD: kobject_put (child); child = NULL; ERR_KCHILD: kset_unre Gister (c_kset); c_kset = NULL; ERR_KSET: kobject_del (parent); parent = NULL; ERR_KPARENT: return-1;} static void _ exit demo_exit (void) {printk ("unload vision: % s \ n", _ TIME _); kobject_del (child); kset_unregister (c_kset); kobject_del (parent );} MODULE_LICENSE ("GPL"); module_init (demo_init); module_exit (demo_exit); the program idea is implemented according to the general driver loading idea. In the end, this program will create the dem_parent folder under the/sys/directory, and then create the dem_child and dem_set folders under it. The property file/sys/dem_parent/dem_child/child_attr is created. When we execute "echo '1'>/sys/dem_parent/dem_child/child_attr", the message notifies the child object of the kernel and calls the store processing function. This completes the communication between the application layer and the kernel layer.
The communication between the kernel layer and the user layer is implemented through kobject_uevent. In the store function, we have called this function. It will find the corresponding program or script file in the specified path and build the process in the kernel space. This completes the communication between the kernel and the user space. After searching, we found that the path of the application layer program was specified by the uevent_helper variable, and a file uevent_helper was found in the/sys/kernel directory, which should be left to the application layer interface. Enter "echo '/sbin/xxx'>/sys/kernel/uevent_helper ", when we run "echo '1'>/sys/dem_parent/dem_child/child_attr" again, we will find that the program or script we set at the application layer is called (remember to modify the program or script ).