分布式文件系统之NFS

news2024/10/3 0:32:13

「分布式」是现在蛮流行的一个词,而其盛行,离不开底层网络通信能力的迅速发展。在文件系统这个领域,早期的分布式实现更多的是用来实现「共享」,而不是「容错」。传统的集中式文件系统允许单个系统中的多用户共享本地存储的文件,而分布式文件系统将共享的范围扩展到通过网络互连的不同机器的用户中。

 

(免费订阅,永久学习)学习地址: Dpdk/网络协议栈/vpp/OvS/DDos/NFV/虚拟化/高性能专家-学习视频教程-腾讯课堂

更多DPDK相关学习资料有需要的可以自行报名学习,免费订阅,永久学习,或点击这里加qun免费
领取,关注我持续更新哦! ! 

最早的分布式文件系统诞生自上世纪70年代,目前依然在使用的FTP算是其中老资格的代表,但FTP的使用涉及文件的上传和下载,而随后出现的NFS,提供了像访问本地文件一样访问远程文件的方式。

NFS直译过来是「网络文件系统」,但其实网络文件系统是一个大类,为了区分,我们可以把它叫做"Sun Network File System"。

NFS的实现基于client-server模型,当client的用户态程序使用read()系统调用后,client位于内核的文件系统将通过网络传输,向server的文件系统发送读取某个block数据的请求。

server收到请求后,从自己的disk(或者page cache)读取对应的block数据,发送给client。然后,client的文件系统将收到的数据复制到read()入参指定的user buffer中。

虽然文件的实体在远端,但从client端用户程序的角度,read()调用返回的结果与对本地文件系统的访问无异,是透明的,这属于一种远程过程调用(RPC)。

RPC本身不依赖于任何传输协议,但其通常是基于UDP/IP实现的,为了保证传输服务的可靠性,RPC层会跟踪所有未回复的请求,如果没有收到答复,就按一定的时间间隔重传请求。

  • 谁来维护状态

当client发送访问文件的请求后,由server返回给client一个文件描述符,之后当client试图访问这个文件时,将文件描述符传给server就可以了,由server来维护这个文件的状态信息,这种模式被称为stateful

与之相对的另一种方式是:哪个文件被client打开了,访问到文件的哪个位置了(即"offset"),server通通都不记录,由client自己维护,并在发送读写文件请求时,包含所需的全部信息(self-contained),即stateless模式。

stateful模式平时用着没什么问题,但当server和client的任意一方出现crash的时候,都将给recovery增加复杂性。比如当server给client返回一个文件描述符后,自己崩溃了,之后重启运行后,client拿着这个文件描述符来找它,它就不记得自己前世的事情(该文件描述符对应哪个文件啊)。

虽然在崩溃恢复上可以少操很多心,但面对crash的问题,stateless模式也并非就可以高枕无忧。当server崩溃后,client会发现请求没有响应,于是持续地重发请求,直到server重启后回复请求,所以client实际上没法区分server是崩溃重启了,还是运行比较缓慢(或者网络连接存在异常)。

正因为此,当client发送写block的请求后,server需要等到数据真的写到disk上,才能回复client( 其实就是"write-through"方式 )。要不然client会误以为已经写成功了,实际上没有。对于本地文件系统,除非使用sync语义,write()调用只要到达page cache就可以返回,这是因为如果crash发生在本地的话,至少能确切地知道,数据可能是丢失或者不一致的。

  • NFS的选择

stateful和stateless各有利弊,作为以fast crash recovery为设计目标的NFS,其v2(1989年)和v3(1995年)版本的实现采用的都是stateless模型。

打开操作

既然NFS对于client是“透明的”文件操作,那要访问文件,第一步肯定是open()调用。client的open请求,在server端将转换成一系列的lookup操作:

比如client是通过"mount -t nfs server:/home/share /usr"命令挂载的,那么文件路径中的"usr"在server端就会被转换成"/home/share"。那为什么需要lookup那么多次呢?因为路径中的每一个目录都可能是一个不同的挂载点。

