为了账号安全,请及时绑定邮箱和手机立即绑定

大话闲聊之系统

文件系统(File System)

RAMCloud

Why RAMCloud uses a log-structured strategy for data in DRAM?

RAMCloud 在备份上,没有完全使用内存做备份,而是在内存中有一个备份,在硬盘上也有一个备份。这样的备份策略省了内存,但是造成了两个问题:

  • 备份相对慢速,可能会影响系统的正常操作
  • 系统 crash 后恢复要从硬盘中开始,会比较慢

Log-Structured Strategy 就是为了解决第一个问题而采用的。请求到了内存里是以日志的形式记录,随后会将 entry 分发给各个备份,各个备份在收到后会直接返回,然后再处理,约等于实现了异步的操作。因此备份时的 overhead 被 hide 了,但是这样引入了新的问题,可能导致系统 crash 后没有把 buffer flush 到真正的存储中。软件不行硬件来凑,文中提到了两种方法来保证 entry 到了 buffer 中就是持久化的,一种是 DIMM + super-capacitor,一种是加电池。

综上,日志主要是为了解决同步备份的时候因为 hierarchy 产生的性能问题。

Which policy does RAMCloud use to place segment replicas and how to find the segment replicas during recovery?

以往的实现是中心化的 coordinator,这样会造成性能瓶颈。所以 RAMCloud 采纳了去中心化的思想,利用了随机化和微调的方式,来分散备份。有些类似 k choises 的选择,RAMCloud 会先随机选择一些,然后再从中选出最合适的,并且加入了 reject 的机制来保证在乐观并发的情况下不会产生竞争的问题。同时为了保证尽可能贴近最优解,RAMCloud 考虑了硬盘速度以及硬盘上已经有的 segment 的数量来进行微调,保证尽可能的均匀。

recovery 的时候以往的实现是在 coordinator 里维护一个中心化的表,这样的做法向上面提到的一样会造成瓶颈,因此 RAMCloud 在 recovery 的时候会问所有的备份,备份会返回一个它存储的副本列表,整个过程是并行的而且 RAMCloud 用了自称 fast 的 RPC,因此整个过程不会特别慢。

Does RAMCloud support random access? If so, please explain how.

支持不支持,支持。

RAMCloud 在每个 master 上都维护有一个哈希表,结构为 <table identifier, object identifier>,通过哈希表,可以进行随机的访问。

Optimistic Crash Consistency

这是一篇发表在 SOSP’13 上的论文,主要的工作是在 Ext 4 这样的基于 Journal 来做 Crash Recovery 的文件系统的基础上实现了一种 Optimistic 的 Crash Recovery 的方法,这样的方法能够在保证 Crash Consistency 的同时,大幅度提高性能。但是计算机所有的提高都是 tradeoff 的结果,Optimistic Crash Consistency 是牺牲了数据的 Freshness,也就是新鲜度。

Crash Consistency,就是指文件系统在 Crash 之后,其中的数据还是不是一致的。这里的一致指的是 metadata 和 data 等等数据之间的一致。如果不一致的情况发生了,往往意味着硬盘丢了数据,或者文件系统找不到硬盘上的数据。

在基于 Journal 的文件系统中,一次写入磁盘的操作,要写入的数据有 data,以及在 Journal 中的一份 metadata 的冗余,还有 Journal 中的 commit block,以及最后的 metadata。这四个数据要保证写入顺序才能确保 Crash Consistency。因此要保证数据写入的顺序,就要借助磁盘的 flush 操作,来强制地把数据从磁盘的 cache 刷到真正的磁盘中才行。但是这样会导致很大的性能问题,这篇论文提出了使用 checksums,asynchronous durability notifications,delayed writes 等技术来使得文件系统不需要强制的 flush 操作。但是这样的实现,就会牺牲掉数据的 Freshness,在之前 Ext 4 的实现中,Crash 之后最多丢一个 transaction 的数据,现在可能丢 k 个。但是在性能上比带 flush 的 ext 4 提高了 4-10 倍。

F2FS

随着 NAND Flash 的发展,现在很多持久存储都渐渐地变成了 SSD。之前的文件系统都是针对 HDD 来设计的,因此并没有针对 FLash 存储的一些硬件特性进行优化,而本文则是提出了一个为了 Flash 存储设计的文件系统,现在以及被并入了 Linux Kernel。整篇论文是很多比较偏工程的点拼接起来的,因此读起来不像是其他文件系统的论文那么晦涩。

