Linux Kernel programming (character device files)

Source: Internet
Author: User
Linux Kernel programming (character device file)-General Linux technology-Linux programming and kernel information. The following is a detailed description. Now we are original-level kernel programmers who know how to write kernel modules that do not do anything. We are proud and proud of ourselves. But somehow we feel something missing. The module with mental tension is not that meaningful.
There are two main ways for the kernel module to communicate with processes. One is through a device file (such as a file in the/dev directory), and the other is to use the proc file system. One of the main reasons we write something into the kernel is to support some hardware devices, so we start with the device file.
The initial purpose of a device file is to allow processes to communicate with device drivers in the kernel and communicate with physical devices (modem, terminal, and so on ). The implementation of this method is as follows:
Each device driver corresponds to a certain type of hardware device and is assigned a master code. The list of device drivers and their master codes can be found in/proc/devices. The physical device under the driver management of each device is also assigned a slave code. Whether these devices are installed or not, there will be a file in the/dev directory called a device file, corresponding to each device.
For example, if you perform ls? L/dev/hd [AB] * operation, you will see all the IDE Hard Disk Partitions that may be linked to a machine. Note that they all use the same master code, 3, but the slave code is different from each other. (Declaration: this is the case in the PC structure. I don't know if this is true for linux running on other structures .)
During system installation, all device files are created under the mknod command. They must be created in the/dev directory for no technical reasons, but they are easy to use. For device files created for testing purposes, for example, the exercises here may be placed in the directory where you compile the kernel module.
Devices can be divided into character devices and Block devices. The difference is that block devices have a buffer for requests, so they can choose the order in which to respond to them. This is very important for storage devices. Reading adjacent slices is much faster than moving away from each other. Another difference is that block devices can only accept input and return output by block (the block size varies with devices), while character devices accept input by the minimum bytes they can accept. Most devices are character devices because they do not need this type of buffer. You can view the first character in the output of the ls-l command to know whether a device file is a block device or a character device. B is a block device, and c is a character device.
This module can be divided into two parts: module and device driver. The Init_module function calls module_register_chrdev to add a device driver to the kernel block device table. Returns the primary code used by the driver. The Cleanup_module function unregisters a device.
These operations (Registration and logout) are the main functions of these two functions. Functions in the kernel do not run spontaneously like a process, but are called by the process through system calling, hardware interruption, or other parts of the kernel (as long as the specific function is called. Therefore, when you add code in and out, you should register it as the handle of a specific event, and when you delete it, you need to deregister the handle.
The device driver consists of four devices _
Another thing to remember is that we cannot allow administrators to delete kernel modules as they wish. This is because if the device files are opened by the process, when we delete the kernel module, using these files will lead to access the memory location of the normal function (read/write. If we are lucky, there will be no other code loaded, and we will get a vicious error message. If not, the other kernel module will be loaded to the same location, which means it will jump into the middle of another program in the kernel and the results will be unpredictable.
When you do not want a function to do anything, an error code (a negative number) is returned from that function ). But this is not possible in cleanup_module because it is a void function. Once cleanup_module is called, this module will die. However, there is a counter that records how many kernel modules are using this module. This counter is called the index counter (the last number that is not row in/proc/modules ). If this number is not 0, deletion will fail. The index counter of the module is included in the variable mod_use_count. We have defined macros (MOD_INC_USE_COUNT and MOD_DEC_USE_COUNT) for processing this variable, So we generally use macros instead of directly using the variable mod_use_count _, which will bring security when changes are implemented in the future.

Ex chardev. c

/* Chardev. c
* Copyright (C) 1998-1999 by Ori Pomerantz
*
* Create a character device (read only)
*/

/* The necessary header files */

/* Standard in kernel modules */
# Include/* Were doing kernel work */
# Include/* Specifically, a module */

/* Deal with CONFIG_MODVERSIONS */
# If CONFIG_MODVERSIONS = 1
# Define MODVERSIONS
# Include
# Endif

/* For character devices */
# Include/* The character device
* Definitions are here */
# Include/* A wrapper which does
* Next to nothing
* At present, but may
* Help for compatibility
* With future versions
* Of Linux */


/* In 2.2.3/usr/include/linux/version. h Using DES
* A macro for this, but 2.0.35 doesnt-so I add
* It here if necessary .*/
# Ifndef KERNEL_VERSION
# Define KERNEL_VERSION (a, B, c) (a) x 65536 + (B) * 256 + (c ))
# Endif


/* Conditional compilation. LINUX_VERSION_CODE is
* The code (as per KERNEL_VERSION) of this version .*/
# If LINUX_VERSION_CODE> KERNEL_VERSION (2, 2, 0)
# Include/* for put_user */
# Endif



# Define SUCCESS 0


/* Device Declarations *****************************/

/* 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

/* Is the device open right now? Used to prevent
* Concurent access into the same device */
Static int Device_Open = 0;

/* The message the device will give when asked */
Static char Message [BUF_LEN];

/* How far did the process reading the message
* Get? Useful if the message is larger than the size
* Of the buffer we get to fill in device_read .*/
Static char * Message_Ptr;


/* This function is called whenever a process
* Attempts to open 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

/* This is how you get the minor device number in
* Case you have more than one physical device using
* The driver .*/
Printk ("Device: % d. % d \ n ",
Inode-> I _rdev> 8, inode-> I _rdev & 0xFF );

/* We dont want to talk to two processes at
* Same time */
If (Device_Open)
Return-EBUSY;

/* If this was a process, we wowould have had
* Be more careful here.
*
* In the case of processes, the danger wocould be
* That one process might have check Device_Open
* And then be replaced by the schedualer by another
* Process which runs this function. Then, when
* The first process was back on the CPU, it wowould assume
* The device is still not open.
* However, Linux guarantees that a process wont
* Be replaced while it is running in kernel context.
*
* In the case of SMP, one CPU might increment
* Device_Open while another CPU is here, right after the check.
* However, in version 2.0 of the kernel this is not a problem
* Because theres a lock to guarantee only one CPU will
* Be kernel module at the same time.
* This is bad in terms of performance, so version 2.2 changed it.
* Unfortunately, I dont have access to an SMP box
* To check how it works with SMP.
*/

Device_Open ++;

/* Initialize the message .*/
Sprintf (Message,
"If I told you once, I told you % d times-% s ",
Counter ++,
"Hello, world \ n ");
/* The only reason were allowed to do this sprintf
* Is because the maximum length of the message
* (Assuming 32 bit integers-up to 10 digits
* With the minus sign) is less than BUF_LEN, which
* Is 80. be careful not to overflow buffers,
* Especially in the kernel !!!
*/

Message_Ptr = Message;

/* Make sure that the module isnt removed while
* The file is open by incrementing the usage count
* (The number of opened references to the module, if
* Its not zero rmmod will fail)
*/
MOD_INC_USE_COUNT;

Return SUCCESS;
}


/* This function is called when a process closes
* Device file. It doesnt have a return value in
* Version 2.0.x because it cant fail (you must ALWAYS
* Be able to close a device). In version 2.2.x it is
* Allowed to fail-but we wont let it.
*/
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Static int device_release (struct inode * inode,
Struct file * file)
# Else
Static void device_release (struct inode * inode,
Struct file * file)
# Endif
{
# Ifdef DEBUG
Printk ("device_release (% p, % p) \ n", inode, file );
# Endif

/* Were now ready for our next caller */
Device_Open --;

/* Decrement the usage count, otherwise once you
* Opened the file youll never get rid of the module.
*/
MOD_DEC_USE_COUNT;

# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Return 0;
# Endif
}


/* This function is called whenever a process which
* Have already opened the device file attempts
* Read from it .*/


# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Static ssize_t device_read (struct file * file,
Char * buffer,/* The buffer to fill with data */
Size_t length,/* The length of the buffer */
Loff_t * offset)/* Our offset in the file */
# Else
Static int device_read (struct inode * inode,
Struct file * file,
Char * buffer,/* The buffer to fill
* The data */
Int length)/* The length of the buffer
* (Mustnt write beyond that !) */
# Endif
{
/* Number of bytes actually written to the buffer */
Int bytes_read = 0;

/* If were at the end of the message, return 0
* (Which signifies end of file )*/
If (* Message_Ptr = 0)
Return 0;

/* Actually put the data into the buffer */
While (length & * Message_Ptr ){

/* Because the buffer is in the user data segment,
* Not the kernel data segment, assignment wouldnt
* Work. Instead, we have to use put_user which
* Copies data from the kernel data segment to
* User data segment .*/
Put_user (* (Message_Ptr ++), buffer ++ );


Length --;
Bytes_read ++;
}

# Ifdef DEBUG
Printk ("Read % d bytes, % d left \ n ",
Bytes_read, length );
# Endif

/* Read functions are supposed to return the number
* Of bytes actually inserted into the buffer */
Return bytes_read;
}