对于没有记忆(stateless)的server来说,面对open请求,不能只给client返回一个file descriptor,而是需要包含能定位文件的信息,包括文件所在的文件系统ID、文件的inode编号等。这些信息被封装在file handle里,交给client,之后client发送针对这个文件的读写请求时,再把对应的file handle拿给server,server才能知道怎么去磁盘上找到这个文件。

读写操作

访问本地文件系统,通常打开时需要检查一下权限,但读写时就不再需要了,但由于stateless的NFS没有维护状态,所以理论上每次读写都得进行权限检查。从这个角度来说,server端其实并没有真正地实现open()对应的操作。

为了提高读写性能,client可以将一些文件block的数据缓存在本地。但有cache就不可避免地带来cache一致性的问题,当client读取cache中的数据时,还是得向server发送查询该文件attribute(也就是meta data)的请求,以询问该block数据是否有被其他的client更改。

这种方案虽然原理简单,但会导致server收到大量的meta data查询请求,而且可能大部分的时间里,这个block的数据并没有被更改。一个改进的做法是:client将文件的meta data也缓存起来,每隔一段时间更新一次(比如3秒)。当然,这可能造成不一致的问题(读到stale的数据),但算是性能和cache一致性之间的一种平衡吧。

删除操作

根据Unix的规则,只要文件还在open状态(有object指向它),那么执行remove操作只会删除文件路径并将文件标记为"deleted",文件的内容并不会从磁盘移除,已经打开文件的进程依然可以继续访问文件(参考这篇文章)。

但NFS有所不同,假设Client B对共享文件进行了rename/move操作,Client A依然可以访问这个文件,因为访问基于的是inode而不是文件名,但如果Client C在server端把文件remove了,之后A的读取操作就会失败。

究其原因,同样因为stateless。作为server,它并不知道文件被remove的时候,有没有client端的进程还在"open"这个文件,client端的close()操作在server端也没有对应的实现。

那Client A的"read error"又是怎么来的(怎么被server判断的)呢?文件被Client C移除后,对应的inode也将随之被server回收,并且可能很快就被reuse以创建新的文件,但Client A不知道啊,它还继续发送read这个inode的请求。

这时的inode已经不是Client A想要的那个inode了,看来啊,得对inode加上一些额外的信息,比如一个单调递增的数字,或者是文件创建时的timestamp,反正能作为唯一性的区别就好。NFS选择的是递增数字的方式,称为"generation number"[注1],包在前面讲的file handle里,在之后client和server的交互中传递,过期的handle意味着文件已不复存在。

  • 不断演进的NFS

到了新世纪的2000年,NFS演进到了v4版本,其中一个重大的变化就是:它投靠了stateful阵营。除了解决上述stateless模式存在的一些固有问题,stateful模式还让client端的close()操作有了意义:通知server丢弃关于这个文件的状态信息。此外,NFSv4还在诸多方面进行了优化。

在client向server发送的请求中,很多操作常常是一前一后地伴随出现,比如client对NFS中的文件执行"ls -l"命令,"READDIR"之后往往紧接着"GETATTR"。对此NFSv3采用的方法是在协议中增加一个新的"READDIRPLUS"操作,到了v4则更加灵活,一个RPC message可以传递多个NFS操作,操作可以合并发送,在避免协议改动的情况下,增加了协议的可扩展性。

  • 联想

在我们自己的代码设计中,要不要记录状态(和过去发生关系),由哪个模块来记录状态,通常也是重要的考量之一。比如要计时,就得在过去采样一个timestamp,现在再采样一个timestamp,这就形成了对过去时刻状态的依赖。如果能构造出不依赖的场景,则往往可以减少很多判断,使得逻辑更加简洁清晰,也有利于减少异常造成的影响(这次错误不影响下次)。

注1:这主要是依靠"inode"结构体中的"i_version"来实现的,但考虑到重启后"i_version"可能回滚,所以还加上了ctime,以确保唯一性。

