当前位置: 首页 > news >正文

Linux 块设备开发学习

Linux 块设备是指可以以固定大小的块(通常为 512 字节或 4KB)进行读写的存储设备。块设备通常用于实现文件系统和管理数据的存储与访问。以下是块设备的一些主要特点和组成部分:

1. 主要特点

  • 随机访问:块设备支持随机读写,可以直接访问任意位置的数据。
  • 块大小:数据以块为单位进行传输,块的大小通常为 512 字节或 4KB。
  • 设备类型:常见的块设备包括硬盘、固态硬盘(SSD)、USB 驱动器等。

2. 组成部分

  • 块设备驱动:负责管理块设备的读取、写入和控制等操作。驱动程序通过系统调用与内核进行交互。
  • 设备节点:在 /dev 目录下的特殊文件,用户空间程序通过这个节点与块设备进行交互。
  • 请求队列:块设备驱动使用请求队列来管理 I/O 请求,确保高效的读写操作。

3. 块设备的工作原理

  • I/O 调度:内核将读写请求排入请求队列,使用 I/O 调度算法(如 CFQ、Deadline 等)优化请求顺序,提高性能。
  • 数据传输:通过 DMA(直接内存访问)技术,块设备可以直接与内存进行数据传输,减轻 CPU 负担。

4. 常用操作

  • 读操作:从块设备读取指定块的数据到内存。
  • 写操作:将内存中的数据写入块设备的指定块。
  • 分区管理:使用工具(如 fdisk)对块设备进行分区,以便创建多个文件系统。

5. 文件系统

块设备通常用来创建文件系统,如 ext4、NTFS、FAT 等,文件系统提供了文件的组织、存储和访问方式。

6. 设备树

在某些平台上,块设备的信息可以通过设备树(Device Tree)来描述,以便在启动时告知内核如何初始化设备。

块设备开发中使用到API介绍:

1. 设备注册与注销

  • register_blkdev

    • 用途:注册一个块设备主设备号。
    • 使用
      int major;
      major = register_blkdev(0, "myblock");  // 0 表示动态分配设备号
      if (major < 0) {printk(KERN_ERR "Failed to register block device\n");
      }
      
  • unregister_blkdev

    • 用途:注销已经注册的块设备主设备号。
    • 使用
      unregister_blkdev(major, "myblock");
      

2. 分配和管理块设备结构

  • alloc_disk

    • 用途:分配 gendisk 结构体。
    • 使用
      struct gendisk *gd;
      gd = alloc_disk(16);  // 分配 16 个从设备号
      if (!gd) {printk(KERN_ERR "Failed to allocate gendisk\n");
      }
      
  • set_capacity

    • 用途:设置设备容量(单位为扇区)。
    • 使用
      set_capacity(gd, DEVICE_SIZE / SECTOR_SIZE);  // DEVICE_SIZE 是总字节数
      
  • add_disk

    • 用途:将 gendisk 结构体添加到系统中。
    • 使用
      add_disk(gd);
      
  • del_gendisk

    • 用途:从系统中删除 gendisk 结构体。
    • 使用
      del_gendisk(gd);
      

3. 设备操作结构

  • block_device_operations
    • 用途:包含与块设备相关的操作函数指针。
    • 使用
      static struct block_device_operations my_fops = {.owner = THIS_MODULE,.open = my_open,.release = my_release,.read = my_read,.write = my_write,
      };
      

4. 读写操作

  • submit_bio
    • 用途:提交一个块 IO 请求。
    • 使用
      struct bio *bio;
      bio = bio_alloc(GFP_KERNEL, 1);  // 分配一个 bio
      bio_set_dev(bio, bdget(dev));     // 设置设备
      bio_add_page(bio, page, size, offset); // 添加数据页
      submit_bio(READ, bio);            // 提交读请求
      

5. 内存管理

  • kmallockfree
    • 用途:动态分配和释放内存。
    • 使用
      device_buffer = kmalloc(DEVICE_SIZE, GFP_KERNEL);
      if (!device_buffer) {printk(KERN_ERR "Failed to allocate memory\n");
      }kfree(device_buffer);  // 释放内存
      

6. 设备节点管理

  • mknod
    • 用途:在 /dev 目录下创建设备节点,通常在用户空间执行。
    • 使用
      sudo mknod /dev/myblock b <major> 0  # <major> 是主设备号
      ``