Flash 存储在读上面的速度众所周知非常快,而且它并不是像 HDD 那样的机械结构,用磁头来进行读写,而是电子的,因此像内存一样拥有一定的并行性。与此同时,在写数据时,Flash 存储并不是 in place 的写入,而是需要写入一个新的地方,然后修改地址使其生效。这一点非常重要,它导致了 “Wandering Tree“ 的问题,这是 Log structured fs 在 SSD 上存在的一个问题:因为 Flash 存储的写是 out of place 的,因此每次写操作都会使得数据块的地址发生变化,所以需要递归地修改 direct block, indirect block 等等一系列块的内容。为了解决这个问题,F2FS 引入了一个新的表:Node Address Table(NAT)。Indirect block 是指向 NAT 的,这样每当一个 data block 被污染的时候,会更新 direct block 和 NAT 中的表项,这样就防止了在 indirect node block 中的传播。

同时为了利用 Flash 存储的并行性,F2FS 采取了 multi-head logging,并不只有一个 log,而是有多个 log,根据数据的更新频率来写 log,这样提高了性能。

PMFS

现在有一种新的概念,Non-volatile Memory(非易失性内存)。它是指通过给内存加电容等等方式来实现内存持久性的一种方式。而也有很多研究是基于此来设计文件系统,这篇文章就是这样的一个文件系统。

很惭愧,这篇文章没怎么听课,也没怎么好好读,不过可以讲讲大致的实现。它最让人印象深刻的地方是在保证 Consistency 的时候,使用了 Hybrid 的方式,综合使用了 COW,Journaling 等等方式。还有就是引入了一个新的硬件 primitive,读的不深就不瞎说了。

操作系统(Operating System)

Exokernel

// TODO Add the notes

Memory

Transactional Memory

事务性内存已经不是一个新鲜的概念了,从大二以来就一直听说这个词,但一直没有了解过相关的内容。这篇论文是 93 年的,应该是比较老的一篇关于硬件支持事务性内存的论文。而现在在 Intel Haswell 的 CPU 架构中已经有了对事务性内存的支持,但是大家还是有些相关性的。

// Critical Section
x=VALIDATE;
if (x) {
      COMMIT;
} else {
      ABORT;
}

上面的代码是事务性内存的一个编程范式,原本是使用锁来进行同步,现在所有的操作可以被理解为是一个事务,只有在事务提交的时候才会生效,而如果遇到了冲突则会被 Abort,所有修改完全被 Discard。

本文是基于嗅探的缓存一致性协议修改来的,文章中在硬件上实现这一点的时候加入了一个新的 L1 Cache,叫做 Tx Cache。因此非事务修改走 regular cache,事务走 Tx Cache。

事务缓存有四个标签,Normal, XCommit, XAbort, Empty。事务提交的时候 XCommit -> Empty, XAbort -> Normal。事务 Abort 的时候,XAbort -> Empty, XCommit -> Normal。

它其实是利用了现存的缓存一致性协议,对于冲突的判断,都是用它来完成的。要理解这篇论文就必须对缓存一致性有比较清楚的认识。而因为实现利用了 Cache,所以在发生 Context Switch 等需要 Flush Cache 的情况下时,事务是一定会被 Abort 的,所以这篇文章的实现主要面向的场景是 short critical section,同时没有 IO 读写。

不过,实现因为是完全基于现有的实现(缓存一致性),因此可以跟非事务性内存的同步操作一起进行,因为其他不在事务中的操作是一样会触发缓存操作的。这算是一个这样实现带来的好处,很关键。

CFI

Control-Flow Integrity

// TODO Add the notes

Tail Latency

The Tail at Scale

Why latency variability is amplified by scale?

这是受到系统的特性影响的。在文中给出的例子,是假如一个用户访问一个服务器,99% 的请求是在 10ms 内被处理完的,而有 1% 的请求是在 1s 内被处理的。在这种情况下,针对一个服务器的请求还是可以接受的。但是现在绝大多数应用都是需要多个服务共同服务最后才返回给终端用户的,因此在这样的情况下,用户感知到的延迟是所需要的服务中延迟最大的值。因此,在这样的情况下,用户感知到的延迟是随着系统规模的增大而提高的。这里的 scale 指的是系统的规模。

