Author:Liu Hongtao, a gold medal lecturer in the far-sighted embedded School of Huaqing.
I. background knowledge of module usage count
A module is a kernel function component that can be dynamically loaded and detached during kernel running. 2.6 The Module name in the kernel is *. Ko. When a module is used, it cannot be detached. During programming, you need to use "Count" to describe whether the module is being used.
Ii. How to Implement the 2.4 kernel count
In the 2.4 kernel, the module uses the mod_inc_use_count and mod_dec_use_count macros to manage its own counts. When writing a module, we usually add mod_inc_use_count to the open method, and add mod_dec_use_count to the close method to implement counting.
Iii. How to Implement the 2.6 kernel count
2.6 The Kernel provides a more robust and flexible module counting management interface try_module_get (& Module). module_put (& module) replaces the module in section 2.4 with the count management macro. The module does not have to be managed by itself, and the SMP and preempt mechanisms are taken into account when the module is used for counting (refer to the implementation of try_module_get and module_put in module. h ).
Int try_module_get (struct module * module); used to increase the module usage count. If the return value is 0, the call fails. The expected module is not loaded or is being detached.
Void module_put (struct module * module); reduces the module usage count.
The introduction and use of try_module_get and module_put are closely related to the device model in the 2.6 kernel. 2.6 The kernel defines the struct module * owner domain for different types of devices to point to the module that manages the device. For example, the definition of a character device:
Struct cdev
{
Struct kobject kobj;
Struct module * owner;
Struct file_operations * OPS;
Struct list_head list;
Dev_t dev;
Unsigned int count;
};
Struct file_operations {
Struct module * owner;
Loff_t (* llseek) (struct file *, loff_t, INT );
Ssize_t (* read) (struct file *, char _ User *, size_t, loff_t *);
Ssize_t (* write) (struct file *, const char _ User *, size_t, loff_t *);
......
};
From the perspective of device usage, when you need to enable or start using a device, use try_module_get (Dev-> owner) to increase the use count of the owner module that manages the device; when you disable or stop using this device, use module_put (Dev-> owner) to reduce the use count of the owner module that manages this device. In this way, when the device is in use, the module managing the device cannot be detached; the module can only be detached when the device is no longer in use.
In the 2.6 kernel, developers who write drivers for specific devices do not need to use try_module_get and module_put, because the driver written by the developer is usually the owner module that supports a specific device, the counting manager of the device owner module simplifies device driver development by implementing more underlying code in the kernel, such as bus drivers or core modules shared by such devices.
Iv. Examples of how the 2.6 kernel module uses counting
An example of writing a 2.6-core character device driver is provided to illustrate the problem. When writing a device driver in the 2.6 kernel, you will see the following template during the initialization process:
Static struct file_operations simple_remap_ops = {
. Owner = this_module,
. Open = simple_open,
......
};
Static void simple_setup_cdev (struct cdev * Dev, int minor,
Struct file_operations * FoPs)
{
Int err, devno = mkdev (simple_major, minor );
Cdev_init (Dev, fops );
Dev-> owner = this_module;
Err = cdev_add (Dev, devno, 1 );
/* Fail gracefully if need be */
If (ERR)
Printk (kern_notice "error % d adding simple % d", err, minor );
}
Both cdev and file_operations direct their struct module * owner members to this_module. So what is this_module?
Include/Linux/module. h In the kernel source code directory
# Ifdef Module
# Define module_generic_table (gtype, name )\
Extern const struct gtype ##_ ID _ mod _ ## gtype ##_ table \
_ Attribute _ (unused, alias (_ stringify (name ))))
Extern struct module _ this_module;
# Define this_module (&__ this_module)
# Else /*! Module */
# Define module_generic_table (gtype, name)
# Define this_module (struct module *) 0)
# Endif
The _ this_module symbol is generated after it is loaded to the kernel. After the insmod command is executed, the kernel/module is called. A System in C calls sys_init_module, which calls the load_module function, creates the entire kernel module File imported into the user space into a kernel module, and returns a struct module structure. From then on, in the kernel, this struct represents this kernel module. This_module is similar to the current process.
Struct module
{......
Struct module_ref Ref [nr_cpus];
}
Struct module_ref
{
Local_t count; // count used by the record module
}____ Cacheline_aligned;
Now let's look at how the kernel helps us complete the count.
In the 2.4 kernel, we increase the reference count in the open method to reduce the reference count in the close method. In the 2.6 kernel, the kernel must also help us implement the same function during open and close operations.
The general process for enabling a character device is as follows:
Sys_open ()-> do_sys_open ()-> do_filp_open ()-> nameidata_to_filp ()->__ dentry_open ()-> chrdev_open ()-> open ()
The 2.6 kernel does not require the module to display the implementation count in the open. The actual use of the module count is done in chrdev_open.
Kernel source code fs/char_dev.c
Static int chrdev_open (struct inode * inode, struct file * filp)
{
Struct cdev * P;
......
Cdev_get (p); // increase the usage count of the module pointed by the owner in cdev
......
Filp-> f_op = fops_get (p-> OPS);} // increase the usage count of the module pointed by the owner in file_operations
If (filp-> f_op-> open ){
Lock_kernel ();
Ret = filp-> f_op-> open (inode, filp); // call open in the device driver
Unlock_kernel ();
}
}
Static struct kobject * cdev_get (struct cdev * P)
{
Struct module * owner = p-> owner;
......
If (owner &&! Try_module_get (owner ))
Return NULL;
......
}
Kernel source code include/Linux/fs. h
# Define fops_get (FoPs )\
(FoPs) & try_module_get (FoPs)-> owner )? (FoPs): NULL ))
General process of disabling a device
Sys_close ()-> filp_close ()-> fput ()->__ fput ()-> release ()
The 2.6 kernel does not require the module to display the implementation count in the release. The actual use of the module count is completed in _ fput.
Void _ fput (struct file * file)
{
Struct dentry * dentry = file-> f_path.dentry;
Struct vfsmount * mnt = file-> f_path.mnt;
Struct inode * inode = dentry-> d_inode;
If (file-> f_op & file-> f_op-> release)
File-> f_op-> release (inode, file); // call the release in the device driver.
......
Cdev_put (inode-> I _cdev); // reduce the usage count of the module pointed by the owner in cdev
......
Fops_put (file-> f_op); // reduce the usage count of the module pointed by the owner in file_operations
......
}