We could use i_version alone as the change attribute. However, i_version can go backwards after a reboot. On its own that doesn't necessarily cause a problem, but if i_version goes backwards and then is incremented again it could reuse a value that was previously used before boot, and a client who queried the two values might incorrectly assume nothing changed. By using both ctime and the i_version counter we guarantee that as long as time doesn't go backwards we never reuse an old value.

参考:

  • DESIGN AND IMPLEMENTATION OF THE SUN NETWORK FILESYSTEM

原文链接:https://zhuanlan.zhihu.com/p/295230549   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/95229.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SVG公众号排版 | GIF动图如何禁止循环播放?PS设置了也没用!

在SVG公众号排版中,我们经常使用到GIF动图,有些排版需求是想让GIF动图一直无限循环播放,也有其他排版需求是只想让GIF动图播放一次就停止了,这种情况我们可以通过Photoshop软件来设置GIF动图的播放次数,详见下图。 但是呢,也有一种情况,即使在Photoshop软件设置了GIF动图…

大话设计模型 Task02:策略、装饰、代理

目录一、简单工厂模式问题描述模式定义问题分析代码实现二、策略模式问题描述问题分析模式定义代码实现三、装饰模式问题描述问题分析模式定义代码实现四、代理模式问题描述问题分析模式定义代码实现五、工厂方法模式问题描述问题分析模式定义简单工厂 vs. 工厂方法代码实现一、…

上传项目代码到Github|Gitee

上传项目代码到Github|Gitee 文章目录上传项目代码到Github|Gitee1、前置准备1.1 Git 安装1.2 在 Git 中设置用户名1.2.1 为计算机上的每个存储库设置 Git 用户名1.2.2 为一个仓库设置 Git 用户名1.3 SSH免密登录1.4 Github创建一个新的仓库2、上传项目2.1 初始化本地库2.2 添加…

蓝桥杯入门即劝退(十六)查找元素范围(双解法)

欢迎关注点赞评论,共同学习,共同进步! ------持续更新蓝桥杯入门系列算法实例-------- 如果你也喜欢Java和算法,欢迎订阅专栏共同学习交流! 你的点赞、关注、评论、是我创作的动力! -------希望我的文章…

什么是制造业数字化转型?制造业数字化转型的核心与意义

对于生产制造企业来讲,当下如果不进行数字化转型的话,很大概率会被时代所抛弃的。为什么这么讲?因为在未来的很长一段时间,你可以充分了解到,数字化转型已然成为了制造业向前的主旋律。既然数字化势在必行,…

可以赚钱的副业项目,简单易上手兼职副业推荐

在当前的经济环境下,对每个人来说,仅仅依靠那点薪水生活是非常紧张的。为了改善你的生活,你需要找到其他赚钱的方法,在互联网上做兼职是一个不错的选择。 今天推荐几个普通人可以做的兼职副业,希望对大家有所帮助。 一…

微信公众号的文章可以修改几次?修改的步骤有哪些

许多小伙伴们在运营微信公众号的时候,可能会遇到过这些难题,在发布微信公众号之前检查没有检查好,导致有错字或者是错句。有的时候可能配图还会配错! 今天伯乐网络传媒就给大家带来一些实用的东西,比如微信公众号可以…

深入理解 Python 的对象拷贝和内存布局

深入理解 Python 的对象拷贝和内存布局 前言 在本篇文章当中主要给大家介绍 python 当中的拷贝问题,话不多说我们直接看代码,你知道下面一些程序片段的输出结果吗? a [1, 2, 3, 4] b a print(f"{a } \t|\t {b }") a[0] 100…

微信小程序的自定义组件(1)

文章目录1. 自定义组件2. 组件样式3. 组件-数据、方法和属性4. 组件数据监听器5. 组件纯数据字段1. 自定义组件 Component(Object object) | 微信开放文档 (qq.com) 创建组件 在项目的根目录中,鼠标右键,创建components->test文件夹在新建的componen…

