文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Linux内核中的块设备驱动

2024-11-30 13:58

关注

在现代计算机系统中,块设备驱动程序是操作系统内核中一个重要的组成部分,它用于管理硬盘、闪存等存储设备。Linux内核是一个开源、自由的操作系统内核,驱动程序源代码公开可用,可以帮助开发人员更好地了解Linux内核块设备驱动的工作原理。

一、块设备驱动程序的基础概念

1、块设备驱动程序的作用

块设备驱动程序是一种负责管理块设备(如硬盘、NVMe快闪存储器等)的软件组件,它负责实现块设备的读写操作、磁盘缓存的管理等。块设备驱动程序使得操作系统内核和各种应用程序都可以通过标准的接口访问块设备。

2、Linux内核块设备驱动程序中的主要数据结构

在Linux内核中,块设备驱动程序主要包含以下数据结构:

(1)bio

I/O操作描述符(I/O descriptor,简称bio)是Linux内核中块设备驱动程序中最基本的数据结构。它描述了一个块设备操作的所有细节和参数,包括读写操作的块数、读写的数据指针、物理地址、缓冲区大小、操作类型等。bio数据结构的重要性在于它是向块设备发送I/O操作的载体。

(2)request_queue

request_queue是块设备驱动程序中的另一个重要的数据结构,它管理着一组bio数据结构。request_queue中可以管理多个bio请求,并且可以高效地组织和处理这些请求。当一个新的I/O请求到来时,request_queue会将其和之前未完成的请求进行合并,以提高I/O操作的效率。

(3)gendisk设备结构

gendisk设备结构是Linux内核中块设备驱动程序的子结构之一,它是块设备驱动程序和块设备层之间的接口数据结构。每个块设备(如硬盘、固态硬盘等)都对应着一个gendisk设备结构,以便于被块设备驱动程序和块设备层管理和访问。gendisk设备结构中包括了块设备的主要属性,例如块大小、可访问的扇区数、分区信息、分块信息等。

3、Linux内核块设备驱动程序的主要工作流程

Linux内核中块设备驱动程序的主要工作流程如下:

(1)初始化块设备驱动程序

在Linux内核中,块设备驱动程序的初始化通常是在模块加载时完成的(即init函数中完成初始化)。块设备驱动程序的初始化包括注册块设备驱动程序、创建gendisk设备结构以及建立request_queue等。

(2)接受并处理I/O请求

块设备驱动程序通常是被块设备层调用的,以提供块设备的读写服务,块设备层会将I/O请求通过request_queue发送给块设备驱动程序,块设备驱动程序会在这里接收到并处理I/O请求。

(3)处理I/O请求

块设备驱动程序主要实现I/O请求的处理,其处理流程通常包括以下几个步骤:

(4)释放块设备驱动程序资源

在Linux内核中,块设备驱动程序资源的释放实际上是由模块卸载时完成的(即exit函数中完成资源释放)。在资源释放时,块设备驱动程序需要注销注册设备、删除gendisk设备结构以及销毁request_queue等。

二、块设备驱动程序源代码分析

1、块设备驱动程序的编写

Linux内核中块设备驱动程序涉及到很多I/O操作,因此需要仔细编写。下面分别简要介绍块设备驱动程序的读、写和I/O请求处理函数的编写方法。

(1)块设备驱动程序的读函数编写

块设备驱动程序的读函数(read函数)通常是异步的,即它不会等待传输完成。当执行一个读请求时,驱动程序中的read函数会创建一个读取请求,并将其添加到request_queue队列中等待处理。一旦请求被添加到request_queue中,驱动程序就会返回给调用者一个代表读请求正在处理的值。

(2)块设备驱动程序的写函数编写

块设备驱动程序的写函数(write函数)与读函数类似,也是异步的,与读函数不同的是它需要等待写操作完成。当执行一个写请求时,驱动程序中的write函数会创建一个写请求,并将其添加到request_queue队列中等待处理。一旦请求被添加到request_queue中,驱动程序就会等待写操作完成后将控制权限返回给调用者。

(3)块设备驱动程序的I/O请求处理函数编写

块设备驱动程序中最重要的函数是I/O请求处理函数,它被用来接收和处理所有接收到的I/O请求。当新的I/O请求到来时,块设备层会将请求通过request_queue发送给块设备驱动程序中的I/O请求处理函数进行处理。

I/O请求处理函数主要包括以下几个步骤:

2、块设备驱动程序源代码

