热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

续《一个IO的传奇一生(13)——Linux中的MD开源RAID(1)》4.6make_request函数说明函数原型:staticintmake_request(request_

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

4.6  make_request函数说明

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

参数:*q,请求队列

           *biIO请求数据结构

 

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

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

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

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

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

 

 

4.7  sync_request函数说明

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

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

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

mddev->pers->sync_request(mddev, j, currspeed

 

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

 

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

输入参数:   *mddevmd设备

                            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中有两个请求结束回调函数,他们为:

1raid5_end_read_request()

2raid5_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_terror_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)

参数: *mddevmd设备的数据结构

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

参数:*confRAID5私有数据结构体

           *sh,需要处理的stripe

 

该函数实现了handle_listdelayed_listinactive_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_threadconf->mddev->thread)函数唤醒守护进程。

 

5RAID5 I/O读写方法

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

 

RAID1make_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 stripeget_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操作才算真正的结束。

 

<结束>

本文出自 “存储之道” 博客,请务必保留此出处http://alanwu.blog.51cto.com/3652632/1549758

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


推荐阅读
  • 本文介绍了一款用于自动化部署 Linux 服务的 Bash 脚本。该脚本不仅涵盖了基本的文件复制和目录创建,还处理了系统服务的配置和启动,确保在多种 Linux 发行版上都能顺利运行。 ... [详细]
  • QUIC协议:快速UDP互联网连接
    QUIC(Quick UDP Internet Connections)是谷歌开发的一种旨在提高网络性能和安全性的传输层协议。它基于UDP,并结合了TLS级别的安全性,提供了更高效、更可靠的互联网通信方式。 ... [详细]
  • 本文介绍如何使用 NSTimer 实现倒计时功能,详细讲解了初始化方法、参数配置以及具体实现步骤。通过示例代码展示如何创建和管理定时器,确保在指定时间间隔内执行特定任务。 ... [详细]
  • 本文介绍了在Windows环境下使用pydoc工具的方法,并详细解释了如何通过命令行和浏览器查看Python内置函数的文档。此外,还提供了关于raw_input和open函数的具体用法和功能说明。 ... [详细]
  • 本文介绍如何使用阿里云的fastjson库解析包含时间戳、IP地址和参数等信息的JSON格式文本,并进行数据处理和保存。 ... [详细]
  • 深入理解OAuth认证机制
    本文介绍了OAuth认证协议的核心概念及其工作原理。OAuth是一种开放标准,旨在为第三方应用提供安全的用户资源访问授权,同时确保用户的账户信息(如用户名和密码)不会暴露给第三方。 ... [详细]
  • 深入理解 Oracle 存储函数:计算员工年收入
    本文介绍如何使用 Oracle 存储函数查询特定员工的年收入。我们将详细解释存储函数的创建过程,并提供完整的代码示例。 ... [详细]
  • CSS 布局:液态三栏混合宽度布局
    本文介绍了如何使用 CSS 实现液态的三栏布局,其中各栏具有不同的宽度设置。通过调整容器和内容区域的属性,可以实现灵活且响应式的网页设计。 ... [详细]
  • 深入理解Cookie与Session会话管理
    本文详细介绍了如何通过HTTP响应和请求处理浏览器的Cookie信息,以及如何创建、设置和管理Cookie。同时探讨了会话跟踪技术中的Session机制,解释其原理及应用场景。 ... [详细]
  • 本文介绍如何通过Windows批处理脚本定期检查并重启Java应用程序,确保其持续稳定运行。脚本每30分钟检查一次,并在需要时重启Java程序。同时,它会将任务结果发送到Redis。 ... [详细]
  • MySQL中枚举类型的所有可能值获取方法
    本文介绍了一种在MySQL数据库中查询枚举(ENUM)类型字段所有可能取值的方法,帮助开发者更好地理解和利用这一数据类型。 ... [详细]
  • 国内BI工具迎战国际巨头Tableau,稳步崛起
    尽管商业智能(BI)工具在中国的普及程度尚不及国际市场,但近年来,随着本土企业的持续创新和市场推广,国内主流BI工具正逐渐崭露头角。面对国际品牌如Tableau的强大竞争,国内BI工具通过不断优化产品和技术,赢得了越来越多用户的认可。 ... [详细]
  • Linux 系统启动故障排除指南:MBR 和 GRUB 问题
    本文详细介绍了 Linux 系统启动过程中常见的 MBR 扇区和 GRUB 引导程序故障及其解决方案,涵盖从备份、模拟故障到恢复的具体步骤。 ... [详细]
  • 本文介绍了如何使用jQuery根据元素的类型(如复选框)和标签名(如段落)来获取DOM对象。这有助于更高效地操作网页中的特定元素。 ... [详细]
  • 本文详细介绍了如何通过命令行启动MySQL服务,包括打开命令提示符窗口、进入MySQL的bin目录、输入正确的连接命令以及注意事项。文中还提供了更多相关命令的资源链接。 ... [详细]
author-avatar
宝宝抱抱你啊
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有