前言:
本文档以单主模式进行数据流分析,双主模式就是从机也升级为主机可以进行数据读写操作,单主模式下,数据只能从一端也就是升级为primary的那一端进行读写操作,partner端是不能进行任何操作的,不能进行格式化,更不能挂载查看primary同步过来的数据,只有把这一端升级为primary之后才可以进行常规的挂载查看文件、读写文件操作。
第一部分:加载驱动、创建资源、启动服务
配置内核将drbd编译成模块,通过modprob将drbd.ko注册进内核,通过drbdadm命令读取配置文件创建资源,之后启动驱动里面的各种收发处理线程,并建立网络链接。
1.1加载驱动
主要操作就是创建并注册一个块设备文件,主设备号为147,写入drbd的元数据信息、初始化日志、初始化bitmap,两端的drbd设备初始化完成之后,就可以启动drbd服务了。
drbd: initialized. Version: 8.4.5 (api:1/proto:86-101)
drbd: srcversion: D496E56BBEBA8B1339BB34A
drbd: registered as block device major 147
1.2创建资源、启动服务
通过在两端up资源的方式建立最初的网络连接,初始化各自的role,最初两端都是secondary。在启动服务里面比较主要的就是通过drbd_adm_new_resource函数接口,申请网络资源,并通过配置文件与partner建立链接。另一个主要完成的内容就是创建三个线程分别是drbd_receive、drbd_worker、drbd_asender。
drbd_thread_init(resource,&connection->receiver,drbd_receiver, "receiver");
drbd_thread_init(resource,&connection->worker,drbd_worker, "worker");
drbd_thread_init(resource,&connection->asender,drbd_asender, "asender");
1.2.1receiver线程
在receiver里面创建网络资源并进行链接操作,同时通过drbdd函数接口阻塞接收partner发送过来的数据,当收到数据后进行命令解析,执行对应的处理函数。
1.2.2worker线程
Worker函数主要处理来自sender链表里面的请求数据,并通过解析命令执行对应的回调函数进行进一步的处理操作。这里的sender里面的数据主要就是处理drbd_make_request函数接收的请求数据,并且是需要同步到partner端的数据。
1.2.3asender线程
Asender函数主要处理平时的心跳检测,维护两端的状态。主要的同步数据操作还是通过receiver函数里面的drbdd函数进行处理。
第二部分:接收请求、处理请求数据
任何块设备驱动的入口都是对上层提供的请求处理函数。
2.1接收请求、初始化请求队列
驱动通过drbd_adm_new_minor函数提供drbd块设备的make_request_fn函数的注册,也就是drbd_make_request函数。在drbd_adm_new_minor函数里面完成块设备的申请、初始化、注册等内容。并为每一个链接创建一个peer_device设备,用来统一管理之后的所有数据处理。同时初始化一个工作队列INIT_WORK(&device->submit.worker,do_submit);在do_submit函数里面接收下面的drbd_request_prepare函数发送过来的请求。
在drbd_make_request这个函数里面主要进行两步操作,第一步就是drbd_request_prepare函数。
在这个函数里面把上层提交的BIO请求进行克隆,一份用于在本地进行提交,另一份用于进行数据同步操作。通过向初始化时创建的内存池申请req的方式创建,并使用BIO对这个req进行初始化,如果是写请求最终将其链接入device->submit.writes链表,之后唤醒工作队列的处理函数进行do_submit操作。
第二步是直接调用drbd_send_and_submit(device,req);函数对上一步返回的req进行发送与提交操作。
2.2处理请求队列
这里主要通过drbd_send_and_submit(mdev,req);函数进行实现。
这个函数主要完成请求的处理,包括判断请求里面是否存在与暂未处理完成的请求里面数据重叠部分,如果有就等待先前的先处理,避免出现数据不一致。判断是否需要进行同步操作,如果需要就把请求发送到worker处理线程,通过worker处理线程的回调函数进行数据发送,数据发送通过drbd_socket的mutex锁机制进行保护,先发送包头部分,之后在发送BIO里面的数据,并且同步的数据是通过对BIO进行拆分,对BIO每个段里面的页进行的发送,调用标准socket的发送函数即socket->ops->sendpage。与之后的本地数据以BIO为单元进行的数据提交不同。
之后会对本地数据进行处理,本地数据处理使用克隆出来的BIO,通过drbd_submit_req_private_bio(req);最终调用块设备的generic_make_request(bio);函数进行本地数据的提交操作。
脑图文件链接:点击打开链接