Linux 内核中RAID5源码详解之RAID模块声明
来源:互联网 发布:中国外汇储备数据 编辑:程序博客网 时间:2024/06/02 01:21
Linux 内核中RAID5源码详解之RAID模块声明
系统整体布局
在讲RAID5之前,我们先来思考下这个问题:我们平时写的C函数,比如说write()
是怎么将数据写到计算机的硬盘上的?整个系统指令执行的流程是什么?我们带着这个问题来了解系统的整体布局。
这是系统的整体布局的示意图,比如说问题中的write()
执行流程则为用户态调用该函数,然后进行系统调用,将指令和参数传给虚拟文件系统层,然后再具体的文件系统上执行,文件系统再将指令和参数转换为写请求(包含地址和数据)传给通用设备层,最后通过设备驱动将数据写到物理硬盘上。可是我们会想了,这个问题和我们要讲的RAID5有啥关系呢?别急嘛,好酒要一点点品,同样了解了整个系统的布局再具体到某一点,应该会理解的更好一点哦~
MD及RAID模块
接下来就来看看MD和RAID模块在上述的布局中起到什么作用。首先如果将MD和RAID模块加入到上图中的话,那加到什么位置呢?下图告诉你答案。
从图中可看出,文件系统将请求(写的地址和数据)发送给下层的MD模块,而MD模块则选用一种阵列结构来执行这条指令,于是指令被传送到了RAID上,由于RAID有好几种等级,RAID0,RAID1,RAID5,RAID6,RAID10等等,经过RAID模块时,会将请求中的地址转换为某块盘上的偏移量,同时根据写的数据以及原来的数据计算新的parity(校验位),再将数据和parity写到物理硬盘上。所以MD和RAID模块起到一个承上启下的作用,但是MD和RAID之间有什么联系呢?
其实RAID是MD上的一个子模块,上层将请求传到MD,MD再讲请求发送到具体的子模块上,再通过这些子模块执行具体的请求。也可以看成MD实现了下层设备阵列的抽象。
模块的声明
由于内核将MD和RAID模块化,可是怎么通过代码的形式来识别这些模块呢?首先我们看看RAID5模块的声明(MD模块的声明和这个类似,我们最关注的是RAID模块),代码在raid5.c中(其实包含的是raid4、raid5、raid6的源代码)。
static int __init raid5_init(void){ raid5_wq = alloc_workqueue("raid5wq", WQ_UNBOUND|WQ_MEM_RECLAIM|WQ_CPU_INTENSIVE|WQ_SYSFS, 0); if (!raid5_wq) return -ENOMEM; register_md_personality(&raid6_personality); register_md_personality(&raid5_personality); register_md_personality(&raid4_personality); return 0;}static void raid5_exit(void){ unregister_md_personality(&raid6_personality); unregister_md_personality(&raid5_personality); unregister_md_personality(&raid4_personality); destroy_workqueue(raid5_wq);}module_init(raid5_init);module_exit(raid5_exit);MODULE_LICENSE("GPL");MODULE_DESCRIPTION("RAID4/5/6 (striping with parity) personality for MD");MODULE_ALIAS("md-personality-4"); /* RAID5 */MODULE_ALIAS("md-raid5");MODULE_ALIAS("md-raid4");MODULE_ALIAS("md-level-5");MODULE_ALIAS("md-level-4");MODULE_ALIAS("md-personality-8"); /* RAID6 */MODULE_ALIAS("md-raid6");MODULE_ALIAS("md-level-6");/* This used to be two separate modules, they were: */MODULE_ALIAS("raid5");MODULE_ALIAS("raid6");
结合下面语句的解释看,结果一目了然啦。
Module_init(raid5_init);//模块的初始化
Module_exit(raid5_exit);//模块的反初始化
MODULE_LICENSE(“GPL”);//模块的证书版本
MODULE_DESCRIPTION(XXX);//模块的描述,human-readable
MODULE_ALIAS(xx);//模块的别名
RAID5模块的初始化是由raid5_init()
函数来完成的,追踪此函数发现这条语句register_md_personality(&raid5_personality);
,这条的意思是将raid5_personality注册到md_personality中。nice,现在就来看看raid5_personality是什么:
static struct md_personality raid5_personality ={ .name = "raid5", .level = 5, .owner = THIS_MODULE, .make_request = make_request, .run = run, .free = raid5_free, .status = status, .error_handler = error, .hot_add_disk = raid5_add_disk, .hot_remove_disk= raid5_remove_disk, .spare_active = raid5_spare_active, .sync_request = sync_request, .resize = raid5_resize, .size = raid5_size, .check_reshape = raid5_check_reshape, .start_reshape = raid5_start_reshape, .finish_reshape = raid5_finish_reshape, .quiesce = raid5_quiesce, .takeover = raid5_takeover, .congested = raid5_congested, .mergeable_bvec = raid5_mergeable_bvec,};
也许开始看这个时发现不是很懂,那好我们再把md_personality贴出来,看看有什么联系:
struct md_personality{ char *name; int level; struct list_head list; struct module *owner; void (*make_request)(struct mddev *mddev, struct bio *bio); int (*run)(struct mddev *mddev); void (*free)(struct mddev *mddev, void *priv); void (*status)(struct seq_file *seq, struct mddev *mddev); /* error_handler must set ->faulty and clear ->in_sync * if appropriate, and should abort recovery if needed */ void (*error_handler)(struct mddev *mddev, struct md_rdev *rdev); int (*hot_add_disk) (struct mddev *mddev, struct md_rdev *rdev); int (*hot_remove_disk) (struct mddev *mddev, struct md_rdev *rdev); int (*spare_active) (struct mddev *mddev); sector_t (*sync_request)(struct mddev *mddev, sector_t sector_nr, int *skipped, int go_faster); int (*resize) (struct mddev *mddev, sector_t sectors); sector_t (*size) (struct mddev *mddev, sector_t sectors, int raid_disks); int (*check_reshape) (struct mddev *mddev); int (*start_reshape) (struct mddev *mddev); void (*finish_reshape) (struct mddev *mddev); /* quiesce moves between quiescence states * 0 - fully active * 1 - no new requests allowed * others - reserved */ void (*quiesce) (struct mddev *mddev, int state); /* takeover is used to transition an array from one * personality to another. The new personality must be able * to handle the data in the current layout. * e.g. 2drive raid1 -> 2drive raid5 * ndrive raid5 -> degraded n+1drive raid6 with special layout * If the takeover succeeds, a new 'private' structure is returned. * This needs to be installed and then ->run used to activate the * array. */ void *(*takeover) (struct mddev *mddev); /* congested implements bdi.congested_fn(). * Will not be called while array is 'suspended' */ int (*congested)(struct mddev *mddev, int bits); /* mergeable_bvec is use to implement ->merge_bvec_fn */ int (*mergeable_bvec)(struct mddev *mddev, struct bvec_merge_data *bvm, struct bio_vec *biovec);};
这下清楚了吧,哦,原来这是一个个指针函数,可是这两个是怎么联系到一起的呢?我们再回头看register_md_personality(&raid5_personality)
:
int register_md_personality(struct md_personality *p){ printk(KERN_INFO "md: %s personality registered for level %d\n", p->name, p->level); spin_lock(&pers_lock);//加锁 list_add_tail(&p->list, &pers_list);//添加到pers_list中 spin_unlock(&pers_lock);//解锁 return 0;}
这个函数最重要的一行就是讲传进来的md_personality结构添加到pers_list中,pers_list维护了整个MD的md_personality结构。可是如何通过MD调用到RAID5呢?细心了解源码,在md_run()
中给出了这种调用关系,由于函数过长,我们只贴出所需要的地方,首先获取对应RAID级别的md_personality结构:pers = find_pers(mddev->level, mddev->clevel);
,追踪这个函数:
static struct md_personality *find_pers(int level, char *clevel){ struct md_personality *pers; list_for_each_entry(pers, &pers_list, list) {//查找相应的pers if (level != LEVEL_NONE && pers->level == level) return pers; if (strcmp(pers->name, clevel)==0) return pers; } return NULL;}
仔细看注释的那句,居然有pers_list,好眼熟,这不就是register时那个全局list嘛!!!哎呀,好巧好巧,所以这个函数就是在pers_list中一个个比,如果level相同或者name相同,则找到了对应的md_personality结构,返回。
查看md.c中的代码,会发现有很多地方出现了如下形式的语句:md->pers->make_request
,这就是通过模块的声明,将RAID模块声明到MD模块中,通过md_personality的结构调用指针函数,从而实现RAID的功能,嘻嘻,回过头来看看也不是很复杂耶~
至此,RAID模块的声明已经完成,嘻嘻,不难吧,同样,对于模块的删除,在raid5_exit()
中:
static void raid5_exit(void){ unregister_md_personality(&raid6_personality); unregister_md_personality(&raid5_personality); unregister_md_personality(&raid4_personality); destroy_workqueue(raid5_wq);}
追踪unregister_md_personality()
:
int unregister_md_personality(struct md_personality *p){ printk(KERN_INFO "md: %s personality unregistered\n", p->name); spin_lock(&pers_lock); list_del_init(&p->list); spin_unlock(&pers_lock); return 0;}
和register相反,只是从链表中删除而已,道理一样。
关于模块的声明和删除已经讲得差不多了,下篇主要介绍下内核中RAID5的stripe_head管理和守护线程raid5d,这是了解RAID5运行原理的基石哦。
- Linux 内核中RAID5源码详解之RAID模块声明
- Linux 内核中RAID5源码详解之stripe_head的管理
- Linux 内核中RAID5源码详解之守护进程raid5d
- Linux内核中RAID5源码详解之基本架构与数据结构
- Linux 内核中RAID5源码详解之写过程剖析(一)
- Linux内核中RAID5源码详解之写过程剖析(二)
- linux内核raid5坏块读之谜
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID5/RAID10]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- RAID详解[RAID0/RAID1/RAID10/RAID5]
- 安装ruby环境
- 激活2003终端服务
- 【亲述】Uber容错设计与多机房容灾方案 - 高可用架构系列
- 什么是虚函数
- 知识点一:Settings默认值设置(一)
- Linux 内核中RAID5源码详解之RAID模块声明
- js和jq常用方法区分
- Android Studio 添加类似eclipse 里的user library
- There is no Action mapped for namespace / and action name login等Strut2错误总结!! (2011-11-04 14:25:07)
- C# 性能优化——三种字符串拼接效率
- #1066 : 无间道之并查集
- linux串口通讯问题小结
- 在Ubuntu上安装监控rstatd
- 自定义异常的应用1