下面为读者介绍Linux内核块设备驱动程序的一个例子(内核版本为4.19.0),该程序负责管理SATA磁盘设备的读写操作。

(1)块设备驱动程序的头文件

#include
#include
#include
#include

(2)块设备驱动程序的声明


static int dev_major = 0;

static int volumes_count = 3;

static int block_size = 512;

static int sector_size = 512;
static int mydrv_open(struct block_device *bdev, fmode_t mode);
static void mydrv_release(struct gendisk *gd, fmode_t mode);
static int mydrv_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg);
static int mydrv_getgeo(struct block_device *bdev, struct hd_geometry *geo);
static struct block_device_operations mydrv_ops = {
.owner = THIS_MODULE,
.open = mydrv_open,
.release = mydrv_release,
.ioctl = mydrv_ioctl,
.getgeo = mydrv_getgeo,
};
static struct request_queue *mydrv_queue = NULL;

static void mydrv_request(struct request_queue *q);

(3)块设备驱动程序的模块加载及卸载函数


static int __init mydrv_init(void)
{
int ret = -1;
struct gendisk *disk = NULL;

dev_major = register_blkdev(dev_major, "mydrv");
if (dev_major <= 0) {
pr_err("mydrv: block device registration failed\n");
goto err_reg;
}

mydrv_queue = blk_alloc_queue(GFP_KERNEL);
if (!mydrv_queue) {
pr_err("mydrv: request queue creation failed\n");
goto err_queue;
}

blk_queue_make_request(mydrv_queue, mydrv_request);

if (!init_volumes(&mydrv_queue, &disk)) {
pr_err("mydrv: volume initialization failed\n");
goto err_vols;
}

if (!add_disk(disk)) { pr_err("mydrv: disk registration failed\n");
goto err_disk;
}

disk->fops = &mydrv_ops;

return 0;

err_disk:
if (disk) {
del_gendisk(disk);
put_disk(disk);
}
err_vols:
blk_cleanup_queue(mydrv_queue);
err_queue:
unregister_blkdev(dev_major, "mydrv");
err_reg:
return ret;
}

static void __exit mydrv_exit(void)
{
unregister_blkdev(dev_major, "mydrv");
blk_cleanup_queue(mydrv_queue);
cleanup_volumes();
}

(4)块设备驱动程序的操作函数

及请求处理函数。

```c



static int mydrv_open(struct block_device *bdev, fmode_t mode)

{

return 0;

}



static void mydrv_release(struct gendisk *gd, fmode_t mode)

{

}



static int mydrv_ioctl(struct block_device *bdev, fmode_t mode,

unsigned int cmd, unsigned long arg)

{

return -ENOTTY;

}



static int mydrv_getgeo(struct block_device *bdev, struct hd_geometry *geo)

{

struct mydrv_volume *vol = NULL;

int ret = -1;



vol = bdev->bd_disk->private_data;

if (!vol) {

pr_err("mydrv: invalid volume information\n");

goto out;

}



geo->heads = vol->heads;

geo->sectors = vol->sectors;

geo->cylinders = vol->cylinders;

ret = 0;

out:

return ret;

}





static void mydrv_request(struct request_queue *q)

{

struct request *req = NULL;

struct bio *bio = NULL;

struct mydrv_volume *vol = NULL;





while ((req = blk_fetch_request(q)) != NULL) {



if (req->cmd_type != REQ_TYPE_FS) {

pr_err("mydrv: wrong request type\n");

__blk_end_request_all(req, -EIO);

continue;

}



__rq_for_each_bio(bio, req) {



vol = bio->bi_bdev->bd_disk->private_data;

if (!vol) {

pr_err("mydrv: invalid volume information\n");

__blk_end_request_all(req, -EIO);

continue;

}



switch (bio_rw(bio)) {

case READ:

mydrv_read(vol, bio);

break;

case WRITE:

mydrv_write(vol, bio);

break;

default:

pr_err("mydrv: wrong I/O operation\n");

__blk_end_request_all(req, -EIO);

break;

}

}



__blk_end_request_all(req, 0);

}

}

三、总结

块设备驱动程序是Linux内核中非常重要的组件之一,它负责处理块设备的读写操作,为操作系统内核和各种应用程序提供标准接口。在块设备驱动程序的编写过程中,需要仔细处理读、写和I/O请求处理函数的实现,以实现块设备的最佳操作效率。

希望大家通过阅读本文,了解和掌握块设备驱动程序的工作原理,进一步提高对Linux内核的理解和认知。

来源:今日头条内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