块设备开发流程

  1. 环境准备

    • 确保你的 Linux 系统上安装了开发工具,例如 gccmakekernel headersbuild-essential
  2. 创建块设备驱动文件

    • 创建一个新的 C 文件,例如 myblock.c,用于实现块设备驱动。
  3. 编写 Makefile

    • 创建一个 Makefile 以便编译驱动程序。
  4. 实现驱动功能

    • 编写驱动的主要功能,如打开、关闭、读写等。
  5. 编译驱动

    • 使用 make 命令编译驱动,生成模块文件(.ko)。
  6. 加载模块

    • 使用 insmod 命令加载块设备驱动模块。
  7. 创建设备节点

    • 使用 mknod 命令在 /dev 目录下创建设备文件。
  8. 测试驱动

    • 使用工具(如 ddfdiskmount)进行读写测试。
  9. 卸载模块

    • 使用 rmmod 卸载驱动。

示例代码

以下是一个简单的块设备驱动示例,包含基本的功能:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/bio.h>
#include <linux/blkdev.h>
#include <linux/slab.h>#define DEVICE_NAME "myblock"
#define SECTOR_SIZE 512
#define DEVICE_SIZE (SECTOR_SIZE * 1024) // 512KBstatic int major;
static char *device_buffer;static int my_open(struct block_device *bdev, fmode_t mode) {return 0;
}static void my_release(struct gendisk *gd, fmode_t mode) {
}static int my_read(struct block_device *bdev, sector_t sector, unsigned long nsectors, char *buffer) {memcpy(buffer, device_buffer + sector * SECTOR_SIZE, nsectors * SECTOR_SIZE);return nsectors;
}static int my_write(struct block_device *bdev, sector_t sector, unsigned long nsectors, const char *buffer) {memcpy(device_buffer + sector * SECTOR_SIZE, buffer, nsectors * SECTOR_SIZE);return nsectors;
}static struct block_device_operations my_fops = {.owner = THIS_MODULE,.open = my_open,.release = my_release,
};static int __init mymodule_init(void) {major = register_blkdev(0, DEVICE_NAME);if (major < 0) return major;device_buffer = kmalloc(DEVICE_SIZE, GFP_KERNEL);if (!device_buffer) {unregister_blkdev(major, DEVICE_NAME);return -ENOMEM;}memset(device_buffer, 0, DEVICE_SIZE);struct gendisk *gd = alloc_disk(16);if (!gd) {kfree(device_buffer);unregister_blkdev(major, DEVICE_NAME);return -ENOMEM;}gd->major = major;gd->first_minor = 0;gd->fops = &my_fops;sprintf(gd->disk_name, DEVICE_NAME);set_capacity(gd, DEVICE_SIZE / SECTOR_SIZE);add_disk(gd);return 0;
}static void __exit mymodule_exit(void) {del_gendisk(gd);unregister_blkdev(major, DEVICE_NAME);kfree(device_buffer);
}module_init(mymodule_init);
module_exit(mymodule_exit);
MODULE_LICENSE("GPL");

Makefile 示例

obj-m += myblock.oall:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modulesclean:make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

测试步骤

  1. 编译驱动

    make
    
  2. 加载模块

    sudo insmod myblock.ko
    
  3. 创建设备节点

    sudo mknod /dev/myblock b <major> 0  # <major> 是驱动的主设备号
    
  4. 测试读写

    echo "Hello, World!" | sudo dd of=/dev/myblock bs=512 count=1
    sudo dd if=/dev/myblock of=testfile bs=512 count=1
    
  5. 卸载模块

    sudo rmmod myblock
    

下面是一个简单的测试应用程序,用于测试 Linux 块设备驱动的读写功能,以及预期的输出结果。

测试应用程序

创建一个名为 test_myblock.c 的文件,包含以下代码:

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>#define DEVICE "/dev/myblock"
#define BUFFER_SIZE 512int main() {int fd;char write_buffer[BUFFER_SIZE] = "Hello, Linux Block Device!";char read_buffer[BUFFER_SIZE];// 打开设备文件fd = open(DEVICE, O_RDWR);if (fd < 0) {perror("Failed to open device");return -1;}// 写入数据if (write(fd, write_buffer, BUFFER_SIZE) < 0) {perror("Failed to write to device");close(fd);return -1;}// 读取数据lseek(fd, 0, SEEK_SET);  // 重置文件指针if (read(fd, read_buffer, BUFFER_SIZE) < 0) {perror("Failed to read from device");close(fd);return -1;}// 输出读取的数据printf("Read from device: %s\n", read_buffer);// 关闭设备close(fd);return 0;
}

