Recently in *nix and Win32 Kernel Category

文件系统与大扇区

| 1 Comment

大扇区(超过旧式标准的512字节扇区)是改善硬件工艺或访问方式以后的一种直接提高存储密度的方法。对于磁介质来说,其盘片被分成若干的磁道(通常是同心圆)、每个磁道分成若干的扇区或称扇段,扇区是磁盘读写时的最小操作单元。对于基于闪存的存储设备而言,扇区则是一种模拟传统磁盘的概念。

对于磁盘来说,由于它是一种机械和电子一体化设备,由于各种原因的限制,其寻址和持续读写的可靠性方面都有一定的限制,例如,马达维持同样的转速,且磁头能够稳定在同一个位置,并将数据可靠地连续读写,可能只能持续8KiB或16KiB,为了提高可靠性,我们往往会需要在这之后重新校准设备。另一方面,对于随机写入的情形,较小的数据块有时会更为有利,同时,避免对磁介质的不必要的反复覆写,也有助于提高其使用寿命。因此,在磁道的基础上又引入了扇区的概念。

现代磁盘的设计中,扇区除了用户可见的部分之外,其前后还有一定的空隙用于定位和校验。这些空隙和其附近的介质一样可以用来保存数据,只是制造商和操作系统的撰写者通常不推荐使用这些空间。软盘上的这些空隙在DOS时代可以通过对控制器直接编程并改变扇区尺寸的方式强制覆写,并用类似的方法读出,从而起到一定的保密效果。

随着制造工艺的改善,现今的硬盘在十年前就早已不必受限于512字节的限制。由于在每个扇区前后都需要添加一些信息会浪费一部分空间并增加潜在的定位开销,因此硬盘制造商开始推出4KiB扇区的磁盘。这种磁盘在系统检测时会仍然汇报扇区尺寸是512字节,但物理扇区则是4KiB,并保持现有的512字节编址方式不便,从而与现有的BIOS保持兼容。

另一种已经存在一段时间的大扇区是Flash设备。这类设备采用的方法类似,不过其大扇区更多地是出于制造和避免不必要的擦写而不是提高存储密度考虑。

最后一种大扇区是RAID。许多RAID级别是一次性操作一整个stripe的,此时这种stripe也可以视为一种大扇区。

对于大扇区的支持主要是操作系统,特别是文件系统的责任,而应用程序只需确保自己操作的记录尽量是对齐的整块(例如,16K的记录,在文件中的偏移量都是从16K的整数倍开始)。文件系统需要保证应用程序对于磁盘的这种预期是能够满足的,换言之,文件系统的数据结构不应在磁盘上导致新的不对齐问题,这类不对齐可能是由于分区格式引起,也可能是文件系统本身的设计问题,如果存在这样的问题,操作系统中都需要予以纠正:例如,传统上从第63个512字节逻辑扇区开始的分区,可以牺牲掉一个512字节单元来换取对齐4KiB物理扇区的目的,由于对齐写入和读出操作需要的实际物理操作要少一半左右,因此这样做能够显著地改善性能并提高设备的使用寿命。

FreeBSD 9-CURRENT中目前已经支持通过GEOM子系统的stripe信息来描述磁盘或RAID的物理扇区尺寸以及GEOM相对于物理扇区的偏移量。新的ahci(4)驱动目前已经能够提供相关的GEOM信息给更上层的驱动程序了。

SU+J

| No Comments

Jeff Roberson 下周左右将会正式发表对于 UFS 的一项改进,为 Soft Updates 加入 Journal-ling,从而简化其恢复逻辑,并消除对 fsck 的依赖。

目前常见的保持元数据一致性的方法有四种:最原始的、将元数据以同步方式写盘的方法,性能非常差;常见的文件系统中使用的元数据回写日志(如ext3),缺点是无法检验日志本身的正确性,而且元数据需要写入两次因此对性能有潜在影响;Soft Updates,缺点是需要运行fsck来释放资源泄漏,而这个操作很耗时,且实现本身比较复杂;Copy-on-Write,在WAFL和ZFS中采用的技术,随着硬盘的淘汰随机存取时间不再是性能瓶颈,应该是未来的发展趋势,目前的缺点是会导致产生较多碎片。SU+J结合了Soft Updates和Journalling的优点,即,使用Soft Updates来确保写到磁盘上的数据的一致性,而使用Journalling来确保资源泄漏能够迅速回收,从而消除了fsck的必要性。