/* This function is called when somebody tries to write
* Into our device file-unsupported in this example .*/
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
Static ssize_t device_write (struct file * file,
Const char * buffer,/* The buffer */
Size_t length,/* The length of the buffer */
Loff_t * offset)/* Our offset in the file */
# Else
Static int device_write (struct inode * inode,
Struct file * file,
Const char * buffer,
Int length)
# Endif
{
Return-EINVAL;
}




/* Module Declarations ******************************/

/* The major device number for the device. This is
* Global (well, static, which in this context is global
* Within this file) because it has to be accessible
* Both for registration and for release .*/
Static int Major;

/* This structure will hold the functions to be
* Called when a process does something to the device
* We created. Since a pointer to this structure is
* Kept in the devices table, it cant be local
* Init_module. NULL is for unimplemented functions .*/


Struct file_operations Fops = {
NULL,/* seek */
Device_read,
Device_write,
NULL,/* readdir */
NULL,/* select */
NULL,/* ioctl */
NULL,/* mmap */
Device_open,
# If LINUX_VERSION_CODE> = KERNEL_VERSION (2, 2, 0)
NULL,/* flush */
# Endif
Device_release/* a.k. a. close */
};


/* Initialize the module-Register the character device */
Int init_module ()
{
/* Register the character device (atleast try )*/
Major = module_register_chrdev (0,
DEVICE_NAME,
& Fops );

/* Negative values signify an error */
If (Major <0 ){
Printk ("% s device failed with % d \ n ",
"Sorry, registering the character ",
Major );
Return Major;
}

Printk ("% s The major device number is % d. \ n ",
"Registeration is a success .",
Major );
Printk ("If you want to talk to the device driver, \ n ");
Printk ("youll have to create a device file. \ n ");
Printk ("We suggest you use: \ n ");
Printk ("mknod c % d \ n", Major );
Printk ("You can try different minor numbers % s ",
"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 theres an error, report it */
If (ret <0)
Printk ("Error in unregister_chrdev: % d \ n", ret );
}
2.1 multi-kernel source files
System calling is the main interface presented to the process by the kernel, which is generally the same in different versions. New systems may be added, but the behavior of old systems remains unchanged. Backward compatibility is necessary-the new kernel version cannot break the normal process rules. In most cases, the device file remains unchanged. However, the internal interfaces in the kernel can be changed between different versions.
Linux Kernel versions are classified into stable versions (n. <偶数> . M) and Development version (n. <奇数> . M ). The development edition contains all novel ideas, including those that are considered wrong or re-implemented in the next edition. Therefore, you cannot believe that these interfaces remain unchanged in those versions (that is why I have never bothered to support different interfaces in this book. This is a lot of work, but it will be out of date soon ). But in the stable version, we can think that the interfaces are the same, even in the corrected version (number m Refers ).
The MPG version supports kernel 2.0.x and 2.2.x. The two kernels are still different, so the compilation depends on the kernel version. The method is to use the macro LINUX_VERSION_CODE. In version a. B. c, the macro value is 216a + 28b + c. To get the specific kernel version number, we can use the macro KERNEL_VERSION. This macro is not defined in version 2.0.35 and can be defined as needed.
Related Article

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.