Write in front
Please read it first https://blog.csdn.net/qq_46640863/article/details/122684580
Compile the linux kernel.
1, Design content and specific requirements
New Linux driver
Add a driver (using memory to simulate the device) and use module compilation.
requirement:
(1) New drivers can be loaded and unloaded dynamically.
(2) Use the driver through the program or command line.
(3) At least 256MB of data can be saved through the driver, and these data can be read out.
(4) To recompile the Linux kernel, you can imitate the implementation of ramdisk.
2, Design idea
The topic requires adding a driver to the memory simulation device. The memory simulation device can imitate the implementation of Ram Disk. After consulting relevant materials, we can know that the function of Ram Disk is to mount a part of memory into a partition of external memory space (disk). From the user's perspective, Ram Disk partition can read and write files just like disk partition.
However, Ram Disk is still different from real disk. After the virtual machine is restarted, the Ram Disk partition disappears, and the data inside the Ram Disk partition will also disappear.
Ram Disk also has its own meaning. If several files need to be read and written frequently, they can be placed on Ram Disk opened by memory, which greatly improves the speed of reading and writing.
In this topic, we adopt to imitate the implementation of Ram Disk. In Chapter 6, we will show the effect similar to Ram Disk that can be obtained by imitating the implementation of Ram Disk.
The Linux system treats all devices as files, / dev / device name is not a directory, but is similar to the pointer pointing to the block of devices, which cannot be read or written directly, but needs to be mounted first. To read and write files in the device, you need to mount the partition of the device to a directory in the system, and access the device by accessing the directory.
3, Design and implementation and source code analysis
When designing your own driver, you need to implement the initialization function when loading the module, that is, the entry function of the driver module. You also need to implement the function when unloading the module, that is, the exit function of the module. At the same time, the device's own request processing function should also be implemented.
Firstly, the data structure of the module is designed. First define the block device name, main device number, size (25610241024 bytes, i.e. 256MB) and the number of sectors of the block as 9.
#define SIMP_BLKDEV_DISKNAME "zombotany_blkdev" #define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR #define SIMP_BLKDEV_BYTES (256*1024*1024) #define SECTOR_SIZE_SHIFT 9
Define gendisk to represent a simple disk device, define the owner of the block device, define the request queue pointer of the block device, and open up the storage space of the block device.
static struct gendisk * zombotany_blkdev_disk; static struct block_device_operations zombotany_blkdev_fops = { .owner = THIS_MODULE, }; static struct request_queue * zombotany_blkdev_queue; unsigned char zombotany_blkdev_data[SIMP_BLKDEV_BYTES];
The method headers of entry function and exit function are as follows:
static int __init _init(void) static void __exit _exit(void)
The functions to be implemented in the entry function include four steps. 1. Apply for equipment resources. If the application fails, exit. 2. Set equipment related attributes. 3. Initialize the request queue and exit if it fails. 4. Add disk block device.
First, apply for equipment resources. Judge whether the application is successful. If it fails, exit.
zombotany_blkdev_disk = alloc_disk(1); if(! zombotany_blkdev_disk){ ret = -ENOMEM; goto err_alloc_disk; }
Next, set the device related properties. Set the device name, device number, fops pointer and number of sectors
strcpy( zombotany_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME); zombotany_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; zombotany_blkdev_disk->first_minor = 0; zombotany_blkdev_disk->fops = & zombotany_blkdev_fops; set_capacity( zombotany_blkdev_disk, SIMP_BLKDEV_BYTES>>9);
Initialize the request queue and exit if it fails.
zombotany_blkdev_queue = blk_init_queue( zombotany_blkdev_do_request, NULL); if(! zombotany_blkdev_queue){ ret = -ENOMEM; goto err_init_queue; } zombotany_blkdev_disk->queue = zombotany_blkdev_queue;
Finally, add a disk block device.
add_disk( zombotany_blkdev_disk); return 0;
The exit function of the module is relatively simple. You only need to release the disk block device, release the applied device resources, and clear the request queue.
static void __exit zombotany_blkdev_exit(void){ del_gendisk( zombotany_blkdev_disk); put_disk( zombotany_blkdev_disk); blk_cleanup_queue( zombotany_blkdev_queue); }
After implementing the entry and exit functions, you need to declare the module entry and exit.
module_init(xxxx_init); module_exit(xxxx_exit);
Implement the request processing function of the module. The data structures involved in the request processing function are as follows: current request, current request bio (the general block layer uses bio to manage a request), segment linked list of current request bio, current disk area and buffer.
struct request *req; struct bio *req_bio; struct bio_vec *bvec; char *disk_mem; char *buffer;
For a request, first judge whether the request is legal. The way to judge whether the request is legal is to judge whether the address is out of bounds.
if((blk_rq_pos(req)<<SECTOR_SIZE_SHIFT)+blk_rq_bytes(req)>SIMP_BLKDEV_BYTES){ blk_end_request_all(req, -EIO); continue; }
If the request is legal, obtain the current address location.
disk_mem =zombotany_blkdev_data + (blk_rq_pos(req) << SECTOR_SIZE_SHIFT); req_bio = req->bio;
The process of judging the request type and processing read requests and write requests is similar. When processing the read request, traverse the request list, find the buffer and bio, and copy the contents of the disk to the buffer. Find the next area of the disk and process the next request in the request queue.
while(req_bio != NULL){ for(i=0; i<req_bio->bi_vcnt; i++){ bvec = &(req_bio->bi_io_vec[i]); buffer = kmap(bvec->bv_page) + bvec->bv_offset; memcpy(buffer, disk_mem, bvec->bv_len); kunmap(bvec->bv_page); disk_mem += bvec->bv_len; } req_bio = req_bio->bi_next; } __blk_end_request_all(req, 0); break;
When processing write requests, the contents of the buffer are copied to the disk. Just exchange the two parameters when calling memcpy, and the rest are the same.
memcpy(disk_mem, buffer, bvec->bv_len);
The code of this part is as follows:
while(req_bio != NULL){ for(i=0; i<req_bio->bi_vcnt; i++){ bvec = &(req_bio->bi_io_vec[i]); buffer = kmap(bvec->bv_page) + bvec->bv_offset; memcpy(disk_mem, buffer, bvec->bv_len); kunmap(bvec->bv_page); disk_mem += bvec->bv_len; } req_bio = req_bio->bi_next; } __blk_end_request_all(req, 0); break;
The complete code of the module is shown in the appendix, and the file name is zombotany_blkdev.c
After writing the module code, you also need to write the makefile file. In the Linux file system, the file has no extension. Makefile file has no extension.
First, when reading and executing this Makefile for the first time, KERNELRELEASE is not defined, so make will only execute the content after else.
ifneq ($(KERNELRELEASE),)
Get the path of the kernel source code and the current working path
KDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd)
If Makefile has been executed before, you need to clean up the previously compiled modules.
modules: $(MAKE) -C $(KDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KDIR) M=$(PWD) modules_install clean: rm -rf *.o *.ko .depend *.mod.o *.mod.c Module.* modules.* .PHONY:modules modules_install clean
Generate o documents
else obj-m := simp_blkdev.o endif
See the appendix for the complete code of Makefile.
4, Operation process
Zombotany module source code_ blkdev. C and Makefile files are placed in the same directory.
Open the console in this directory and enter make. Can be generated correctly o documentation and ko file
5, Testing and analysis
After the module is compiled, first return to the directory and execute the statement insmod zombotany_blkdev.ko, the newly compiled zombotany_blkdev.ko module insertion. After execution, execute lsmod to view the list of block devices in the current system. As you can see, zombotany_blkdev already exists, has been inserted, and the size is 256MB. You can also execute lsblk to view the current block device.
In the / dev / path under the root directory, you can see zombotany_blkdev has been inserted. Execute ls /dev/
After the insertion is completed, the module needs to be formatted to establish a file system. Enter mkfs ext3 /dev/zombotany_ Blkdev, an ext3 file system is established on the memory emulation device.
After establishing the file system, you can mount the device to the directory of the file system. First, you need to create a directory to mount. mkdir -p /mnt/temp1. Mount the block device to this directory. mount /dev/zombotany_blkdev /mnt/temp1. Run mount | grep zombotany again_ blkdev. The mount is complete.
Execute lsmod again to check whether the module is called. This module is called by a user.
Execute ls/mnt/temp1 / to see that the current block device has only one file: lost+found file. Copy all code blocks in the current directory to the current directory, for example. Execute CP. / */ MNT / temp1 / finish copying, and then check the list of current block device files. Execute ls/mnt/temp1 /, you can see that the block device is correctly written to the file and can be read.
Execute df -H to view the current usage of each device. New device zombotany_blkdev has used 2.9MB.
Execute vi /mnt/temp1/zombotany_blkdev.c. You can also read this file.
Finally, uninstall the module. First delete all files in the directory. rm -rf /mnt/temp1/*.
Cancel the mount first. After executing umount /mnt/temp1, execute lsmod | grep zombotany_blkdev. You can see that this 256MB device is called by 0 users.
Execute rmmod zombotany_blkdev. This statement is used to remove the module. After running, execute lsmod grep zombotany again_ blkdev. You can see on the console that the system has no output. Description: zombotany_ The blkdev module has been completely removed.
appendix
Makefile
ifeq ($(KERNELRELEASE),) KDIR := /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) modules: $(MAKE) -C $(KDIR) M=$(PWD) modules modules_install: $(MAKE) -C $(KDIR) M=$(PWD) modules_install clean: rm -rf *.o *.ko .depend *.mod.o *.mod.c Module.* modules.* .PHONY:modules modules_install clean else obj-m := zombotany_blkdev.o endif
zombotany_blkdev.c
#include <linux/module.h> #include <linux/blkdev.h> #define SIMP_BLKDEV_DISKNAME "zombotany_blkdev" / / device name #define SIMP_BLKDEV_DEVICEMAJOR COMPAQ_SMART2_MAJOR / / main equipment No #define SIMP_BLKDEV_BYTES (256*1024*1024) / / the block device size is 256MB #define SECTOR_SIZE_SHIFT 9//9 sectors static struct gendisk * zombotany_blkdev_disk;// The gendisk structure represents a simple disk device static struct block_device_operations zombotany_blkdev_fops = { .owner = THIS_MODULE,//Equipment body }; static struct request_queue * zombotany_blkdev_queue;//Pointer to the block device request queue unsigned char zombotany_blkdev_data[SIMP_BLKDEV_BYTES];// Storage space of virtual disk block device //Request processing function static void zombotany_blkdev_do_request(struct request_queue *q){ struct request *req;// Requests in the processing request queue struct bio *req_bio;// Current requested bio struct bio_vec *bvec;// Linked list of bio segments currently requested char *disk_mem; // Disk area to read / write char *buffer; // A buffer in memory for requests from disk block devices while((req = blk_fetch_request(q)) != NULL){//Get request // Judge whether the current request is legal if((blk_rq_pos(req)<<SECTOR_SIZE_SHIFT) + blk_rq_bytes(req) > SIMP_BLKDEV_BYTES){//Determine whether the address has cross-border access printk(KERN_ERR SIMP_BLKDEV_DISKNAME":bad request:block=%llu, count=%u\n",(unsigned long long)blk_rq_pos(req),blk_rq_sectors(req));//If the access is out of bounds, the output blk_end_request_all(req, -EIO); continue;//Get next request } //Get the memory location where the operation is required disk_mem = zombotany_blkdev_data + (blk_rq_pos(req) << SECTOR_SIZE_SHIFT); req_bio = req->bio;// Get the bio of the current request switch (rq_data_dir(req)) { //Determine the type of request case READ: // Traverse the bio linked list of req requests while(req_bio != NULL){ //The for loop handles bio in the bio structure_ VEC structure array (bio_vec structure array represents a complete buffer) for(int i=0; i<req_bio->bi_vcnt; i++){ bvec = &(req_bio->bi_io_vec[i]); buffer = kmap(bvec->bv_page) + bvec->bv_offset; memcpy(buffer, disk_mem, bvec->bv_len);//Copy data in memory to buffer kunmap(bvec->bv_page); disk_mem += bvec->bv_len; } req_bio = req_bio->bi_next;//Request the next item in the linked list } __blk_end_request_all(req, 0);//It has been traversed break; case WRITE: while(req_bio != NULL){ for(int i=0; i<req_bio->bi_vcnt; i++){ bvec = &(req_bio->bi_io_vec[i]); buffer = kmap(bvec->bv_page) + bvec->bv_offset; memcpy(disk_mem, buffer, bvec->bv_len);//Copy the data in the buffer to memory kunmap(bvec->bv_page); disk_mem += bvec->bv_len; } req_bio = req_bio->bi_next;//Request the next item in the linked list } __blk_end_request_all(req, 0);//End of request list traversal break; default: /* No default because rq_data_dir(req) is 1 bit */ break; } } } //Module entry function static int __init zombotany_blkdev_init(void){ int ret; //Before adding a device, first apply for the resource of the device zombotany_blkdev_disk = alloc_disk(1); if(! zombotany_blkdev_disk){ ret = -ENOMEM; goto err_alloc_disk; } //Set the relevant attributes of the device (device name, device number, fops pointer) strcpy( zombotany_blkdev_disk->disk_name,SIMP_BLKDEV_DISKNAME); zombotany_blkdev_disk->major = SIMP_BLKDEV_DEVICEMAJOR; zombotany_blkdev_disk->first_minor = 0; zombotany_blkdev_disk->fops = & zombotany_blkdev_fops; //Pass the address of the block device request processing function into BLK_ init_ The queue function initializes a request queue zombotany_blkdev_queue = blk_init_queue( zombotany_blkdev_do_request, NULL); if(! zombotany_blkdev_queue){ ret = -ENOMEM; goto err_init_queue; } zombotany_blkdev_disk->queue = zombotany_blkdev_queue; //Number of initialization sectors set_capacity( zombotany_blkdev_disk, SIMP_BLKDEV_BYTES>>9); //Add disk block device at the entrance add_disk( zombotany_blkdev_disk); return 0; err_alloc_disk: return ret; err_init_queue: return ret; } //Exit function of module static void __exit zombotany_blkdev_exit(void){ // Release disk block device del_gendisk( zombotany_blkdev_disk); // Release requested device resources put_disk( zombotany_blkdev_disk); // Clear request queue blk_cleanup_queue( zombotany_blkdev_queue); } module_init( zombotany_blkdev_init);// Declare the entry of the module module_exit( zombotany_blkdev_exit);// Declare the exit of the module