Topic: Linux kernel module programming guide (2)
Sender: kevintz () Prepared by: kevintz (00:59:18), in-station letter |
<Linux kernel module programming guide>
<Linux kernel module programming guide>
Translated by Ori pomerantz: tan Zhi (lkmpg@21cn.com)
Note:
1. lkmpg is a free book. The release and modification of the English version follows the GPL version 2 license. To
Saving time, I only translated most of the things, or this is just some Chinese in my learning.
Note-taking is not a strict translation, but I think it is enough. This article also allows free Publishing
But please contact me before release, but do not use this article for commercial purposes. In view of my own level
Errors are inevitable. Please correct them.
2. The example in this article is successfully debugged on Linux (kernel version 2.2.10. You must use Linux
The kernel module must be loaded. If not, select or upgrade the kernel module during kernel compilation.
Level your kernel to a version that supports the kernel module.
Chapter 2 character Device Files
We have written the simplest kernel module, and it does work properly. But we seem to be missing
Nothing. After all, such a simple kernel module is not very enthusiastic about us.
The kernel module has two main methods to communicate with processes. One is through the device file (like/dev directory
File), and the other is to use the proc file system. When we write a driver to support some hardware,
The device file is usually used, so we start with the device file.
The original purpose of the device file is to allow the process to communicate with the device driver in the kernel.
The program communicates with physical devices (such as modem and terminal. This is used to implement the character kernel below.
Module.
Each device driver responsible for managing the corresponding hardware is assigned a primary device number (Maj
Or number ). The list of available drivers in the system and their master device numbers are listed in/proc/Devi
CES file. Each physical device managed by the device driver is assigned a device number and
The/dev directory contains a file called a device file, regardless of whether the real physical device is installed or not,
It will all have this file under/dev.
For example, if you use LS-L/dev/HD [AB] *, you will see all the points connected to the device's IDE hard disk.
. They all use the same primary device number, but the secondary device number is different (assuming you are using the PC system ). I am not
Understand the Linux device status on machines running in other systems.
During system installation, all these device files are generated using the mknod command. There is no technical reason to make
These files must be stored in the/dev directory, which is just a useful practice. When our example
To create a device file, put it in the directory of the compiling kernel module is more convenient than put it in/dev.
Devices are divided into character devices and Block devices. The difference between Block devices is that there is a buffer for requests.
So it can respond to the request in a different order from the request. This is very helpful for storage devices and fast devices.
Important. Another difference is that block devices can only be measured in blocks (the block size varies with different devices ).
To read and write data. Character devices allow reading and writing any number of bytes, which is determined only by the needs of the process.
. Most devices are character devices, because they do not require a buffer mechanism or block-based operation.
. You can use the LS-l command to determine whether a device file is a block device or a character device, with the first character
'B' indicates a block device, and 'C' indicates a character device.
The kernel module examples in this chapter are divided into two parts: the device registration module and the device driver module. Init
_ Module function calls module_register_chrdev to add the device driver to the kernel
In the backup driver table, and return the master device number used by the driver. Cleanup_module function logout
Driver.
This (register and deregister the driver) is the most common feature of these two functions. Nothing in the kernel
Active running is usually called. For example, a process is called by the system, the hardware is interrupted, or other
To run functions in the kernel. So when you add code to the kernel, you need to do something
The processing function of the component to register it. When you remove it, you need to cancel it.
The device driver consists of four DEVICE _ <action> functions.
These functions are called when the device file of the master device Number. The kernel calls their methods
Is through the file_operations struct -- fops. It contains pointers to four functions and
Used in the backup driver.
Here, we need to remember that we cannot make the kernel module be used by root users at any time.
Rmmod. Because when a process is opening a device file, we remove a kernel module
File Access causes access to the memory location of the corresponding read/write function in the original driver. If Lucky
If no other code is loaded, we will get a bad error message. If unfortunately
The other kernel module is loaded to the same location, which means that the other kernel module is fully loaded.
Different functions. The result is unpredictable, but this error is not obvious.
Generally, you can return an error code (a negative number) in the function when you do not want to allow some things)
. However, the cleanup_module function cannot be used because it is a void function. Once clean_mo
When the dule is called, the module will die. However, you can use a reference counter (File/proc/modul
Number of the last line in ES) to indicate how many other kernel modules use the current module. If
If the number is not 0, rmmod will fail. The Reference Counter of the module is represented by the variable mod_use_count.
There are two macro definitions (mod_inc_use_count and mod_dec_use_count) to process this variable.
Mod_use_count _ should not be used directly, and macro should be used for processing. This makes our code be modified in the future.
The change is still safe.
Example chardev. c
/* Chardev. c
* Copyright (c) 1998 by Ori pomerantz
*
* Create a character device (read only)
*/
/* The necessary header files */
/* Standard in kernel modules */
# Include <Linux/kernel. h>/* We're doing kernel work */
# Include <Linux/module. h>/* specifically, a module */
/* Deal with config_modversions */
# If config_modversions = 1
# Define modversions
# Include <Linux/modversions. h>
# Endif
/* For character devices */
/* The character device definitions are here */
# Include <Linux/fs. h>
/* A wrapper which does next to nothing
* At present, but may help for compatibility
* With future versions of Linux */
# Include <Linux/wrapper. h>
# Define success 0
/* The name for our device, as it will appear in/proc/devices */
# Define device_name "char_dev"
/* The maximum length of the message from the device */
# Define buf_len 80
/* Indicates whether the device is opened? Used to prevent concurrent access to the same device */
Static int device_open = 0;
Static char message [buf_len];
/* This pointer is used to identify the location of information. When a user's process reads data, the user buffer is smaller than the message
It is necessary to use */
Static char * message_ptr;
/* This function is called when the user process opens the device file */
Static int device_open (struct inode * inode, struct file * file)
{
Static int counter = 0;
# Ifdef debug
Printk ("device_open (% P, % P)/n", inode, file );
# Endif
/* When multiple physical devices use this driver, this is the method for obtaining the device number */
Printk ("device: % d. % d/N", inode-> I _rdev> 8, inode-> I _rdev & 0xff );
/* Currently, we do not want to communicate with multiple user processes at the same time */
If (device_open)
Return-ebusy;
/* There may be an error. When a process obtains a value of 0 for device_open, it is increasing
When this value is set, the scheduling is stopped. Another process also opens the device file and adds the device_open
Value. If the first process runs again, the device_open value is still 0.
Incorrect.
But you don't have to worry. The Linux kernel ensures that a process will not be
So the above situation can be avoided.
In SMP scenarios, the 2.0 kernel locks to ensure that only one CPU is in the kernel module at a time.
. This affects performance, which should be safely corrected in later kernel versions. */
Device_open ++;
/* Initialize the message .*/
Sprintf (message,
"If I told you once, I told you % d times-Hello, world/N ",
Counter ++ );
/* Pay attention to buffer overflow, especially in the kernel module */
Message_ptr = message;
/* Ensure that the kernel module cannot be logged out when the device file is opened (by adding a counter)
If the counter is not zero, rmmod will fail */
Mod_inc_use_count;
Return success;
}
/* Call this function when the device file is disabled. It does not return errors, because you must ensure that
Close a device */
Static void device_release (struct inode * inode, struct file * file)
{
# Ifdef debug
Printk ("device_release (% P, % P)/n", inode, file );
# Endif
/* We're re now ready for our next caller */
Device_open --;
/* Reduce the counter */
Mod_dec_use_count;
}
/* This function is called when a process reads an opened Device File */
Static int device_read (struct inode * inode,
Struct file * file,
Char * buffer,
/* Buffer and length of the received data */
Int length)
{
/* Number of bytes actually written to the buffer */
Int bytes_read = 0;
# Ifdef debug
Printk ("device_read (% P, % d)/n ",
Inode, file, buffer, length );
# Endif
/* If we're at the end of the message, return 0 */
If (* message_ptr = 0)
Return 0;/* It means end of file */
/* Actually put the data into the buffer */
While (Length & * message_ptr ){
/* Because the buffer zone is in the user data segment and is not in the kernel space, you cannot assign a value.
To copy data, data from the kernel to the user space should be transmitted through the put_user call */
Put_user (* (message_ptr ++), buffer ++ );
Length --;
Bytes_read ++;
}
# Ifdef debug
Printk ("read % d bytes, % d left/N ",
Bytes_read, length );
# Endif
/* Return the number of bytes read */
Return bytes_read;
}
/* The function called when writing the device file. Currently not supported. The-einval code is returned */
Static int device_write (struct inode * inode,
Struct file * file,
Const char * buffer,
Int length)
{
# Ifdef debug
Printk ("device_write (% P, % P, % s, % d )",
Inode, file, buffer, length );
# Endif
Return-einval;
}
/* The primary device number is declared static because it is used for registration and cancellation */
Static int Major;
/* Structure of Device File Operations */
Struct file_operations fops = {
Null,/* seek */
Device_read,
Device_write,
Null,/* readdir */
Null,/* select */
Null,/* IOCTL */
Null,/* MMAP */
Device_open,
Device_release/* a.k. A. Close */
};
/* Initialize the module-register the character device */
Int init_module ()
{
/* Register the character device (at least try )*/
Major = module_register_chrdev (0,
Device_name,
& FOPS );
/* Negative values signify an error */
If (Major <0 ){
Printk ("sorry, registering the character device failed with % d/N"
,
Major );
Return Major;
}
Printk ("registeration is a success. The major device number is % d ./
N ",
Major );
Printk ("if you want to talk to the device driver, you'll have to/N"
);
Printk ("Create a device file. We suggest you use:/N ");
Printk ("mknod <Name> C % d <minor>/N", Major );
Printk ("You can try different minor numbes and see what happens./N"
);
Return 0;
}
/* Cleanup-unregister the appropriate file from/proc */
Void cleanup_module ()
{
Int ret;
/* Unregister the device */
Ret = module_unregister_chrdev (Major, device_name );
/* If there's an error, report it */
If (Ret <0)
Printk ("error in module_unregister_chrdev: % d/N", RET );
}
Note: This kernel module has many errors and compilation notes, so I wrote it myself.
Chapter to solve these problems.