一个IO的传奇一生(14)—— Linux中的MD开源RAID(2)

续《一个IO的传奇一生(13)—— Linux中的MD开源RAID(1)》

4.6  make_request函数说明

函数原型:static int make_request (request_queue_t *q, struct bio * bi)

参数:*q,请求队列

*bi,IO请求数据结构

各个RAID Level的IO请求函数相同,但是他们的实现是不一样的。RAID1中make_request()函数的主要功能是将上层的bio分发到底层驱动中去,但是,RAID5中的函数并没有实现这样的功能,其主要实现的功能如下:

1.       通过raid5_compute_sector()函数得到逻辑块号所对应的实际物理块号,另外还得到了RAID磁盘阵列中所对应的数据盘索引和校验盘索引。

2.       通过get_active_stripe()函数得到一个stripe,如果在stripe的hash表中无法找到sector对应的条带,那么就从inactive_list中分配一个stripe,如果没有多余的条带,那么整个操作无法进行。如果能够得到一个active的stripe,那么将输入的bio直接挂接到active_stripe上。

3.       调用handle_stripe()函数实现真正的IO读写请求操作。

从上面的分析可以看出,RAID5的make_request()函数实际上实现了条带(stripe)的查找/申请和bio请求数据结构的挂接事情。真正的读写操作由handle_stripe()实现。

4.7  sync_request函数说明

Sync_request()这个函数是RAID5的同步处理函数。

该函数注册到md的mdk_personality_s结构体下的sync_request中。

因此,在md_do_sync()函数中可以采用如下方法来调用RAID5的同步处理函数:

mddev->pers->sync_request(mddev, j, currspeed < sysctl_speed_limit_min);

在分析md_check_recovery()这个函数的时候,我们可以看到,当需要做数据同步或者数据恢复的时候,md_check_recovery()是需要调用md_do_sync()过程的。

函数原型:static int sync_request (mddev_t *mddev, sector_t sector_nr, int go_faster)

输入参数:   *mddev,md设备

Sector_nr,起始扇区

go_faster,需不需要延迟操作

返回值:这一次完成的扇区数目

4.8  raid5d函数说明

这是RAID5的守护线程,该函数在RAID5初始化的时候被注册:

mddev->thread = md_register_thread(raid5d, mddev, "%s_raid5");

在守护线程运行的一开始会调用md_check_recovery(),通过该函数来检查存储是否有故障,如果有故障,那么调用md_do_sync()函数。md_do_sync()函数实际上是不会完成具体同步工作的,它会调用相应级别的RAID同步处理函数sync_request()去实现具体功能。

Raid5d()守护线程是一个while(1)的死循环,他的退出条件是:list_empty(&conf->handle_list)。即当handle_list为空的情况下,raid5d退出睡眠。

Raid5d()守护线程从handle_list中得到active stripe,然后调用handle_stripe()函数对该stripe进行处理。

Handle_stripe()处理完之后,该stripe又被挂接到不活动的list(inactive list)上。

4.9  两个IO读写回调函数说明

RAID5中有两个请求结束回调函数,他们为:

1、raid5_end_read_request()

2、raid5_end_write_request()

这两个回调函数在generic_make_request()的时候被注册到bio中。

Raid5_end_read_request()函数实现如下功能:

u  清除IO请求标志:R5_LOCKED

u  如果数据有效(update),那么设置数据有效标记:R5_UPTODATE

u  如果数据读写错误,那么调用md_error(),需要recovery。

u  设置stripe的handle_list标记,STRIPE_HANDLE,说明要让Raid5d()调用handle_stripe进行处理

Raid5_end_write_request()函数实现如下功能:

u  清除IO请求标记:R5_LOCKED

u  如果写发生错误(uptodate == 0),那么调用md_error(),需要recovery。

u  设置stripe的标记STRIPE_HANDLE,说明要让raid5d()调用handle_stripe()进行处理。

4.10 出错函数error说明

error()函数是RAID5出错处理函数,其被注册到mdk_personality_t的error_handler函数上,所以在MD驱动程序中当出现IO读写错误的时候,直接调用md_error()函数即可。

在raid5.c文件中,有两个地方调度md_error(),他们是raid_end_read_request()和raid_end_write_request()这两个回调函数。当读写I/O错误的时候,回调函数就会调用md_error(),然后设置相应的标志位,进行recovery操作。

函数原型:static void error(mddev_t *mddev, mdk_rdev_t *rdev)

参数: *mddev,md设备的数据结构

*rdev,具体出错的设备

4.11  list链表处理函数release_stripe说明

release_stripe()函数封装了_release_stripe()。因此,讨论_release_stripe()。

函数原型:static inline void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)

参数:*conf,RAID5私有数据结构体

*sh,需要处理的stripe

该函数实现了handle_list,delayed_list和inactive_list链表之间的转换关系处理。

首先需要讲一下几个重要的状态标记:

