Analysis on kernel mechanism of Linux character device driver

Source: Internet
Author: User

Some time ago when learning the Linux device driver, I looked at the Chen Cossong "deep Linux device driver kernel mechanism" book.

Tell the truth. This is a very good book, the author not only gives the device driver development process required knowledge points (such as corresponding functions and data structures), but also into the Linux kernel to analyze the principles of these functions or data structures. The whole process and principle of device-driven development are analyzed very well in place. But it may be because there are too many points of knowledge. The principle is also more deep reason, this book in the knowledge point of typesetting the span is somewhat big. So it seems a little hard to read, but assuming the first time to look at the more serious words, and then look back to the second can really very good understanding of the author's writing ideas.

Chapter Two character device drivers I read it two times before I understood. Take advantage of this heat, in accordance with their own ideas summed up, so that the next time to see, the line according to their own better understanding of the way to see.


1 , character device driver framework:

Before delving into the character device driver, give a typical framework structure of the device driver, so as to have a preliminary understanding of the character device driver.

<span style= "Background-color:rgb (255, 255, 255); >/* character device driver source code *//*demo_chr_dev.c*/#include <linux/module.h> #include <linux/kernel.h> #include < linux/fs.h> #include <linux/cdev.h></span>static struct Cdev chr_dev;//defines a character device object static dev_t ndev;// The device number of the character device node is static int chr_open (struct inode *nd,struct file *filp)//Open device {int major=major (nd->i_rdev); int Minor=minor (Nd->i_rdev);p rintk ("chr_open,major=%d,minor=%d\n", Major,minor); return 0;} Static ssize_t chr_read (struct file *f,char __user *u,size_t sz,loff_t *off)//Read device file contents {PRINTK ("in the Chr_read () function !\n "); return 0;} Important data structure struct file_operations chr_ops={.owner=this_module,.open=chr_open,.read=chr_read,};static int demo_init ( void)//module initialization function {int ret;cdev_init (&chr_dev,&chr_ops);//Initialize character device object. Chr_ops defined above ret=alloc_chardev_region (&ndev,0,1, "Char_dev");//Assign Device number if (ret<0) return RET;PRINTK ("Demo_init ( ): major=%d,minor=%d\n ", Major (Ndev), minor (Ndev)); Ret=cdev_add (&chr_dev,ndev,1);//Character Device object CHr_dev register to the system if (ret<0) return Ret;return 0;} static void Demo_exit (void) {PRINTK ("removing Chr_dev module...\n"); Cdev_del (&chr_dev);//Character Device Object Chr_ Dev unregisters unregister_chr_region (ndev,1) from the system,//releases the assigned device number}module_init (Demo_init); Module_exit (Demo_exit); Module_license ("GPL");

Kernel modules can be Demo_chr_dev.ko after compilation

Overall understanding of the driver framework:

(1) in the Linux system. Various devices are present in the form of files. Therefore, the device driver includes functions for manipulating character device files. such as opening, reading, writing and other operation functions. such as Chr_open (), Chr_read () and so on. These functions are to be implemented by the program ape itself.

(2) A struct object of type struct file_operations is included in the driver, such as Chr_ops, which is used to hold various operation functions for the character device.

(3) device driver as kernel module. Ko is installed into the system, so in the program framework, you must call the Module_init () function to complete the installation of the module. Call the Module_exit () function to complete the unload of the module.

(4) The initialization of the character device object is completed in the module initialization function. This initialization process calls the program Ape-defined data structure chr_ops as a parameter. At the same time in the initialization function also completed the allocation of equipment number, device object registration and so on.

(5) In the Unload function of the module, the corresponding character device object is written off from the system. and releases the assigned device number.

2 , character device driver kernel mechanism specific explanation

To make it easier to understand the driver, we analyze and interpret the corresponding function and data structure in the framework driver given in the previous step.

(1) Structural body structfile_operations