非常期待看到明年BSDCan的presentation。虽然目前我拿到的代码还有少量毛边,但是总体来说这次改进:

  • 不需要修改磁盘上的文件系统数据结构,因此能够用于现有系统;
  • 减少了 Soft Updates 本身的复杂性,每个事务所需的描述数据只需32个字节,扫完32K次操作(1MB日志数据)需要的时间只需2秒不到;
  • 极大地减少了由于 fsck 需要吃很多内存、很多 I/O 导致的恢复速度慢的问题,这个实现基本上可以不再使用fsck了

ses控制和监控程序

| 1 Comment

继续记一笔,在 /usr/src/share/examples/ses 里面。

为啥这些程序不成为基本系统的一部分呢?难道是因为太简单了......害得我好找(好歹应该在联机手册里面提一句吧)。顺手把代码清理了一下,等新机器到了拿这个去杵杵看。有谁知道ses设备和总线位置(也就是物理的槽位)之间的对应关系有什么办法能拿到吗?还是只能写个程序让一个人去根据这种设备训练一下系统?

用hints固定硬盘设备名

| No Comments

今天听 Doug White 大牛说有这个功能,于是查了一下资料,先记一笔,不一定对,有机会找机器测试一下。

首先要定义SCSI/SAS总线所在的位置。通常它是由某个SCSI接口卡提供的,也就是类似这样:

hint.scbus.0.at="mpt0"

这表示 scbus0 是 mpt0。如果一块卡支持多个 SCSI 总线,则还应额外指定 bus 参数。

接下来是每块硬盘的位置。SCSI/SAS设备可以由3个参数来唯一定位:总线、目标(target)和单元(LUN)。例如总线0、目标0、单元0上的设备应该叫做da0,则配置如下:

hint.da.0.at="scbus0"
hint.da.0.target="0"
hint.da.0.unit="0"

可以使用简单的awk或python甚至shell来成批生成。这种定位方式与Solaris的c0t0d0类似,但用户可以自己指定名字(配置 devd.conf 应该可以更进一步创建设备对应的 c0t0d0 这样的符号连接,改天再看看)。配好之后写到 /boot/device.hints 就好。

ZFS性能的一些优化结论

| No Comments

最近几天测试了一下盘很多(具体说是24块盘,其中2块热备的JBOD)的时候ZFS的性能特点。一些结论

a) ZFS的随机读性能比较差(相对于顺序读写)。这一点除了改为用mirror而不是raidz1/z2之外似乎没什么太好的办法。同样多的硬盘做成两组raidz1(11+11+2),与做成两块盘一组的11组mirror的pool相比,针对同样的数据集的随机读性能相差可达10倍多,当然,mirror的结果是顺序写性能会差一些。作为副作用,mirror时的读性能可提高大约4倍左右。

b) 告诉ZFS数据集常用的数据块尺寸可以提高读写混合的操作的性能。例如如果应用程序多数时间都在操作16K的数据块,将块尺寸改为16K(默认为128K)可将混合操作的性能提高十几倍。

暂时还没测试SSD做ZIL/cache对性能的具体影响,先记下一笔。现在看来如果给数据库用的话,比较好的配置应该是若干对mirror+热备组成zpool,然后把具体存放数据库的zfs的recordsize设置为16k,改天拿实际的数据库在上面跑跑看。

DMA设备驱动的常见问题

| 4 Comments

DMA (Direct Memory Access) 是一种提高计算机系统并发能力的技术。简单地说,它允许外围设备以异步方式操作内存,从而减少了CPU在I/O操作中的参与。

目前的微机和PC服务器都广泛采用了 DMA 技术。由于 DMA 是一种异步操作,因此在撰写驱动时,有很多需要注意的问题。

