google怎样使用linux(内核)

今年的linux内核开发大会上,google的开发人员也上台做了名为“how google use linux"的演讲。我斗胆翻译注解一番——括号内为注解,欢迎读者斧正。

(前面几段讲google对linux kernel代码的管理及跟进,偏细碎,不翻译了)
在google为linux加入的代码中,3/4是对内核核心的改动,设备驱动代码只是其中相对较小的一部分。
(linux发展到现在这个阶段,需要加入的新的设备驱动已经越来越少了)

如果google要与linux社区的合作开发,那将面临一系列问题。跟上linux代码的主干太难——它的代码更新的太快了。在一个大型项目 里,开发者对补丁的提交、重改确实是个问题。Alan Cox对此的回答十分简单:人总是贪得无厌的,但有时候就应该简单的对他们说”不“。
(Alan Cox是linux kernel的二号功臣,现已加入Intel公司。我觉得Intel这样的CPU公司很适合内核开发者)

在CPU调度上,google发现想改用新的cfs(“完全公平调度”,由Con Kolivasy在2.6.23中加入内核)非常麻烦。由于太麻烦,google不得不倒回去把O(1) sheduler(2.6.23前内核使用的调度算法,想知道这段具体的历史,可以参考另一篇cfs文章)移植到2.6.26上,一切才能运转起来。内核对sched_yield()语义的更改也造成了麻 烦,尤其当google使用用户态锁时。高优先级的线程会对服务器的负载均衡(这里的负载均衡指的是一台服务器上多个CPU对多任务的分配处理,不是指分布式)造成影响,哪怕这些线程只是运行很短的时间。而负载均衡很重要:google通常在16-32核的服务器上跑5000个线程(好诡谲的用法!)。

在内存管理上,新的linux内核改变了对脏数据的管理,导致出现了大量主动的写回操作(脏数据要写回硬盘)。系统很容易出现这种局 面:kswaped(swap进程)会产生大量小的I/O操作,塞满块设备的请求队列,结果造成别的写回无法完成(写回“饥饿”);这个问题已经通过 “per-DBI写回机制”补丁在2.6.32内核中解决了。
(per-DBI的主要原理是块设备不再只有一个等待队列,而是多个,每个“硬盘轴”一个队列,因为硬盘轴是一个硬件上的真正的工作单位。这样,对装配多个硬盘的服务器会有很好的I/O性能。不过我个人猜测,如果能把kswaped的小请求合并,是否也能提高性能呢?)

如上所述,google在系统中启动很多线程——不寻常的用法。他们发现如果向一个大的线程组发信号,会造成运行队列锁的大量竞争。google 还发现mmap_sem信号量(是内核结构 struct mm_struct中用来保护mmap空间的内核信号量)有竞争问题;一个睡眠的读者会阻塞写者,而这个写者又阻塞了其他读者,最后造成系统死锁。内核应该修改,拿到信号量以后不要 再等待I/O。
(google所说的信号对线程组造成的问题估计是“惊群效应”,就是很多任务睡在一个队列上,一个唤醒操作会造成他们都突然醒来,结果必然是资源拥挤。我个人认为这不是linux的问题,这是google使用linux的方法太奇特了,所以内核开发者没有注意到)

google大量使用了OOM killer来减轻高负载服务器的负担。这样做有一定的麻烦,当拥有锁的进程被OOM杀掉时(锁并不会释放,结果就阻塞了别的任务)。Mike(演讲人) 很想知道为什么kernel费那么大劲搞出OOM killer来,而不是简单的在分配内存失败后返回一个错误。
(不光Mike,大家都有这样的 疑问,估计答案只能在内核邮件组里找了。而google所说的那个“进程被杀锁却没释放、造成阻塞”的问题,yahoo在freebsd-4.11的时代 就已经解决了,用了很巧很轻量级的办法。大家都觉得google的技术最牛,其实公正的说,牛公司牛人很多,只是大家没他那么高调而已。但对国内来说,能 通过改进内核来提高服务器的公司,也真是凤毛麟角了。)

(此外略去一段google对内核开发工作的分类,看不太懂)