The structure is defined in file <include/linux/fs.h>, in detail such as the following:

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 *); ssize_t (*aio_re  AD) (struct KIOCB *, const struct IOVEC *, unsigned long, loff_t); ssize_t (*aio_write) (struct KIOCB *, const struct IOVEC *, unsigned long, loff_t); int (*readdir) (struct file *, void *, filldir_t); unsigned int (*poll) (struct file *, struct P Oll_table_struct *); int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long); Long (*UNLOCKED_IOCTL) (Stru CT file *, unsigned int, unsigned long), Long (*compat_ioctl) (struct file *, unsigned int, unsigned long); int (*mmap) (str UCT file *, struct vm_area_struct *), int (*open) (struct inode *, struct file *), int (*flush) (struct file *, fl_owner_t I d); Int (*release) (struct inode *, struct file *), int (*fsync) (struct file *, struct dentry *, int datasync); Int (*aio_fs Ync) (struct KIOCB *, int datasync); int (*fasync) (int, struct file *, int), int (*lock) (struct file *, int, struct file_lock *); ssize_t (*sendpag e) (struct file *, struct page *, int, size_t, loff_t *, int); unsigned long (*get_unmapped_area) (struct file *, unsigned L Ong, unsigned long, unsigned long, unsigned long); int (*check_flags) (int); int (*flock) (struct file *, int, struct FILE_LO CK *); ssize_t (*splice_write) (struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int); ssize_t (*splice_re AD) (struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int); int (*setlease) (struct file *, long, struct f Ile_lock *);};

The member variable plan that can see struct file_operations is all function pointers.

In reality, the character device driver is written. It's basically about how to implement the member of the function pointers in the struct file_operations.

The application's invocation of a file class function, such as read ()/open (), is finally transferred to the corresponding function pointer member in the structfile_operations under the mechanism of the Linux kernel.

(2) This_module

This is a macro definition #definethis_module (&__this_module)

The compiler tool chain for the __this_module kernel module is the struct module class object produced by the current module. So this_module is actually a pointer to the current module object.

(3) abstract struct Cdev for character devices

The core object of the character device driver management is the character device, which abstracts a detailed data structure for the character device struct Cdev, which is defined in file <include/linux/cdev.h>. For example, the following:

struct Cdev {struct Kobject kobj;//embedded kernel object struct module *owner;//driver The kernel modules object Pointer const struct File_operations *ops;  struct object struct List_head list;//character device chain list dev_t dev;//character device device number, consisting of main device number and secondary device number unsigned int count;// Number of secondary device numbers belonging to the same main device number};

It is important to note that the kernel introduces the struct CDEV data structure as an abstraction of the character device, but in order to satisfy the need of the system to design the character device driver framework, the data structure of a detailed character hardware device in reality may be more complex in such cases. struct Cdev is often used as an inline member variable out of the data structure of today's devices, such as:

struct my_keypad_dev{//hardware-related member variables int A, *p;...//embedded struct cdev struct object struct Cdev c_dev;}

There are two ways in which a struct Cdev object can be produced in a device driver:

Static way: static struct Cdev chr_dev;

Dynamic mode: Static struct Cdev *p=kmalloc (sizeof (struct cdev), gfp_kernel);

(4) Initialization function Cdev_init

In (3) discussed how to produce a struct Cdev object. The next step is to discuss how to initialize a Cdev object. For this The kernel provides the corresponding initialization function Cdev_init. Defined in <fs/char_dev.c>, for example the following:

void Cdev_init (struct cdev *cdev, const struct file_operations *fops) {memset (cdev, 0, sizeof *cdev); Init_list_head (& cdev->list); Kobject_init (&cdev->kobj, &ktype_cdev_default); cdev->ops = FoPs;}

Description of the parameters:

*cdev: Pointing to the device object that needs to be initialized

*fops: A struct pointer that includes an action function for the character device

(5) Device number

In a Linux system, a device number is comprised of a master device number and a secondary device number. The kernel uses the main device number to locate the appropriate device driver. The secondary device number is used by the driver to identify several of the same devices that it manages.

The device number is a valid resource for the system Management device. Linux uses the dev_t (32-bit unsigned integer) to represent a device number.

A, the kernel provides the following three macros for operation of the device number:<include/linux/kdev_t.h>

#define MAJOR (Dev) ((unsignedint) (dev) >>minorbits)   //Extract main device number # define MINOR (Dev) ((dev) &minorbits)  //Extract Secondary unit number       #define MKDEV (Ma,mi) ((MA) <<minorbits) | ( MI))              //Synthetic equipment number

B, in order to effectively manage the device number of the character device, the kernel defines a global pointer array chrdevs, each item in the array is a pointer to the struct CHAR_DEVICE_STRUCT type. The assigned character device numbers in the system are stored in the array. The pointer array is defined such as the following:<fs/char_dev.c>

static struct Char_device_struct {struct char_device_struct *next;unsigned int major;unsigned int baseminor;int minorct; Char name[64];struct Cdev *cdev;/* 'll die */} *chrdevs[chrdev_major_hash_size];

C, the other kernel also provides two functions for assigning and managing device numbers, defined in <fs/char_dev.c>

Alloc_chrdev_region () function: This function is used to assign a device number, and the assigned master device number range will be between 1~254. Definitions such as the following:

int alloc_chrdev_region (dev_t *dev, unsigned baseminor, unsigned count,const char *name) {struct char_device_struct *cd; cd = __register_chrdev_region (0, Baseminor, Count, name), if (Is_err (CD)) return Ptr_err (CD); *dev = MKDEV (Cd->major, Cd->baseminor); return 0;}

The core of this function is to call __register_chr_dev_region, and the first number of parameters is 0. This will cause

__register_chr_dev_region run the following logic:

static struct char_device_struct *__register_chrdev_region (unsigned int major, unsigned int baseminor,   int minorct, const char *name) {jkjkhujhklkljkljmnstruct char_device_struct *cd, **cp;int ret = 0;int I;CD = kzalloc (sizeof (struct CHAR_ device_struct), Gfp_kernel), if (cd = = NULL) return err_ptr (-ENOMEM); Mutex_lock (&chrdevs_lock);/* Temporary */if ( Major = = 0) {for (i = array_size (Chrdevs)-1, i > 0; i--) {if (chrdevs[i] = = NULL) break; if (i = = 0) {ret =-ebusy;goto out;} Major = I;ret = major;} ...}

The principle of this code is that it is scanned forward from the last item of the Chrdevs number in the For loop, assuming that an item in the array is found, for example, the corresponding value of item I is null. Then return the corresponding index value I as the assigned main device number to the driver and add it to the corresponding hash list of chrdevs[i]. Assuming the assignment succeeds, the assigned master device number is recorded on the Structchar_device_struct object CD (the array holds the object) and the CD is returned to the Alloc_chrdev_region function. The latter returns the newly assigned device number through *dev=mkdev (Cd->major,cd->baseminor) to the function-user.

Register_chrdev_region () function: function prototypes such as the following:

int register_chrdev_region (dev_t from,unsigned count,const char *name) {}

Description of the parameters:

From: Indicates the device number; count: Number of consecutive device numbers. Name: the device or driver.

The function is to record the device number to be used by the current device driver in the Chrdev array. Used to track the use of system device numbers to avoid conflicting device numbers. When using this function, you should know in advance what device number it is using.

(6) Registration of character devices

After a character device is initialized, it can be added to the system so that other modules are capable of using it. Adding a character to the system requires calling the function Cdev_add. Its definition is as follows:

int Cdev_add (struct cdev *p, dev_t Dev, unsigned count) {P->dev = Dev;p->count = Count;return kobj_map (cdev_map, Dev, Count, NULL, Exact_match, Exact_lock, p);}

The core function of Cdev_add is implemented by the Kobj_map () function. After calling Cdev_add, a pointer to the character device object to be registered is embedded in a node of type struct probe. The node is then added to the hash list implemented by Cdev_map. The definitions for struct probe and Cdev_map are as follows:

<fs/char_dev.c> static sturct Kobj_map *cdev; This is a global variable of struct KOBJ_MAP pointer type.

The Chrdev_init function is responsible for initializing the Linux system during startup.

The struct KOBJ_MAP definition is as follows:

struct Kobj_map {struct probe {struct probe *next;dev_t dev;unsigned long range;struct module *owner;kobj_probe_t *get;int (*lock) (dev_t, void *); void *data;} *probes[255];struct mutex *lock;};

Kobj_map () Function procedure: Obtain the index value of the probes array by major The main device number of the device to increase the system, and then add a node object of type struct probe to the linked list managed by Probe[i]. The main device number of the device is included in the probe node. And a pointer to a character device object.

For example, with:



By calling Cdev_add, it means that a character device object has been added to the system. The system will be able to find it when it is needed.

In the Cdev_add () function, a node of the struct probe type is dynamically assigned.

When device objects are removed from the system, they need to be removed from the linked list and the memory space occupied by the nodes is freed. This is how the Cdev_del () function works. Definitions such as the following:

void Cdev_del (struct Cdev *p) {cdev_unmap (P->dev, P->count); Kobject_put (&p->kobj);}

For drivers that exist in the form of kernel modules. As a general rule. The Unload function of the module should be responsible for invoking this function to remove the managed device object from the system.

(7) Generation of device file nodes

In Linux systems, hardware devices are in the form of files that exist under/dev/. That is, each of the file nodes under the corresponding/dev/represents a device. In a Linux system, each file has two different representations. For a random file, the file name is generally used in user space to identify such as Demodev. In the kernel space. It is usually expressed in inode.

such as 168. They all actually point to the same file. For device files, there are:

inode->i_fop=&def_chr_fops;

inode->i_rdev=rdev;

(8) Opening operation of character device file

As a sample, it is assumed that the driver previously corresponding to the/DEV/DEMODEV device node has implemented such as the following struct File_operations object Chr_fops and the Open function Chr_open.

struct File_operations chr_ops=

{

. Owner=this_module,

. Open=chr_open,

. Read=chr_read,

};

static int Chr_open (struct inode *nd,structfile *filp)

{

Intmajor=major (Nd->i_rdev);

Intminor=minor (Nd->i_rdev);

PRINTK ("chr_open,major=%d,minor=%d\n", Major,minor);

Return0;

}

The Open function prototype for a user-space application is:

int open (Constchar *filename,int flages,mode_t mode);

The prototype of the open function in a driver in kernel space is:

Structfile_operations

{     ...

Int (*open) (struct inode *,struct file *);

...

}

Next we see how the Open function, which describes the user state, is how to invoke the open function provided by the driver (in this example, the Chr_open function).

As can be seen from the previous three functions: the user-state open function returns the file descriptor FD (shaping).

The parameter type in the driver is struct file*file. It is clear that the kernel needs to establish some kind of relationship between FD and file when opening the device files. It also establishes an association with FoPs in the driver for file.

User space calls the Open function, which initiates a system call through the Sys_open function into the kernel space. Call relationships such as the following:


1) The Do_sys_open function first assigns an unused file descriptor FD to this open operation by Get_unused_flags.

2) The Do_sys_open function then calls the Do_filp_open function. This function looks for the appropriate inode for the "/dev/demodev" device file.

After finding the inode, call the function Get_empty_filp function to assign the open file a new struct file type of memory space (return pointer). The kernel uses a struct file object to describe each file that the narrative process opens.

The definition of a struct file is as follows:

struct file

{     ....

Const structfile_operations *f_op;

....

}

As can be seen from the definition of the struct file, the struct file object includes a pointer to the struct file_operations type.

3) The Linux system maintains a file Description descriptor table for each process. The file Description Descriptor (FD) of a process open file is the index value of the file description descriptor.

Each table entry in the File Description descriptor table has a pointer to the open file. This pointer is a pointer to the struct file type. That is, in the descriptive descriptor table. Only the corresponding table entry can be found through the FD index, and the value of the table entry is FILP, which points to the struct file type space allocated by the kernel for the file that was just opened.

4) in the latter part of the Do_sys_open function, call the function __dentry_open function to assign the I_FOP in the inode of the corresponding node "/dev/demodev" to Filp->f_op.

Created by a node in (7) to be able to know,inode->i_fop=&def_chr_fops; so that the assignment operation Filp->fop=inode->i_fop, filp->fop=&def_ Chr_fops, the file structure member *fop points to the struct file_operations type data structure in the device driver. So that the driver's open function can be called.

3 , Summary

Cond.

。。



Analysis on kernel mechanism of Linux character device driver

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.