Please briefly outline some effective tail-tolerant techniques.

比如最简单的,使用多台服务器,处理完全相同的逻辑,用 replica 的方式降低延迟。用户的请求会发送给所有的服务器,当任何一个服务器处理完请求,就返回。这样的方式要求应用要自己处理幂等请求,逻辑也会变得复杂一些。在之上可以做一些优化,比如在一个服务器处理完请求后,通知其他服务器终止对幂等请求的处理等等,不过这样也更加增加了实现的难度。

还有一些减少 daemon 进程的方法比如使用 unikernel 等等更加精简的内核,但是这样的做法成本就更高了。

Why tolerating latency variability for write operations is easier?

因为一般写都是异步的,而且可以容忍一定的不一致。以及在一致性的写上一般都会使用 Paxos, ZAB, raft 之类的算法。而这些算法本身就是 tail tolerant 的。

Lock

Non-scalable locks are dangerous

Why does the performance of ticket lock collapse with a small number of cores? (Hint: cache coherence protocol)

在 ticket lock 的实现中,是维护两个 ticket 变量,当前 ticket 和 next ticket。在 unlock 操作中是将当前 ticket 自增从而使得下一个拿到了 ticket 的 core 获取到了锁。

凡是遵循文章中提出的硬件缓存一致性模型的系统,都会遇到 ticket lock 的 rapid collapse。文章提出的硬件缓存一致性模型用目录的方式类比了 CPU 中的缓存,并且用马尔可夫链模型,对 ticket lock 的问题进行了分析。其中每一个节点代表有几个 core 在等的状态,Arrival and Service Rates 分别代表了在不同状态下锁的获得和释放。其中到达率跟在没有在等的 core 的数量 (n - k) 成正比,服务率和在等的 core 的数量 (k) 成反比。所以随着 k 的增大,服务率是减小的。这使得模型得到了一个数学上的结论,锁的获取时间和正在等待锁的 core 的数量是成正比的。

因此随着 core 的增加,在 serial section 很小的时候,Sk = 1/(s + ck/2) 中 k 对 Sk 的贡献越大,因此越容易受到 k 的影响。这也就是为什么,在 serial section 很小的时候,只是多了很少一些在等待锁的 cores,就出现了性能的雪崩。

Why does MCS lock have better scalability than ticket lock?

在 ticket lock 中,所有的 core 都去依靠同一个变量来获得锁,而在 MCS lock 中,前一个 core 在 release 的时候会修改下一个 core 的 locked 变量,通知下一个 core 来获得锁。这样每个锁都只依靠属于自己的一个变量,这样的实现是缓存一致性无关的,有着更好的 scalability。

Bug

STACK

Undefined behavior 是编程语言规范对某段代码可能产生的某些执行结果未定义。Unstable code 就是在程序实际的执行过程中,由于涉及到undefined behavior,从而无法被编译器翻译(直接略过)的代码段。

STACK会在Assumption Δ被允许和不允许的情况下分别模拟编译。

  • 先模拟假设不成立的情况进行一次编译;
  • 模拟假设成立的情况进行一次编译;
  • 查看前两步的执行结果有没有区别,有区别的地方就是 unstable code。

如果执行第二步时得不到准确的结果,那么会漏报一些unstable code;如果执行第一步时得不到准确的结果,就会产生误报(false warning / false positive)。目前stack给出的Undefined behavior pattern 可能不齐全。

对于程序员来说,通过fix bug或者去掉一些会被编译器当做是undefined behavior的代码;对于编译器来说,可以集成一些现有的bug-finding的工具,或者利用STACK的方式来判定unstable code;完善编程语言的specification,定义更多的代码执行规则,减少undefined behavior的产生。

STACK为了使可扩展性更高,在计算Δ = ∀e:Reach(e) → ¬Undef(e)的时候做了一些近似运算,使最后得到的结果可能会漏掉一些unstable code。STACK为了简化和滤过某些查询用到的constraint solver如果发生了timeout,也会出现漏报的情况。因此,STACK为了更好的扩展性,牺牲了一定的可靠性(精度)。

点击查看更多内容
1人点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
手记
粉丝
58
获赞与收藏
404

关注作者,订阅最新文章

阅读免费教程

感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消