第一类比较常见的问题是,并不是所有的 DMA 控制器或设备都有能力访问全部物理内存,或对访问有限制。例如,许多低端存储设备和网卡往往只能访问物理内存的前4GB,甚至更小的范围。而另一些设备可能只能同时访问同一段整4GB内存,例如,它可能只能访问物理地址为 0~4G-1,或4G~8G-1的内存,而不能同时访问两部分内存。还有一种比较常见的限制是设备只能按照整数次方幂边界来访问内存,例如它可能要求映射长度为4K的DMA起始地址为4K的整数倍,等等。

这一类问题比较隐蔽。如果驱动程序没有考虑这些问题,导致的结果往往是在运行一段时间之后突然发现数据损坏等问题。如果硬件手册中没有特别指出硬件的限制,我们往往可以通过设计一些特别的用例来强制系统映射位于某些可能导致问题的边缘内存来发现这类问题。

针对这些限制,操作系统往往提供了一些绕过限制的方法,例如比较常见的bouncing page,即在较低的物理内存地址进行DMA映射,然后由OS在DMA完成之后将这些数据复制到其他地方。这些方法都会导致性能下降,因此,对于希望承担高性能任务的系统而言,应尽量避免使用这样的硬件。

第二类比较常见的问题是,驱动程序(CPU)在不适当的时候读写内存。许多设备都支持主动发起DMA(Bus Mastering),这种时候,CPU可能没有办法知道设备是不是正在写内存。解决这种问题的方法是引入内存栅(Memory Barrier),即驱动程序在读写内存前后通知硬件自己将要执行的操作,并由硬件来确保相应的结果。例如,网卡驱动在读写映射环或映射链的时候,应在读前以及写前后分别进行内存栅操作,确保自己没有读到过期数据,并确保设备没有读到过期数据。

这一类问题也非常隐蔽。对于负载不高的情形,很可能驱动程序的开发者不会注意到任何问题。甚至,对于负载较高的情形,这类问题会表现为响应慢而不是不稳定或数据损坏。

一部分应用层协议放进kernel?

| No Comments

先把想法记下来。

  • 提供一种描述语言,将应用层协议描述成DFA;
  • 一个generator将DFA转换为代码;
  • 用户态程序通过某种接口来向kernel请求已经协商好的该种协议的socket并由kernel直接填写一部分数据结构;
  • 应用程序对socket的第一个回应有时间限制----如果一段时间之内不回应,则系统将该socket交给下一个监听进程。

解决的问题:

  • 减少上下文切换。内核直接完成协议初始的协商会话(包括TLS/SSL?)
  • 减少用户能看到的出错情形,i.e. 服务进程不在的时候(例如,在重启过程中)内核可以将服务交给其他服务进程而不是简单地回应RST。

另外一种DSR结构

| 2 Comments

dawnh在之前的人肉traceback中提到了 另一种DSR结构。即:

  • 服务器端将虚拟IP绑在lo0上(子网掩码为/32);----确保服务器收包、不广播ARP;
  • 负载平衡设备接Internet的网口接路由器进来的VLAN;("VLAN-Internet")----从Internet进入的包发到负载均衡设备上;
  • 负载平衡设备的内网网口能够到达服务器;("VLAN-Incoming")----负载均衡设备将请求根据负载均衡的条件路由到VLAN-Incoming上的服务器;
  • 路由器提供一个绑定某一内网网址,且能联通服务器的接口;("VLAN-Outgoing")----服务器回应包发到VLAN-Outgoing上的Internet路由器;
  • 服务器默认网关设为路由器的内网地址。

(此VLAN划分是出于性能方面的考虑,不划分VLAN并不会导致整个系统不能用)。

这种做法保持了在外网网卡上直接绑一个未用公网IP之后,不需要绑定路由器MAC地址的优点,而在这种配置中,服务器直接连接网络的网卡上都不需要绑定公网IP地址。缺点是需要有一个能够在VLAN-Outgoing到Internet之间进行路由的路由设备,或能够直接控制到Internet那一跳的路由器(当然,这种情况,路由器本身甚至也可以不占用同网段IP,即,VLAN-Internet与VLAN-Outgoing合并、负载平衡设备公网网口绑VLAN-Outgoing上的内网IP,在路由器上配置指定的Internet地址段路由到VLAN-Outgoing网口)。

改一行代码带来的性能改进

| No Comments

