Linux 内核中RAID5源码详解之守护进程raid5d


对于一个人,大脑支配着他的一举一动;对于一支部队,指挥中心控制着它的所有活动;同样,对于内核中的RAID5,也需要一个像大脑一样的东西来支配着他的正确运转,那就是RAID5的守护进程raid5d。今天,我们就好好来看看raid5d到底是怎么一回事~


进程的注册

前面的博文中贴出的源码经常会出现这样一条语句md_wakeup_thread(mddev->thread);,这条语句是干嘛的呢?就是唤醒守护进程的。那么,如何让内存感知这个进程的存在呢?这时需要对进程进行注册。

在raid5.c中的set_conf() 中(这时配置RAID5的全局信息的函数),有这样一句conf->thread = md_register_thread(raid5d, mddev, pers_name); ,这就是将RAID5的守护进程raid5d注册到内核中,让内核识别这个进程。追踪md_register_thread() :

struct md_thread *md_register_thread(void (*run) (struct md_thread *),struct mddev *mddev, const char *name)
{struct md_thread *thread;thread = kzalloc(sizeof(struct md_thread), GFP_KERNEL);/*为进程开辟内存空间*/if (!thread)return NULL;init_waitqueue_head(&thread->wqueue);//初始化进程等待队列thread->run = run;//设置进程的执行函数thread->mddev = mddev;thread->timeout = MAX_SCHEDULE_TIMEOUT;//这时进程的超时机制thread->tsk = kthread_run(md_thread, thread,"%s_%s",mdname(thread->mddev),name);//设置运行信息if (IS_ERR(thread->tsk)) {//运行出错时的反应kfree(thread);return NULL;}return thread;
}
EXPORT_SYMBOL(md_register_thread);

结合上述注释,可以清楚的发现当唤醒这个进程时,执行的是raid5d这个函数,真正的主体在raid5d里,好吧,现在是时候揭开它神秘的面纱了,gogogo!!!


进程执行函数—raid5d

在raid5.c中搜索raid5d的代码:

/** This is our raid5 kernel thread.** We scan the hash table for stripes which can be handled now.* During the scan, completed stripes are saved for us by the interrupt* handler, so that they will not have to wait for our next wakeup.*/
static void raid5d(struct md_thread *thread)
{struct mddev *mddev = thread->mddev;struct r5conf *conf = mddev->private;int handled;struct blk_plug plug;pr_debug("+++ raid5d active\n");md_check_recovery(mddev);//检查RAID5同步blk_start_plug(&plug);handled = 0;spin_lock_irq(&conf->device_lock);while (1) {//^_^死循环哦struct bio *bio;int batch_size, released;released = release_stripe_list(conf, conf->temp_inactive_list);if (!list_empty(&conf->bitmap_list)) {//激活bitmap处理/* Now is a good time to flush some bitmap updates */conf->seq_flush++;spin_unlock_irq(&conf->device_lock);bitmap_unplug(mddev->bitmap);spin_lock_irq(&conf->device_lock);conf->seq_write = conf->seq_flush;activate_bit_delay(conf, conf->temp_inactive_list);}raid5_activate_delayed(conf);//激活延迟处理装置while ((bio = remove_bio_from_retry(conf))) {//有关重试读的操作int ok;spin_unlock_irq(&conf->device_lock);ok = retry_aligned_read(conf, bio);spin_lock_irq(&conf->device_lock);if (!ok)break;handled++;}batch_size = handle_active_stripes(conf, ANY_GROUP, NULL,conf->temp_inactive_list);//处理stripe_head的主战场,返回处理的个数if (!batch_size && !released)break;handled += batch_size;if (mddev->flags & ~(1<<MD_CHANGE_PENDING)) {spin_unlock_irq(&conf->device_lock);md_check_recovery(mddev);spin_lock_irq(&conf->device_lock);}}pr_debug("%d stripes handled\n", handled);spin_unlock_irq(&conf->device_lock);async_tx_issue_pending_all();blk_finish_plug(&plug);pr_debug("--- raid5d inactive\n");
}