第十二章 计算学习理论

12.1 基础知识 计算学习理论研究的关于通过计算来进行学习的理论。即关于机器学习的理论基础,其目的是分析学习任务的困难本质,为学习算法提供理论保证,并根据分析结果指导算法设计。 12.2 PAC学习 计算学习理论中最基本的是概率近似正确&…

点击化学染料DBCO-PEG-CY7.5|Cyanine7.5-PEG-DBCO|花青素Cyanine7.5

​DBCO-PEG-CY7.5点击化学染料其中Cy7.5 (Cyanine 7.5) 是一种发近红外(NIR)荧光的花青素荧光染料。根据磺化与否,分为普通Cy7.5和磺化Cy7.5,但常常统称为Cy7.5。 Cy7.5的消光系数高,荧光也很亮,并且对pH不…

为什么很多人转行学习Web前端技术?

为什么很多人转行学习Web前端技术?不管你是工人阶层还是服务行业,是否想过转行IT,转行IT后肯定会选择一门编程语言进行深入学习,很多转行的人基础都不是太好,不是科班出身,甚至有的是专科乃至中专,前端的H…

cuda学习笔记3——cuda常用内存相关函数及其使用示例

cuda学习笔记3——cuda常用内存相关函数及其使用示例常用的GPU内存函数cudaMalloc()cudaMemcpy()cudaFree()代码示例常用的GPU内存函数 cuda程序将系统区分成host和device,二者有各自的memory。kernel可以操作device memory,为了能很好的控制device端内…

软件测试人员去外包公司待遇怎么样?外包薪资高吗?

📌 博客主页: 程序员二黑 📌 专注于软件测试领域相关技术实践和思考,持续分享自动化软件测试开发干货知识! 📌 公号同名,欢迎加入我的测试交流群,我们一起交流学习! 可能…

Python基础-画图:matplotlib

Python画图主要用到matplotlib这个库。具体来说是pylab和pyplot这两个子库。这两个库可以满足基本的画图需求。 pylab神器:pylab.rcParams.update(params)。这个函数几乎可以调节图的一切属性,包括但不限于:坐标范围,axes标签字号…

ESP32 OTA

装好Arduino环境后,做了一个遥控小车: 1、uni-app 包装 nipplejs (Nipplejs by yoannmoinet)做了一个简单的摇杆,调用ESP32的WebServer接口,控制小车 2、ESP32 连上路由的WiFI,用WebServer开发了一个接口&#xff0c…

PG::Wpwn

nmap -Pn -p- -T4 --min-rate1000 192.168.225.123 nmap -Pn -p 22,80 -sCV 192.168.225.123 查看80端口的页面 尝试枚举路径 发现了/wordpress路径 使用wpscan扫描 wpscan --url http://192.168.225.123/wordpress/ -e ap 使用exploit-db搜索“social warfare” https:/…

我国原油期货行业趋势:消费量上升及鼓励政策落地 成交额将继续增长

原油期货是最重要的石油期货,世界上重要的原油期货合约有4个:纽约商业交易所(NYMEX)的轻质低硫原油即“西德克萨斯中质油”期货合约;迪拜商品交易所的高硫原油期货合约;伦敦国际石油交易所(ICE&…

【AIOT】蓝牙调研

经典蓝牙模块(BT):泛指支持蓝牙协议在4.0以下的模块,一般用于数据量比较大的传输,如:语音、音乐等较高数据量传输。经典蓝牙模块可再细分为:传统蓝牙模块和高速蓝牙模块。传统蓝牙模块在2004年推…

非零基础自学Golang 第8章 包管理 8.1 工作区

非零基础自学Golang 文章目录非零基础自学Golang第8章 包管理8.1 工作区8.1.1 工作区结构8.1.2 GOPATH8.1.3 GOROOT8.1.4 GOBIN第8章 包管理 对于大部分编程语言来说,代码包都是最有效的代码管理方式,Go语言也是使用包来管理代码的。如同其他语言一样&a…