FreeBSD先前的vesa framebuffer驱动有个问题,就是滚屏的时候会比较慢。jkim大长辈于是改了一行代码

好吧,我承认我之前一直以为是console驱动想要锁&Giant的问题。其实真正的原因是默认的pmap_mapdev并不做写合并,所以应该呼叫更低阶的pmap_mapdev_attr并传入PAT_WRITE_COMBINING参数。

现在VESA console的滚屏几乎和文本模式一样了。活到老学到老吧......

因为有人在我前一篇blog《使用DSR模式实现单IP服务冗余》里提了个问题,这里解释一下。

DSR比较常见的两种配置,一种是我之前文章中提到的禁止外网网卡ARP的方法,另一种是把虚拟IP绑定到lo0上。这两种方法各自有一些优缺点。一般来说,我喜欢用前一种方法。

实现DSR结构的关键是,通往Internet路由器的那个网络上,只有负载平衡设备在网络上宣示虚拟出来的那个IP的MAC地址,这样,当请求进来的时候,数据会发到负载平衡设备,而不是某一台服务器上。

禁止某块网卡上的ARP的时候,由于这块网卡完全忽略ARP(不仅是禁止发出ARP请求,同一广播域上的ARP广播也会忽略)。禁止网卡上的ARP,但将虚拟IP绑在网卡上的语义是这样:

  • 当收到目的地址为虚拟IP的包时,将这个包看作是发给自己的包;
  • 这块网卡不接受任何ARP协议的请求,也不广播ARP。

而另一种方法,也就是网卡上绑与虚拟IP同网段的另一个IP(服务器之间不冲突),但将虚拟IP绑在lo0上的做法的语义则是这样:

  • 当收到目的地址为虚拟IP的包时,将这个包看作是发给自己的包;
  • 禁止响应虚拟IP的ARP请求,也不主动宣示虚拟IP的MAC地址;

第一种做法的优点是无需在与虚拟IP同网段的网络上再占用一个IP(第二种方法需要:这个IP必须和虚拟IP是同一个网段上的,以便让系统知道通往路由器的网卡是这一块)。很明显,DSR的时候你不会希望使用 RFC 1918 规定的的私有IP,除非这个服务是针对内部网络的。

第二种做法的优点是无需在服务器上维护路由器IP与MAC地址的对应表。这个特性可以减少维护的压力。例如,如果网络上只有一个路由器,而没有做VRRP或CARP的时候,在做路由器割接时,由于路由器的MAC地址变动,第一种做法会需要一次性修改全部服务器上的MAC绑定。当然,如果网络上的路由器是冗余的,这个优势可能就不那么明显了;另一方面,IPv4地址已经快用完了,所以,为了世界和平,还是建议大家用第一种做法。lo上绑定的IP地址掩码最好是/32(仅此IP)。

当然,两种做法,*nix系统都是支持的。FreeBSD支持,Linux也支持。

使用DSR模式实现单IP服务冗余

| 3 Comments

FreeBSD支持以DSR(Direct Server Return,即服务器直接将流量通过路由器返回,而不经过负载平衡设备;俗称"单臂模式")模式提供服务。为了改善服务的可用性,可以使用这种方法来配置服务器。这种方法非常适合于大并发、大流量的环境。

网络及服务器的规划

首先,需要对服务可能产生的负载、流量进行估计,并据此设计网络的结构。DSR结构极大地降低了负载平衡设备成为瓶颈的可能,但仍对它们有一些要求,因此,设计者需要考虑下面一些问题:

  • 如何在服务器之间分摊负载。简单地说,就是应平均地分配流量,还是根据一些特征,例如会话等等去分配流量?原则上,网络设备对于协议本身所做的计算越少,它们所需要消耗的性能就越少,合理的设计应尽可能减少对网络设备在这方面的依赖,以改善整个系统的可扩充性。靠近应用层的规则往往会比靠近物理层的规则消耗更多的计算资源,因此应用的设计者应当尽可能将这部分开销考虑在内。
  • 网络设备的背板带宽。接入层交换机的容量是有限的,在考虑成本的前提下,有时出于性能考虑,很可能会希望使用不同的网络设备,而不是仅仅采用VLAN来划分不同的网络。
  • 了解可能出现瓶颈的点,并予以因应。