编译测试应用程序

在终端中运行以下命令编译测试程序:

gcc test_myblock.c -o test_myblock

测试步骤

  1. 编译并加载块设备驱动
    确保块设备驱动已经编译并加载到内核中。

  2. 创建设备节点

    sudo mknod /dev/myblock b <major> 0  # <major> 是块设备的主设备号
    
  3. 编译测试应用程序

    gcc test_myblock.c -o test_myblock
    
  4. 运行测试程序

    sudo ./test_myblock
    

预期输出结果

如果块设备驱动正常工作,测试程序将输出:

Read from device: Hello, Linux Block Device!
以下是关于 Linux 块设备开发的总结:

块设备开发总结

块设备开发是一个复杂但重要的任务,涉及到系统的存储管理。掌握相关知识和技术,可以有效地实现和优化块设备驱动,提高系统的性能和稳定性。

相关文章:

  • 8个前端小程序开发框架的介绍
  • 【JAVA开源】基于Vue和SpringBoot的足球俱乐部管理后台
  • 记一次停车场后台管理系统漏洞挖掘
  • 让具身智能更快更强!华东师大上大提出TinyVLA:高效视觉-语言-动作模型,遥遥领先
  • 小麦生长状态检测系统源码分享
  • 第十章 【后端】商品分类管理微服务 > 分类列表查询接口(10.8.3)——MyBatis-Plus 逻辑删除
  • Ansible 剧本的执行
  • 设备管理系统-TPM(PC+APP/PDA全流程)高保真Axure原型 源文件分享
  • 【二十七】【QT开发应用】VS如何复制项目,QT无边窗窗口Pro版本,信号与信号槽的应用,背景图片自适应控件大小
  • 文件上传、amrkdown编辑器
  • 第十章 【后端】商品分类管理微服务 > 分类列表查询接口(10.8.2)——MyBatis-Plus 自动填充
  • Qualitor processVariavel.php 未授权命令注入漏洞复现(CVE-2023-47253)
  • 从0开始深度学习(6)——Pytorch动态图机制(前向传播、反向传播)
  • Windows环境下Node.js多版本切换的实用指南
  • CS61b学习 part 2
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • 【跃迁之路】【444天】程序员高效学习方法论探索系列(实验阶段201-2018.04.25)...
  • 【跃迁之路】【733天】程序员高效学习方法论探索系列(实验阶段490-2019.2.23)...
  • 〔开发系列〕一次关于小程序开发的深度总结
  • 2017-08-04 前端日报
  • exif信息对照
  • GraphQL学习过程应该是这样的
  • HashMap剖析之内部结构
  • IDEA 插件开发入门教程
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • linux学习笔记
  • mysql 5.6 原生Online DDL解析
  • Netty源码解析1-Buffer
  • nfs客户端进程变D,延伸linux的lock
  • PaddlePaddle-GitHub的正确打开姿势
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 模型微调
  • 你真的知道 == 和 equals 的区别吗?
  • 使用前端开发工具包WijmoJS - 创建自定义DropDownTree控件(包含源代码)
  • 一份游戏开发学习路线
  • 在 Chrome DevTools 中调试 JavaScript 入门
  • 《天龙八部3D》Unity技术方案揭秘
  • FaaS 的简单实践
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​人工智能书单(数学基础篇)
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • # linux从入门到精通(三)
  • #1015 : KMP算法
  • #if等命令的学习
  • (09)Hive——CTE 公共表达式
  • (1)Hilt的基本概念和使用
  • (1/2) 为了理解 UWP 的启动流程,我从零开始创建了一个 UWP 程序
  • (23)Linux的软硬连接
  • (7) cmake 编译C++程序(二)
  • (BAT向)Java岗常问高频面试汇总:MyBatis 微服务 Spring 分布式 MySQL等(1)
  • (附源码)ssm本科教学合格评估管理系统 毕业设计 180916
  • (附源码)ssm考试题库管理系统 毕业设计 069043
  • (机器学习-深度学习快速入门)第一章第一节:Python环境和数据分析
  • (转)甲方乙方——赵民谈找工作
  • .net CHARTING图表控件下载地址