1、              STRIPE_PREREAD_ACTIVE:该标记为预读标记,当RAID5 进行IO写的时候需要进行该标记的判断,即在读的第一阶段需要判断该标记。实际上根据字面意思也知道,在写操作的时候需要一个pre_read过程,即读操作的第一个步骤。

2、              STRIPE_DELAYED:该标记为延迟处理标记,该标记有效时,release_stripe()函数会将list挂接到delayed_list中。

从上面的分析中,我们可以看到写操作的第一个步骤是一个pre_read的过程,并且是一个延迟操作的过程。延迟操作往往需要等到handle_list中的stripe处理完成之后,再从delayed_list挂接到handle_stripe中。因为在写操作的第一阶段需要置Wantread标记,调度一个读操作,那么STRIPE_PREREAD_ACTIVE标记必须有效。而该标记的设置在raid5_activate_delayed()函数中实现。该函数的调用又需要等到handle_list为空(raid5d()中实现)。这个过程可以描述成如下流程:

Release_stripe()函数执行过程:

1、  当STRIPE_HANDLE标记有效的时候,可以将stripe挂接到handle_list或者delayed_list上,否则这个stripe将会被挂接到inactive_list上。

2、  当STRIPE_DELAYED标记有效的时候,stripe将会被挂接到delayed_list上,实现一个延迟处理。否则,stripe将会被挂接到handle_list上。

3、  挂接到handle_list或者delayed_list上之后,调用md_wakeup_thread(conf->mddev->thread)函数唤醒守护进程。

5、RAID5 中I/O读写方法

RAID5中I/O的读写操作由make_request发起,该函数被注册到mdk_personality_s结构的make_request函数中,当操作系统调用make_request_fn函数进行块设备读写操作的时候,直接调用make_request()函数实现相应功能。

在RAID1的make_request函数中直接将上层的bio分发下去,实现IO读写操作,但是在RAID5中的实现方法有所不同,其调用了handle_stripe函数实现读写操作。

RAID5中实现IO读写操作的函数主要有:

1、  make_request()。该函数传递上层发送的IO请求

2、  handle_stripe()。实现IO读写请求的主干函数

3、  generic_make_request()。发送IO请求至底层驱动程序

4、  raid5_end_read_request()。读操作结束回调函数

5、  raid5_end_write_request()。写操作结束回调函数

6、  release_stripe()。Sh挂接至handle_list处理函数

7、  raid5d()。守护线程

I/O写操作过程:

写操作过程历经如下函数调用过程:

读取数据过程:

Make_request()-> handle_stripe()->generic_make_request()底层驱动工作…

计算/写数据过程:

Raid5_end_read_request()-> release_stripe()-> raid5d ()-> handle_stripe()底层驱动工作…

结束写过程:

Raid5_end_write_request()->release_stripe()->raid5d()->handle_stripe()

具体的IO写过程参考handle_stripe()写过程分析。

I/O读操作过程:

IO读过程比较简单,其经历的函数调用过程如下:

调度一个读过程:

Make_request()-> handle_stripe()-> generic_make_request()底层驱动工作…

从page缓存中拷贝数据:

Raid5_end_read_request()-> release_stripe()-> raid5d()-> handle_stripe()

具体的分析可以参考handle_stripe()读过程分析。

6、几个list的关系及数据挂接关系

RAID5中涉及的几个list:

1、  handle_list:这个list中的stripe需要分发执行

2、  delayed_list:这个list中的stripe延迟分发执行

3、  inactive_list:这个list中的stripe为不活动的条带

当需要进行一个IO操作的时候,首先要获取一个active stripe(get_active_stripe()函数实现),这个stripe可以从hash表中找到,当找不到的时候,可以从inactive_list中请求一个(get_free_stripe()函数实现)。当handle_stripe()函数将stripe处理完毕之后,release_stripe()函数又将stripe放入inactive_list。

几个list的挂接关系可以基本描述如下:

7、错误处理数据恢复方法

RAID需要进行IO的出错处理。在RAID5这个级别可以纠正由于一个磁盘故障导致的错误,在raid驱动中通过error()函数来报错,然后通过md_check_recovery()函数来检错,通过md_do_sync()、sync_request()函数来纠错。他的运行机制和相互之间的逻辑关系又是怎样的呢?

基本的数据恢复过程如下图所示:

在RAID5系统中,其数据同步/恢复操作分为两个阶段:

1、  数据写阶段,将有效数据写到spare盘上去。

2、  数据校验阶段,确认校验和是否正确,如果正确,那么整个recovery操作才算真正的结束。

<结束>

时间: 2024-12-22 11:46:22

一个IO的传奇一生(14)—— Linux中的MD开源RAID(2)的相关文章

一个IO的传奇一生(13)—— Linux中的MD开源RAID(1)

1.前言 RAID是IO路径中的关键模块,甚至在整个存储系统中,RAID是最为核心和复杂的模块.在Linux操作系统中,提供了一个开源的RAID,那就是MD RAID.该RAID可以实现RAID0.RAID1.RAID5和RAID6的功能,在产业界得到了广泛的应用.MD RAID对外采用块设备的形式导出,因此,通过块设备层的调度,BIO请求会被发送到RAID模块进行进一步的IO处理.在MD RAID一层,请求会被按照条带的方式进行切分,然后再按照RAID中磁盘的个数进行磁盘请求的映射.在请求的处

