关于
我的项目
热度排行
- [转] 宫崎骏用动漫教给我们的人生哲理,每一句都能说到心里! - (日期:[八月 24, 2013] 点击:[53,590])
- Google 网页爬虫报告无法连接站点解决办法 - (日期:[七月 20, 2014] 点击:[38,665])
- 架设Tiny Tiny RSS(TTRSS)阅读器,找回Google Reader! - (日期:[九月 27, 2013] 点击:[27,806])
- SkyDrive、DropBox和Google Drive三大公有云存储服务对比 - (日期:[六月 25, 2013] 点击:[25,665])
- 升级到至强E5440后,与i5 CPU笔记本性能对比 - (日期:[二月 18, 2014] 点击:[23,841])
- 公钥私钥加密解密数字证书数字签名详解 - (日期:[四月 19, 2014] 点击:[22,976])
- 本站建站技术合集 - (日期:[九月 20, 2013] 点击:[22,555])
- 使用OpenerDNS解决无法访问Google的问题 - (日期:[七月 5, 2014] 点击:[21,856])
- WordPress博客添加“返回顶部”按钮 - (日期:[七月 14, 2013] 点击:[21,273])
- Linux文件系统基础之inode和dentry - (日期:[三月 13, 2015] 点击:[20,216])
- 云存储中的HTTP鉴权算法分析 - (日期:[二月 7, 2014] 点击:[18,654])
- 存储基础知识之——磁盘阵列原理及操作实战 - (日期:[二月 9, 2014] 点击:[17,542])
- 精选37条强大的常用linux shell命令组合 - (日期:[九月 4, 2013] 点击:[17,469])
- DNS原理、架构和配置详解 - (日期:[九月 6, 2013] 点击:[16,874])
- Netty和Jetty的Java NIO 网络框架模型分析 - (日期:[七月 13, 2013] 点击:[16,350])
- CoreOS 初识之安装 - (日期:[十一月 16, 2014] 点击:[16,218])
- Windows与Linux文件系统互访的几种方法 - (日期:[八月 21, 2014] 点击:[15,739])
- Dijkstra算法求解最短路径分析 - (日期:[七月 12, 2014] 点击:[14,942])
- NAS解决方案实现多媒体文件共享播放 - (日期:[十二月 21, 2014] 点击:[13,966])
- 简介 - (日期:[九月 1, 2012] 点击:[13,788])
- 如何编程实现 2 + 2 = 5? - (日期:[六月 2, 2014] 点击:[13,278])
- 搭建了一个iNews程序 - (日期:[十月 15, 2013] 点击:[13,252])
- 2014年9月曝出的Bash ShellShock漏洞简析 - (日期:[九月 26, 2014] 点击:[13,170])
- 彻底解决WordPress博客垃圾评论的问题 - (日期:[八月 5, 2013] 点击:[13,162])
- 如何使用1M的内存排序100万个8位数 - (日期:[三月 27, 2014] 点击:[12,570])
- 全部日志列表 - (日期:[十一月 11, 2012] 点击:[12,424])
- 关于回调函数和this指针探讨 - (日期:[八月 24, 2014] 点击:[12,245])
- 开源好用的电子书管理服务Talebook(Calibre网络版)安装使用指南 - (日期:[四月 23, 2022] 点击:[11,835])
- 给定一个long型常量,其值为x,给定long型变量a,要求a & x 的取值集合 - (日期:[九月 8, 2012] 点击:[11,738])
- WordPress建站必备实用插件 - (日期:[八月 7, 2014] 点击:[11,388])
分类目录
文章归档
- 2025年四月 (3)
- 2025年一月 (1)
- 2024年十二月 (1)
- 2024年四月 (1)
- 2024年二月 (1)
- 2023年九月 (1)
- 2023年一月 (1)
- 2022年十月 (1)
- 2022年八月 (2)
- 2022年四月 (1)
- 2022年三月 (1)
- 2021年十二月 (2)
- 2021年十月 (2)
- 2021年九月 (1)
- 2021年八月 (1)
- 2021年五月 (1)
- 2021年三月 (2)
- 2021年一月 (2)
- 2020年十二月 (5)
- 2020年十一月 (2)
- 2020年十月 (2)
- 2020年九月 (1)
- 2020年八月 (5)
- 2020年七月 (2)
- 2019年九月 (1)
- 2018年八月 (1)
- 2018年七月 (1)
- 2018年六月 (1)
- 2018年五月 (1)
- 2018年三月 (1)
- 2018年二月 (1)
- 2018年一月 (2)
- 2017年十二月 (3)
- 2017年十月 (4)
- 2017年九月 (1)
- 2017年七月 (1)
- 2017年六月 (1)
- 2016年十二月 (1)
- 2016年十月 (1)
- 2016年九月 (1)
- 2016年七月 (2)
- 2016年六月 (1)
- 2016年二月 (3)
- 2015年十二月 (3)
- 2015年十一月 (2)
- 2015年十月 (1)
- 2015年八月 (2)
- 2015年七月 (4)
- 2015年六月 (1)
- 2015年三月 (2)
- 2015年二月 (1)
- 2015年一月 (4)
- 2014年十二月 (2)
- 2014年十一月 (2)
- 2014年十月 (5)
- 2014年九月 (8)
- 2014年八月 (11)
- 2014年七月 (17)
- 2014年六月 (7)
- 2014年五月 (15)
- 2014年四月 (16)
- 2014年三月 (14)
- 2014年二月 (5)
- 2013年十二月 (5)
- 2013年十一月 (3)
- 2013年十月 (13)
- 2013年九月 (13)
- 2013年八月 (13)
- 2013年七月 (9)
- 2013年六月 (8)
- 2013年五月 (1)
- 2013年三月 (3)
- 2013年一月 (1)
- 2012年十一月 (1)
- 2012年九月 (12)
- 2012年八月 (3)
- 2011年二月 (1)
- 2009年三月 (1)
- 2009年二月 (1)
- 2008年十一月 (1)
- 2008年六月 (1)
- 2008年四月 (1)
- 2008年三月 (1)
分类目录: 存储技术
基于DRBD的高可用NFS解决方案分析
存储基础知识之——硬盘接口简述
一、IDE(Integrated Drive Electronics,简称IDE)
一般说来,ATA是一个控制器技术术,而IDE是一个匹配它的磁盘驱动器技术,但是两个术语经常可以互用。ATA是一个花费低而性能适中的接口,主要是针对台式机而设计的,销售的大多数ATA控制器和IDE磁盘都是更高版本的,称为ATA – 2和ATA – 3,与之匹配的磁盘驱动器称为增强的IDE。
随着SATA(Serial ATA)的推出,ATA已经退出历史舞台。为与SATA区分,原ATA已经改称PATA(Parallel ATA)。
[转] 分布式存储系统(GlusterFS, Swift, Cassandra)设计对比
之前转过一篇分布式文件系统比较的文章,几大分布式文件系统全方位比较,这里再从存储的角度转一个。应该说者三个开源软件各自侧重的领域不一样,但是都具备分布式存储的特征,因此这篇文章主要是从存储的角度来进行对比。
云存储中的HTTP鉴权算法分析
基于Base64编码的HTTP Basic Authentication由于安全问题,已经不再广泛使用了。在云存储中,数据的安全性一直被广泛关注。亚马逊的AWS S3和Openstack Swift分别采取了不同的算法来对每一个HTTP请求进行鉴权。这里想对二者的鉴权过程作简单分析和总结。
一、AWS S3的HTTP请求鉴权流程
Openstack Swift简介
背景与概览
Swift 最初是由 Rackspace 公司开发的高可用分布式对象存储服务,并于 2010 年贡献给 OpenStack 开源社区作为其最初的核心子项目之一,为其 Nova 子项目提供虚机镜像存储服务。Swift 构筑在比较便宜的标准硬件存储基础设施之上,无需采用 RAID(磁盘冗余阵列),通过在软件层面引入一致性散列技术和数据冗余性,牺牲一定程度的数据一致性来达到高可用性和可伸缩性,支持多租户模式、容器和对象读写操作,适合解决互联网的应用场景下非结构化数据存储问题。
此项目是基于 Python 开发的,采用 Apache 2.0 许可协议,可用来开发商用系统。
华为存储助力CERN揭开宇宙奥秘
摘要:2013年10月8日,瑞典皇家科学院宣布将今年诺贝尔物理学奖授予英国物理学家彼得·希格斯和比利时物理学家弗朗索瓦·恩格勒,用于表彰他们对希格斯玻色子(又称“上帝粒子”)所做的预测。
2013年10月8日,瑞典皇家科学院宣布将今年诺贝尔物理学奖授予英国物理学家彼得·希格斯和比利时物理学家弗朗索瓦·恩格勒,用于表彰他们对希格斯玻色子(又称“上帝粒子”)所做的预测。
DRBD源码分析(三)——块设备驱动和IO队列处理函数
很长时间没有继续这个源码分析了,原因是到了主流业务,对底层的驱动知识不太了解,也没有太多时间。
在上一节中分析到
STATIC void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp)
方法。在该方法中有一处调用:
mdev = ensure_mdev(nlp);
在这个调用中,会进行设备的注册和驱动的加载。这一节重点分析struct drbd_conf* drbd_new_device(unsigned int minor)方法。该方法主要是一个块设备的驱动。关于块设备的驱动程序的编写,可以参考CU上面的赵磊的帖子,该帖子绘声绘色的讲解了如何从0基础开始编写块设备驱动:链接。
对于每一个块设备,会进行一系列的初始化,会启动3个内核线程:
drbd_thread_init(mdev, &mdev->receiver, drbdd_init);
drbd_thread_init(mdev, &mdev->worker, drbd_worker);
drbd_thread_init(mdev, &mdev->asender, drbd_asender);
DRBD源码分析(二)——内核模块网络配置和启动
在上一篇里面分析到了基于netlink的connector,connector正是内核态与用户态配置命令交互的通道。用户通过调用用户态的工具,发送相应的命令参数,用户态工具将命令参数转换成相应的消息包,内核态解析消息后得到相应的指令,继续转换成函数调用,最后得以执行。
首先仔细看一下上一节提到的创建connector时注册的收数据的回调函数:
#ifdef KERNEL_HAS_CN_SKB_PARMS
STATIC void drbd_connector_callback(struct cn_msg *req, struct netlink_skb_parms *nsp)
{
#else
STATIC void drbd_connector_callback(void *data)
{
struct cn_msg *req = data;
#endif
struct drbd_nl_cfg_req *nlp = (struct drbd_nl_cfg_req *)req->data;
struct cn_handler_struct *cm;
struct cn_msg *cn_reply;
struct drbd_nl_cfg_reply *reply;
struct drbd_conf *mdev;
int retcode, rr;
int reply_size = sizeof(struct cn_msg)
+ sizeof(struct drbd_nl_cfg_reply)
+ sizeof(short int);
if (!try_module_get(THIS_MODULE)) {
printk(KERN_ERR "drbd: try_module_get() failed!\n");
return;
}
#ifdef KERNEL_HAS_CN_SKB_PARMS
if (!cap_raised(nsp->eff_cap, CAP_SYS_ADMIN)) {
retcode = ERR_PERM;
goto fail;
}
#endif
mdev = ensure_mdev(nlp);
if (!mdev) {
retcode = ERR_MINOR_INVALID;
goto fail;
}
trace_drbd_netlink(req, 1);
if (nlp->packet_type >= P_nl_after_last_packet) {
retcode = ERR_PACKET_NR;
goto fail;
}
printk("packet_type is %d\n", nlp->packet_type);
cm = cnd_table + nlp->packet_type;
/* This may happen if packet number is 0: */
if (cm->function == NULL) {
retcode = ERR_PACKET_NR;
goto fail;
}
reply_size += cm->reply_body_size;
/* allocation not in the IO path, cqueue thread context */
cn_reply = kmalloc(reply_size, GFP_KERNEL);
if (!cn_reply) {
retcode = ERR_NOMEM;
goto fail;
}
reply = (struct drbd_nl_cfg_reply *) cn_reply->data;
reply->packet_type =
cm->reply_body_size ? nlp->packet_type : P_nl_after_last_packet;
reply->minor = nlp->drbd_minor;
reply->ret_code = NO_ERROR; /* Might by modified by cm->function. */
/* reply->tag_list; might be modified by cm->function. */
rr = cm->function(mdev, nlp, reply);
cn_reply->id = req->id;
cn_reply->seq = req->seq;
cn_reply->ack = req->ack + 1;
cn_reply->len = sizeof(struct drbd_nl_cfg_reply) + rr;
cn_reply->flags = 0;
trace_drbd_netlink(cn_reply, 0);
rr = cn_netlink_send(cn_reply, CN_IDX_DRBD, GFP_KERNEL);
if (rr && rr != -ESRCH)
printk(KERN_INFO "drbd: cn_netlink_send()=%d\n", rr);
kfree(cn_reply);
module_put(THIS_MODULE);
return;
fail:
drbd_nl_send_reply(req, retcode);
module_put(THIS_MODULE);
}
值得注意的是:
rr=cm->function(mdev,nlp,reply);
这一句,这里相当于是一个多态,function绑定到哪一个方法由消息包中携带的包类型决定:
cm=cnd_table+nlp->packet_type;
系统在初始化时级生成了一个全局的静态函数表,类似P_primary的标识符是在编译时动态生成的宏。表示其所在的元素的下标,同时也月包类型相对应。
static struct cn_handler_struct cnd_table[] = {
[ P_primary ] = { &drbd_nl_primary, 0 },
[ P_secondary ] = { &drbd_nl_secondary, 0 },
[ P_disk_conf ] = { &drbd_nl_disk_conf, 0 },
[ P_detach ] = { &drbd_nl_detach, 0 },
[ P_net_conf ] = { &drbd_nl_net_conf, 0 },
[ P_disconnect ] = { &drbd_nl_disconnect, 0 },
[ P_resize ] = { &drbd_nl_resize, 0 },
[ P_syncer_conf ] = { &drbd_nl_syncer_conf, 0 },
[ P_invalidate ] = { &drbd_nl_invalidate, 0 },
[ P_invalidate_peer ] = { &drbd_nl_invalidate_peer, 0 },
[ P_pause_sync ] = { &drbd_nl_pause_sync, 0 },
[ P_resume_sync ] = { &drbd_nl_resume_sync, 0 },
[ P_suspend_io ] = { &drbd_nl_suspend_io, 0 },
[ P_resume_io ] = { &drbd_nl_resume_io, 0 },
[ P_outdate ] = { &drbd_nl_outdate, 0 },
[ P_get_config ] = { &drbd_nl_get_config,
sizeof(struct syncer_conf_tag_len_struct) +
sizeof(struct disk_conf_tag_len_struct) +
sizeof(struct net_conf_tag_len_struct) },
[ P_get_state ] = { &drbd_nl_get_state,
sizeof(struct get_state_tag_len_struct) +
sizeof(struct sync_progress_tag_len_struct) },
[ P_get_uuids ] = { &drbd_nl_get_uuids,
sizeof(struct get_uuids_tag_len_struct) },
[ P_get_timeout_flag ] = { &drbd_nl_get_timeout_flag,
sizeof(struct get_timeout_flag_tag_len_struct)},
[ P_start_ov ] = { &drbd_nl_start_ov, 0 },
[ P_new_c_uuid ] = { &drbd_nl_new_c_uuid, 0 },
};
比如,在一次完整的用户态与内核态的交互中,用户态会多次发出P_get_state消息,该消息的包类型码为17。
类似cn_handler_struct这样的函数表,在drbd的代码中随处可见,无论是内核态还是用户态,这样一致的风格,应该非常利于扩展和维护。看代码的人也会觉得非常轻松,不至于无章可循。
DRBD的配置信息、虚拟设备、网络通信端口、对端信息等都是通过drbdsetup或者drbdadm工具以netlink消息包发送到内核态的。
在收到5号消息包时,drbd_nl_net_conf会被调用。在该函数中,会启动worker内核线程,该线程监控一个等待队列,当有事件到来时,即取出处理:
int drbd_worker(struct drbd_thread* thi)
{
...
w = NULL;
spin_lock_irq(&mdev->data.work.q_lock);
ERR_IF(list_empty(&mdev->data.work.q))
{
/* something terribly wrong in our logic.
* we were able to down() the semaphore,
* but the list is empty... doh.
*
* what is the best thing to do now?
* try again from scratch, restarting the receiver,
* asender, whatnot? could break even more ugly,
* e.g. when we are primary, but no good local data.
*
* I'll try to get away just starting over this loop.
*/
spin_unlock_irq(&mdev->data.work.q_lock);
continue;
}
w = list_entry(mdev->data.work.q.next, struct drbd_work, list);
list_del_init(&w->list);
spin_unlock_irq(&mdev->data.work.q_lock);
if (!w->cb(mdev, w, mdev->state.conn < C_CONNECTED))
{
/* dev_warn(DEV, "worker: a callback failed! \n"); */
if (mdev->state.conn >= C_CONNECTED)
drbd_force_state(mdev, NS(conn, C_NETWORK_FAILURE));
}
...
}
启动了worker线程之后,几乎所有的内核态的事务都会交给这个线程来处理。
继续回到drbd_nl_net_conf方法中,在初始化完worker线程后,会继续执行如下语句:
retcode=_drbd_request_state(mdev,NS(conn,C_UNCONNECTED),CS_VERBOSE);
这里既是与对端协商确定当前谁是主节点。在该方法中会向等待队列中放入一个事务,该事务为启动一个receiver线程,receiver线程会使用配置文件中指定的端口和IP信息建立tcp socket监听,等待对端的链接。此时,如果对端一直未有连接过来,本端尝试与对端连接也一直无法建立,则会根据配置等待指定的超时时间,之后会将本段置为Standalone状态。这也就是我们常见的两台服务器同时重启时,会发现一端的启动过程卡在drbd的等待上面。