这里我们只介绍一些流程,并不对某些操作具体讲解,因为像同步或者处理条带这些操作很复杂,后面会详细的介绍,今天,我们只做一个流程规划。
其实raid5d只做了这几件事:检查同步、处理temp_inactive_list、激活bitmap处理、激活延迟处理、重试读和处理条带。下面一一介绍这些功能:

  • 检查同步:由于下层的磁盘设备会发生故障或者产生损坏致使数据错误,而RAID5独有的parity机制则对数据的安全性有了很可靠的保障。检查同步就是按stripe_head为单元,一条条读取磁盘上的数据,然后计算新parity与原先的parity进行比较,如果相同则数据正确,否则数据产生错误,需要修复。
  • 处理temp_inactive_list:跟进该函数,发现调用的是__release_stripe(),我的前一篇博文里面有提及这个函数,具体点击这里,在此就不赘述了。
  • 激活bitmap处理:bitmap,前面没接触过的话,会觉得这个名词很陌生,其实它的存在是为了保证RAID5甚至整个MD模块运行的可靠性。打个比方说:RAID5在写1MB的数据,然而此时断电了,那么内存数据丢失,而且也不知道此时磁盘写了多大的数据,那么怎么恢复这个缺陷呢?于是bitmap出现了,它将每次写请求做一次类似log的形式保存在bitmap_list中,直到写请求已经全部完成后才将bitmap_list中的请求删去,比如说再遇到断电的情况,bitmap中保存着这次请求,下次可以直接恢复。
    所以说bitmap的存在是为了系统的可靠性考虑。
  • 激活延迟处理:该性能由raid5_activate_delayed() 实现,跟进函数:
static void raid5_activate_delayed(struct r5conf *conf)
{if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) {while (!list_empty(&conf->delayed_list)) {struct list_head *l = conf->delayed_list.next;//延迟链表struct stripe_head *sh;sh = list_entry(l, struct stripe_head, lru);list_del_init(l);//从list中删除clear_bit(STRIPE_DELAYED, &sh->state);//清楚延迟处理标志if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))atomic_inc(&conf->preread_active_stripes);list_add_tail(&sh->lru, &conf->hold_list);//加入到hold_listraid5_wakeup_stripe_thread(sh);}}
}

还记得Linux 内核中RAID5源码详解之stripe_head的管理 中提到的delayed_list和hold_list之间的转化吗?就是在这里实现的。

  • 重试读:这部分是为了提高读的性能,后面会有介绍。
  • 处理条带:这是我们最关心的功能,也是stripe_head处理的入口,跟进handle_active_stripes()
static int handle_active_stripes(struct r5conf *conf, int group,struct r5worker *worker,struct list_head *temp_inactive_list)
{struct stripe_head *batch[MAX_STRIPE_BATCH], *sh;int i, batch_size = 0, hash;bool release_inactive = false;while (batch_size < MAX_STRIPE_BATCH &&(sh = __get_priority_stripe(conf, group)) != NULL)/*默认的MAX_STRIPE_BATCH为8,即一次最多取8个stripe_head处理*/batch[batch_size++] = sh;if (batch_size == 0) {for (i = 0; i < NR_STRIPE_HASH_LOCKS; i++)if (!list_empty(temp_inactive_list + i))break;if (i == NR_STRIPE_HASH_LOCKS)return batch_size;release_inactive = true;}spin_unlock_irq(&conf->device_lock);release_inactive_stripe_list(conf, temp_inactive_list,NR_STRIPE_HASH_LOCKS);if (release_inactive) {spin_lock_irq(&conf->device_lock);return 0;}for (i = 0; i < batch_size; i++)handle_stripe(batch[i]);//处理条带的主战场cond_resched();spin_lock_irq(&conf->device_lock);for (i = 0; i < batch_size; i++) {hash = batch[i]->hash_lock_index;__release_stripe(conf, batch[i], &temp_inactive_list[hash]);//回收条带}return batch_size;
}

首先,RAID5是进行批量处理条带的,每次处理MAX_STRIPE_BATCH个条带,默认值为8,在取条带时也有规则,跟进__get_priority_stripe()

/* __get_priority_stripe - get the next stripe to process** Full stripe writes are allowed to pass preread active stripes up until* the bypass_threshold is exceeded.  In general the bypass_count* increments when the handle_list is handled before the hold_list; however, it* will not be incremented when STRIPE_IO_STARTED is sampled set signifying a* stripe with in flight i/o.  The bypass_count will be reset when the* head of the hold_list has changed, i.e. the head was promoted to the* handle_list.*/
static struct stripe_head *__get_priority_stripe(struct r5conf *conf, int group)
{struct stripe_head *sh = NULL, *tmp;struct list_head *handle_list = NULL;struct r5worker_group *wg = NULL;if (conf->worker_cnt_per_group == 0) {//确定handle_listhandle_list = &conf->handle_list;} else if (group != ANY_GROUP) {handle_list = &conf->worker_groups[group].handle_list;wg = &conf->worker_groups[group];} else {int i;for (i = 0; i < conf->group_cnt; i++) {handle_list = &conf->worker_groups[i].handle_list;wg = &conf->worker_groups[i];if (!list_empty(handle_list))break;}}pr_debug("%s: handle: %s hold: %s full_writes: %d bypass_count: %d\n",__func__,list_empty(handle_list) ? "empty" : "busy",list_empty(&conf->hold_list) ? "empty" : "busy",atomic_read(&conf->pending_full_writes), conf->bypass_count);if (!list_empty(handle_list)) {//handle_list不为空,则从中取条带sh = list_entry(handle_list->next, typeof(*sh), lru);if (list_empty(&conf->hold_list))conf->bypass_count = 0;else if (!test_bit(STRIPE_IO_STARTED, &sh->state)) {if (conf->hold_list.next == conf->last_hold)conf->bypass_count++;else {conf->last_hold = conf->hold_list.next;conf->bypass_count -= conf->bypass_threshold;if (conf->bypass_count < 0)conf->bypass_count = 0;}}} else if (!list_empty(&conf->hold_list) &&((conf->bypass_threshold &&conf->bypass_count > conf->bypass_threshold) ||atomic_read(&conf->pending_full_writes) == 0)) {//否则从hold_list中取list_for_each_entry(tmp, &conf->hold_list,  lru) {if (conf->worker_cnt_per_group == 0 ||group == ANY_GROUP ||!cpu_online(tmp->cpu) ||cpu_to_group(tmp->cpu) == group) {sh = tmp;break;}}if (sh) {conf->bypass_count -= conf->bypass_threshold;if (conf->bypass_count < 0)conf->bypass_count = 0;}wg = NULL;}if (!sh)return NULL;if (wg) {wg->stripes_cnt--;sh->group = NULL;}list_del_init(&sh->lru);BUG_ON(atomic_inc_return(&sh->count) != 1);return sh;
}

根据代码我们可以看出,handle_list中条带的优先级高于hold_list中的条带优先级,而且函数的注释中已经明确表明了bypass_count的赋值情况。


回到handle_active_stripes() 中,取到条带后,调用handle_stripe() 进行处理,这个函数可厉害了,而且情况也特别复杂,我们这里不做讨论,后面会专门来讲解这个函数。处理完成后,调用__release_stripe() 对条带进行回收,我的前一篇博文里面有提及这个函数,具体点击这里,在此就不赘述了。
至此,raid5d所干的事已经昭告于天下了,也不是很复杂的哦,毕竟它只是个指挥官,真正的提枪上阵还得真正的士兵。后面会对某些操作进行具体的讲解,别急哦~


进程的注销

RAID5的守护进程raid5d的注销是通过调用md_unregister_thread() 函数来实现的,跟进md_unregister_thread()

void md_unregister_thread(struct md_thread **threadp)
{struct md_thread *thread = *threadp;if (!thread)return;pr_debug("interrupting MD-thread pid %d\n", task_pid_nr(thread->tsk));/* Locking ensures that mddev_unlock does not wake_up a* non-existent thread*/spin_lock(&pers_lock);*threadp = NULL;spin_unlock(&pers_lock);kthread_stop(thread->tsk);//停止这个进程kfree(thread);
}
EXPORT_SYMBOL(md_unregister_thread);

很简单,只是调用一下kthread_stop() 来停止下raid5d。


有关RAID5的守护进程raid5d的一些基本功能讲得差不多了,从注册到注销,正应了那句话,善始善终,good luck~

Linux 内核中RAID5源码详解之守护进程raid5d相关推荐

  1. linux内核中的hook函数详解,linux内核中的hook函数详解

    在编写linux内核中的网络模块时,用到了钩子函数也就是hook函数.现在来看看linux是如何实现hook函数的. 先介绍一个结构体: struct nf_hook_ops,这个结构体是实现钩子函数 ...

  2. linux hook 任意内核函数,linux内核中的hook函数详解

    在编写linux内核中的网络模块时,用到了钩子函数也就是hook函数.现在来看看linux是如何实现hook函数的. 先介绍一个结构体: struct nf_hook_ops,这个结构体是实现钩子函数 ...

  3. linux内核kconfig objs,linux内核中Kconfig和Makefile 详解

    内核源码树的目录下都有两个文档 Kconfig (2.4版本是Config.in)和Makefile.分布到各目录的Kconfig构成了一个分布式的内核配置数据库,每个Kconfig分别描述了所属目录 ...

  4. Linux内核中的延时函数详解

    内核中涉及的延时主要有两种实现方式:忙等待或者睡眠等待.前者阻塞程序,在延时时间到达前一直占用CPU,而后者是将进程挂起(置进程于睡眠状态并释放CPU资源).所以,前者一般用在延时时间在毫秒以内的精确 ...

  5. Tensorflow 2.x(keras)源码详解之第十二章:keras中的损失函数之BinaryCrossentropy详解

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  6. Tensorflow 2.x(keras)源码详解之第七章:keras中的tf.keras.layers

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  7. Tensorflow 2.x(keras)源码详解之第十章:keras中的模型保存与加载(详解Checkpointmd5模型序列化)

      大家好,我是爱编程的喵喵.双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中.从事机器学习以及相关的前后端开发工作.曾在阿里云.科大讯飞.CCF等比赛获得多次Top名次.现 ...

  8. (十三)Java工具类StringUtils中strip、stripStart、stripEnd剥离方法源码详解

    1. strip方法源码解析 public static String strip(String str){return strip(str, null);} 源码解析:调用strip方法,参数是字符 ...

  9. 源码详解Android 9.0(P) 系统启动流程之SystemServer

    源码详解Android 9.0(P) 系统启动流程目录: 源码详解Android 9.0(P)系统启动流程之init进程(第一阶段) 源码详解Android 9.0(P)系统启动流程之init进程(第 ...

最新文章

  1. Apache的Commons Lang和BeanUtils
  2. Java 8 获取某天最大(23:59:59)最小(00:00:00)时间
  3. 最近开机老是弹出网银插件的问题
  4. leetcode 241. Different Ways to Add Parentheses | 241. 为运算表达式设计优先级(Java)
  5. 如何将Wii遥控器用作陀螺仪鼠标
  6. mysql datetime 间隔,MySQL datetime默认时间间隔
  7. mysql like 多个条件_千万级MySQL数据库这样建索引可以让你的数据库飞起来.........
  8. surface php老是用不了,surface pro7触摸屏没反应怎么办
  9. (Incomplete) Codeforces #395 (Div 1 + Div 2)
  10. 输出星期名缩写python_python练习题5.1输出星期名缩写
  11. 服务器带宽打开网页很慢,网速快但是打开网页慢是怎么回事 浏览器打开网页慢的解决办法汇总...
  12. wordpress启动_使用Wumblr在WordPress中启动微博
  13. Win键失效解决方案+键盘检测器
  14. 福特汉姆大学计算机科学专业,福特汉姆大学优势专业
  15. MATLAB 图像嵌入水印图像程序
  16. 老大难的GC原理及调优,这下全说清楚了
  17. 输入多组字符数组c语言,c语言怎样能连续输入多个一维数组
  18. 云计算机服务英文翻译,基于云的服务,Cloud Based Service,音标,读音,翻译,英文例句,英语词典...
  19. Mock.js有什么用
  20. 服务器snb芯片组,技嘉发布三款SNB Xeon单路服务器主板

热门文章

  1. CAD图如何导入Visio
  2. 如何使用CE来修改游戏并制作一个修改器
  3. 网络安全兼职注意事项
  4. JAVA毕业设计装修设计管理系统设计与实现计算机源码+lw文档+系统+调试部署+数据库
  5. 郑州大学计算机系好请假吗,郑州大学生最让人心碎的请假条:请假期限永远,无法按时返校!...
  6. 大厂对学历的要求是什么?如果学历不够,拿什么来凑?
  7. 其他计算机设备是什么,电脑设备指什么
  8. 一起来玩U3D之基础物理引擎
  9. creating output section “HRCap1RegsFile“ without a SECTIONS
  10. 《东周列国志》第五十二回 公子宋尝鼋构逆 陈灵公衵服戏朝