服务器配置

DSR结构对服务器有这样一些要求:

  • 接入Internet的网络端口不应发出或响应ARP请求。具备三层能力的交换机在看到ARP请求时,会将二层地址(MAC地址)和三层地址(IP地址)予以绑定。因此,在这些交换机看来,拥有公网IP地址的端口应该是负载平衡设备,而不是最终提供服务的服务器。
  • 服务器上应绑定提供服务的公网IP地址。操作系统通常只处理发到本机的IP包。
  • 服务器上应绑定公网路由器的MAC地址。操作系统的TCP/IP协议栈会发二层包到公网路由器。
  • (可选)服务器应配置两组,而不是一组默认路由。严格地说这并不是必需的,因为提供服务的服务器并不一定需要直接主动发起访问 Internet 的请求,例如,它们可以使用有内网IP地址的代理服务器。
  • 每一台实际提供服务的服务器应拥有一个唯一的内网IP地址。

在FreeBSD上,这可以通过在 rc.conf 中的网卡配置中增加"-arp"参数,并配置 static_arp (9.0和更高版本,我会在稍后将其MFC)来实现。

负载平衡设备配置

在负载平衡设备上,在实现DSR结构时,这些设备扮演的角色是 路由器,而不是 NAT 网关。也就是说,它们的作用是将来自客户端的访问 IP 包直接 路由 到服务器的内网地址(说明:此时,这些内网 IP 地址会被当作"下一跳";负载平衡设备查找本地 ARP 表并将数据包在2层上直接传递给对应的服务器;而服务器看到的数据包的目的地址仍是提供服务的那个公网IP)。

FreeBSD pf规则举例:pass in on em0 route-to { em1 内网IP1, em1 内网IP2, em1 内网IP3 } round-robin proto tcp from any to 公网IP port http keep state (sloppy)

其中,(sloppy)需要比较新版本的pf。目前的 FreeBSD 版本并不支持这一用法。可以使用 这个 补丁。这个选项表示pf应使用较为宽松的状态机;如果不需要状态(DNS协议通常不需要保持完整的会话,因为通常不会有很大的请求包)则不需要keep state和(sloppy)配置。

模拟实模式x86 CPU

| No Comments

前一段时间commit了来自XFree86 -> NetBSD -> OpenBSD的x86emu,这是一个大约9千行C代码的x86实模式CPU模拟器。

由于 amd64/EM64T "long mode" 不支持先前 x86 保护模式中的 "vm86" 扩展(虚拟实模式环境),因此,这个模拟器使得 FreeBSD 能够在 amd64(EM64T) 平台上支持 vesa、dpms 等功能了。

这些改动同样适用于 RELENG_6、RELENG_7、RELENG_8(前两个可能需要略微做一点点改动)。具体的changeset包括:

r197009 - 导入原版(OpenBSD 4.6)x86emu

r197019 - 对x86emu进行一些修改使其适用于 FreeBSD 编译环境,连接到build上。(需要说明的是,x86emu是跨平台的)。


r197021 - MFC时需要:建立目录。

r197022 - 把vesa和dpms代码复制到新位置。(注:svn无法有效地在一次commit中表达把文件挪到新的位置,并进行修改这样的操作。为了便于在 diff -c 时能够看到改动,将这个mv操作拆成了cp和rm)

r197025 - 对vesa、dpms代码进行修改,不再使用vm86而改为使用x86emu。删除原先位置的 vesa 和 dpms。

r197042 - bz@ 补齐了我在 197025 中忘记删除的一个文件。

r197081 - 告诉 make delete-old 需要删除的文件。

r197085 和 r197109 - 启动时可通过 hint 配置直接进入高分辨率模式。

还有一些小问题需要修正,不过大体上是做完了。其中,x86emu的代码经过OpenBSD和NetBSD两拨人的修整已经很清晰易读(主要是译码和执行单元两部分)。

About this Archive

This page is an archive of recent entries in the *nix and Win32 Kernel category.

AsiaBSDCon is the next category.

Find recent content on the main index or look in the archives to find all content.

Pages

OpenID accepted here Learn more about OpenID
Powered by Movable Type 5.01