Analysis on kernel mechanism of Linux character device driver

Source: Internet
Author: User
Tags function prototype

Some time ago when learning the Linux device driver, I looked at the Chen Cossong "deep Linux device driver kernel mechanism" book. To tell the truth, this is a very good book, the author not only give the device driver development process of the required knowledge points (such as the corresponding functions and data structures), but also into the Linux kernel to analyze the principles of these functions or data structures, the entire process and principles of device-driven development are analyzed in place. But may be due to too many knowledge points, the principle is also relatively deep reason, this book in the knowledge points of the layout of some large span, so read it seems a bit difficult, but if the first time to look at the more serious words, and then look back to the second is really good to understand the author's writing ideas. Chapter II character device driver I also looked at two times to understand, take advantage of this heat, in accordance with their own ideas summed up, so that the next time to see, you can follow 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;} Key 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 objectChr_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 a Linux system, various devices are in the form of files, so the device driver contains functions for manipulating character device files, such as opening, reading, writing and other operations functions. such as Chr_open (), Chr_read () and so on. These functions are to be implemented by the programmer himself.

(2) The driver contains a struct object of type struct file_operations 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) In the module initialization function to complete the initialization of the character device object, this initialization process calls the programmer-defined data structure Chr_ops as a parameter, in the initialization function also completed the allocation of device 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 the assigned device number is freed.

2 , character device driver kernel mechanism detailed

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>, as follows:

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 *);};

You can see that the member variable plans for struct file_operations are all function pointers. In reality, the writing of the character device driver is basically centered around 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 (), will eventually go to the corresponding function pointer member in Structfile_operations, under the mechanism of the Linux kernel.

(2) This_module

This is a macro definition #definethis_module (&__this_module)

The compiler toolchain 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

Character device driver Management core object is the character device, the kernel for the character device abstract out a specific data structure struct CDEV, which is defined in the file <include/linux/cdev.h>, as follows:

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 should be noted that the kernel introduces a struct CDEV data structure as a character device abstraction, only to meet the system to the character device driver framework design needs, in reality a specific character hardware device data structure may be more complex, in this case, the struct Cdev often appears as an inline member variable in the device's data structure, 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), we discuss how to produce a struct Cdev object, and then discuss how to initialize a Cdev object. To do this, the kernel provides the corresponding initialization function cdev_init, defined in <fs/char_dev.c>, as follows:

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;}

Parameter description:

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

*fops: A struct pointer containing 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 corresponding 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 as follows:<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. Defined as follows:

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 parameter is 0, which results in a

__register_chr_dev_region performs 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: it in the For loop, the last item from the Chrdevs number is scanned forward, if an item in the array is found, such as the number of the corresponding value is NULL, then the item corresponding to the index value I as the assigned main device number is returned to the driver, and add it to the chrdevs[i] corresponding hash list. If the assignment succeeds, the assigned master device number is recorded on the Structchar_device_struct object CD (the array holds this object) and the CD is returned to the Alloc_chrdev_region function, which is passed *dev=mkdev (cd- >major,cd->baseminor) returns the newly assigned device number to the function-user.

Register_chrdev_region () function: The function prototype is as follows:

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

Parameter description:

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

The function is to record the current device driver to use the device number in the Chrdev array, to track the use of the system device number, so as 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 the initialization of a character device, it can be added to the system so that other modules can use it. Adding a character to the system requires calling the function Cdev_add. It is defined 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, and the node is 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 is defined 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 be added to the system, and then add a node object of type struct probe to the linked list managed by Probe[i]. Where the probe node contains the device's main device number, and a pointer to the character device object. Such as:



By calling Cdev_add, it means that a character device object has been added to the system, and the system can find it when 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 what the Cdev_del () function does, as defined below:

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 file node under the corresponding/dev/represents a device. In a Linux system, each file has two different representations. For any file, the user space is typically identified by a file name such as Demodev. In kernel space, the inode is generally used to represent it. 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 an example, it is assumed that the front facing driver for the/DEV/DEMODEV device node has implemented 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 that describes the user's state is called in step to the open function provided by the driver (in this case 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 obvious that the kernel needs to establish a relationship with file for FD when opening the device file, and to associate the file with the FoPs in the driver.

User space calls the Open function, which initiates a system call through the Sys_open function into the kernel space. The invocation relationship is as follows:


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

2) The Do_sys_open function then calls the Do_filp_open function, which finds the inode corresponding to 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 process opens. The struct file is defined as follows:

struct file

{     ....

Const structfile_operations *f_op;

....

}

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

3) The Linux system maintains a file descriptor table for each process. The file descriptor (FD) of a process open file is the index value of the file descriptor. Each table item in the File descriptor table has a pointer to the open file. This pointer is a pointer to the struct file type. That is: In the 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 "/dev/demodev" node to Filp->f_op. The node created by (7) can know,inode->i_fop=&def_chr_fops; so that after 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. Thus the driver's open function can be called.

3 , Summary

Cond...



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.