14.linux中设备的访问

一 设备访问1.设备识别/dev/xdxn       ##硬盘设备/dev/sda1/dev/cdrom      ##光驱/dev/mapper/*   ##虚拟设备 2.设备的使用##<设备的发现>##fdisk -l                ##查看真实存在的设备cat /proc/partitions    ##系统能够识别的设备blkid                   ##系统能够挂载使用的设备iddf                      ##查看设备被系统使用的

linux中的‘make’和‘makefile’

在提及'make'和'makefile'之前有必要先理清楚程序编译的过程,在windows操作系统下,我们一般使用的编写程序的软件有vs.vc等,这些都是集成软件,当编写完程序之后,直接点击进行编译和链接,那么编译器是怎样将程序进行编译的呢? ◆编译过程 (1)预处理阶段 将程序编辑完成之后,在编译之前,编译器会先对程序进行一下预处理,预处理阶段一般的工作是将程序的注释去掉,将头文件在源文件之中进行展开,同时进行宏替换等操作,经过处理之后就会生成一个.i文件. (2)编译阶段 将编译的源程序转换

Linux中查看日志文件的正确姿势,求你别tail走天下了!

作为一个后端开发工程师,在Linux中查看查看文件内容是基本操作了.尤其是通常要分析日志文件排查问题,那么我们应该如何正确打开日志文件呢?对于笔者这种小菜鸡来说,第一反应就是 cat,tail,vi(或vim)了,是的,我曾经用过好多次vim编辑器来查看日志文件. 千万不要使用vi命令来查看大文件内容, 尤其对于那些几十G的大文件.因为vi仅仅是一个编辑器(可以理解为windows中的记事本),使用vi命令后则会把文件所有内容加载到内存中,如果内存不够大的话,则可能会导致服务器瘫痪. 为了生成测

Linux中的文件IO

一.应用框架介绍     1.什么是应用编程 (1)典型的嵌入式产品就是基于嵌入式Linux操作系统来工作的.研发过程:第一步让Linux系统在硬件上跑起来(系统移植工作),第二步基于Linux系统来开发应用程序实现产品功能. (2)基于Linux去做应用编程,其实就是通过调用Linux的系统API来实现应用需要完成的任务. 2.什么是文件的IO 文件的input和output,就是读写文件. 二.文件操作的主要接口API 1.什么是操作系统API (1)API是一些函数,这些函数是有Linux

&lt;实训|第十一天&gt;学习一下linux中的进程,文件查找,文件压缩与IO重定向

[[email protected]~]#序言 在今后的工作中,运维工程师每天的例行事务就是使用free -m,top,uptime,df -h...每天都要检查一下服务器,看看是否出现异常.那么今天我们就讲解一下关于运维工程师例行事务的知识!  开班第十一天: [[email protected]~]#今天的课程大纲 查看进程,中断进程,切换进程 内存与swap分区 linux中文件查找的基本方法 linux中是如何解压缩文件的 关于I/O重定向的知识点 远程scp配合管道 详细讲解: [[e

聊聊 Linux 中的五种 IO 模型

本文转载自: http://mp.weixin.qq.com/s?__biz=MzAxODI5ODMwOA==&mid=2666538919&idx=1&sn=6013c451b5f14bf809aec77dd5df6cff&scene=21#wechat_redirect 上一篇<聊聊同步.异步.阻塞与非阻塞>已经通俗的讲解了,要理解同步.异步.阻塞与非阻塞重要的两个概念点了,没有看过的,建议先看这篇博文理解这两个概念点.在认知上,建立统一的模型.这样,大家在

深入理解JAVA I/O系列六:Linux中的IO模型

IO模型 linux系统IO分为内核准备数据和将数据从内核拷贝到用户空间两个阶段. 这张图大致描述了数据从外部磁盘向运行中程序的内存中移动的过程. 用户空间.内核空间 现在操作系统都是采用虚拟存储器,那么对32位操作系统而言,它的寻址空间(虚拟储存空间)为4G(2的32次方).操作系统的核心是内核,独立于普通的应用程序,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限.为了保证用户进程不能直接操作内核,保证内核的安全,操作系统将虚拟空间划分为两个部分,一个部分为内核空间,一部分为用户空间

Linux中的IO复用接口简介(文件监视?)

I/O复用是Linux中的I/O模型之一.所谓I/O复用,指的是进程预先告诉内核,使得内核一旦发现进程指定的一个或多个I/O条件就绪,就通知进程进行处理,从而不会在单个I/O上导致阻塞. 在Linux中,提供了select.poll.epoll三类接口来实现I/O复用. select函数接口 select中主要就是一个select函数,用于监听指定事件的发生,原型如下: 12345 #include<sys/select.h>#include<sys/time.h>int sele