google增加了一种SCHED_GIDLE的调度类,是真正的空闲类;如果没有CPU供使用,属于此类的任务就彻底不运行(甚至不参与对 CPU的抢夺)。为了避免“优先级反转”问题,SCHED_GIDLE类的进程在睡眠时(此处指内核睡眠,不是系统调用sleep造成的睡眠)会临时提高 优先级。网络由HTB排队规则管理,配有一组流量控制逻辑。对硬盘来说,它们按linux的I/O调度来工作。
(假设三个进程A、B、C,优先级 为A>B>C,假设C先运行,占了一个重要的共享资源,A也想要这个资源,所以A等待C完成,但由于B的优先级比C高,结果C还没完成就调度 到B运行了,这样总的来看,B的运行先于A,尽管A的优先级比B高。这就是“优先级反转”问题。通常的解决方法是:谁占了重要的共享资源,谁就临时提升自 己的优先级,比如C占了资源后优先级临时升到和A一样高,释放资源后再把优先级降回来。说白了,占用资源的一伙人,他们最好有相同的优先级,不然会有麻 烦)

除了这些,google还有很多代码用于监控。监控所有的硬盘和网络流量,记录之,用于后期对运维的分析。google在内核加了很多钩子,这样 他们就能把所有的硬盘I/O情况返回给应用程序——包括异步的写回I/O。当Mike被问到他们是否使用跟踪点时,回答是“是的”,但是,自然 的,google使用的是自己的一套跟踪方法。

google内核改进在2010年还有很多重要的目标:

      google对CPU限制功能很兴奋,通过此功能,就能给“低延时任务”较高的优先级,而不必担心这些任务把霸占整个系统。

      基于RPC的CPU任务调度,这包括监控RPC入口流量,以决定唤醒哪一个进程。(这很有分布式OS的味道)

      延迟调度。对很多任务来说,延迟并不是什么大不了的事。但当RPC消息到来时,内核却尝试去运行所有这些任务;这些消息不会分布到不同的CPU上(意思就是处理这些请求的服务进程可能就在某几个CPU上运转),这造成了系统负载在CPU间分配的问题。所以任务应该能被标为“延迟调度”;当被唤醒后,这些任务并不会被直接放到运行队列上,而是等待,知道全局的CPU负载调度完成。

      插入空闲周期。高级电源管理使google能够把服务器用到接近烧毁的边缘——但是不超过这个边缘。

      更好的内存管理已经列入计划,包括统计内核的内存使用。

      “离线内存”。Mike强调想买到便宜好用的内存是越来越难。所以google需要能够把坏内存标出的功能。HWPOISON兴许可以帮到他们。

      在网络方面,google希望改进对“接收端缩放”的支持——即把输入流量导到指定的队列。google还需要统计软件中断次数然后转给指定的任务——网络进程通常包含大量的软中断。google已经在拥塞控制上做了很多工作;他们开发了“网络不安全”的拥塞算法,该算法在google的数据中心运转良好。名为“TCP pacing”的算法放慢了服务器的输出流量,避免了交换机过载。
      (自己管理数据中心的公司就是不一样,网络优化做得很细)

      在存储方面,google花了大量精力降低块设备层的瓶颈,这样就能更好的使用高速flash。在块设备层通过flash来提高存储效率已经列入了开发计划。google考虑在内核里增加flash转换层,但大家建议google还是把操作flash的逻辑直接放入文件系统层更好一些。

Mike最后总结了几个“感兴趣的问题”。其中一个是google希望把文件系统的元数据固定在内存里。目的是减少I/O请求的时间。从硬盘读取一个块的时间是已知的,但是如果元数据不在内存中,就会有不只一个I/O操作被执行(会有新的I/O操作用来读元数据)。这样就减慢了读文件的速度。google目前对此问题的解决方案是直接从原始块设备读取数据到用户空间(估计是用O_DIRECT,然后自己在用户态管理元数据缓存,这样就不会使用系统的cache),但以后不想 再这么做了。
(不知道google所说的filesystem metadata到底指哪些数据,因为不同的文件系统,metadata也很不同,既然google说要把这些数据固定在内存里,估计应该不大,那自己缓存有什么不好?希望有机会可以问问Mike)

还有一个问题,使用fadvise会降低系统调用的性能。目前还不清楚问题的细节。

google的这个演讲很成功,linux社区也从自己最大的客户那里学到了不少东西。如果google更加面向linux社区的计划能够付诸行动,那linux将会拥有一个更好的kernel。

(注:google可算是IT公司中使用linux最多的,也很可能是使用得最深的,30个人的内核开发队伍,非常可观。看看国内,很少公司、很少人为开源做过贡献,唉,说起来惭愧,在下也是之一啊。)

相关文章

分类

留言:

关于文章

This page contains a single entry by DongHao published on 11 9, 2009 5:07 PM.

《青蛇》 was the previous entry in this blog.

测试linux上的hugepages (hugetlbfs) is the next entry in this blog.

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