主题:【原创】进程的反击 -- zllwy
借用一下Star War里面“帝国的反击”的题目,来聊一下最近进程(process)重新回到系统设计的前台的情况。操作系统的并发计算技术(concurrent computing,不是并行计算 parallel computing。Concurrent computing是个更广的概念,parallel computing是指有可以并行处理的硬件)从multiprogramming,multitasking/multiprocessing到multi-threading,并发的单元从job,task到process,再到thread。最近十几年,multi-threading是最时髦的并发编程技术。进程(process)作为一个并发计算的单元,已经逐渐隐入后台,很少为程序员接触了。还在直接用fork()和IPC编程的可能不多见了。进程和线程的最大区别就在于进程有独立的地址空间,线程则在一个进程内共享地址空间。从系统资源的角度来说,进程比较重,粒度更大,耗用的资源多。随着并发计算越来越重要,尤其是并行的硬件支持越来越丰富,轻量级的线程自然成了时髦的选择。
不过,最近一些新的计算平台的出现,又把进程带到了前台。这里说两个例子。一个是Chrome浏览器,一个是Android操作系统。Chrome改变了以往浏览器用线程运行并发的任务(比如显示一个网页),增加了进程来隔离不同网页的显示。最大的好处自然是提高了浏览器的安全性和可靠性。一个网页进程的崩溃不会导致整个浏览器的崩溃。最多只要把出现故障的页面关掉就可以了。在实际的使用体验中我们也确实感受到了可靠性的提高。另外,一个网页的进程无法访问另外一个网页的进程,使得浏览器的安全漏洞减少了。无独有偶,在Android系统中也使用了进程来隔离不同的应用。每个应用在缺省的情况下单独在一个虚拟机的实例中运行。这样如果一个应用发生问题不会影响到其他的应用。这点我体会也很深。以前我的java me的dumb phone,时不时会死机然后需要重启。Android的手机我到目前为止还没有死机过。最多是某个单独的应用死了,把它杀掉也就是了。从安全性角度来说也一样,不同应用之间无法访问对方的地址,这就大大减少了被攻击的可能。
进程之所以能重新杀到前台来,我觉得跟硬件的进步有很大的关系。硬件资源(尤其是内存)已经可以支持比较多的进程。另外,软件设计上对可靠性和安全性的重视也是使得进程重新受宠的一个重要原因。
多谢文章,多指教啊
Android进程间通信的机制(binder)也挺有意思。原来是Palm的OpenBinder,在Android上重新实现用于lightweight的RPC,用一个kernel module实现数据在进程间的传递。
最近苦恼中,几乎没有办法移植基于pc的应用程序。Android所谓的可靠性是以牺牲功能作为代价的。
Android自然也支持threading。基本就是JavaSE啊。只有GUI和Activity是比较特殊的,其他的跟桌面Java没什么不同。而且process和thread的组合也很灵活。你可以指定几个activity共享一个process。
android多线程,最典型的是facebook,foursquare之类的notification都是基于后台的service. android的问题不是没有多线程,而是service过多,导致性能有问题....
iphone ios3.x单线程, ios4.x伪多线程,这才真是无法移植pc应用。
另外多说一句,从pc移植软件到手机平台是不合潮流的......
16M的memory heap限制在处理图片时让人很郁闷。 而且似乎bitmap的资源释放也有问题....
在server端,早就是这样了,比如fast cgi,
再比如python,python由于自己实现的原因,有个GIL,意思就是说每个python的解释器进程,都有一个全局锁,每个线程在运行之前,都要得到这把锁。这就使得python下面多线程编程没有办法用到多cpu的好处,始终只能用到一个core。
GIL是大家的肉中刺,眼中钉,一票子人想着把这个东西拿掉。不过弄了半天,推出的是一个另类解决方案,就是提供了multiprocessing这个库,直接用process来模拟thread,诸如pool阿,lock之类的东西都一并打包奉送。
cgi,那是多么古老的技术啊。所以我说进程又回来了,而不是出现啊。而且cgi是个协议,跟进程没有必然联系。
说到server端的并发技术,也是很有意思的一个问题。早期的web server是用process,比如apache(当然现在也通过模块支持线程了)。然后threading成为主流。但thread对于server的大量并发请求还是不够细,操作系统只能支持有限的threads,著名的c10k问题就是关于这个的。很多模型出现了,比如SEDA,event-driven。目前event-driven是最流行的,因为event-driven不需要维护thread的context(register, stack等等),少量的thread就可以对付大量的并发请求。很多AIO(异步I/O)的程序库应运而生,比如Java NIO,Netty,libevent(C)。现在特别热火的Node.js(用javascript写的web server)也是event-driven。不过,event-driven在编程上比较难写而且容易出错,是我很讨厌的一种模式。我比较看好Actor/message passing或者CSP之类的并发编程技术,erlang是最早在这方面探索的。基本想法就是并发的粒度进一步减少,这样就不受thread limit的限制。这就是所谓的coroutine。coroutine依靠合作的方式进行切换,需要比较小的stack。coroutine之间的同步可以采用message passing。message passing和share memory是等效的,但相对于thread的传统同步方式,message passing比较容易写,而且不容易出错。目前有很多新的编程语言在探索这种方式,或者改进现有的语言来支持。比如scala,kilim,和我目前最喜欢的语言Go。Go的goroutine就是类似于coroutine的实现,多个goroutine被map到有限的几个线程上去(基本上就等于你机器有的cpu core的数量就可以了)。goroutine之间用channel来传递消息。一个程序可以生成大量的goroutine,这样我们就又可以回到同步,顺序的方式来写server了。所以用Go来写networking的程序是最容易的。所以在server端,基本趋势就是并发的粒度越来越小。我还没有看到有回到process的必要。
至于GIL是python设计的一大缺陷,但这不以为着python是基于进程的,python也是有thread的,只是有GIL这个bottleneck,限制了并发的程度。另外,python 3这个问题好像已经解决了。
聊聊几笔,这些问题似乎都没法说清楚!
我的看法是:虽然新技术如雨后春笋般的冒出来,但说到底事情一点没变。所以的工作还是交给OS的Kernel thread来完成。不管进程也好,线程也罢,最后是操作系统的scheduler在安排一个个的Kernel thread在CPU上运行,没有什么讨巧的地方。
编程语言可以把下面的东西藏起来,给用户友好的开发,运行和debug环境,可以让程序员在更高的一个层次想问题,这一点没有问题。但是到目前为止,我还没有看到一个真正成熟的东西,包括刚刚推出概念的C# 5.0的Asynchronous programming model(Java也差不多)。
还有一点就是高级的程序语言和库函数可以让你站得很高,但是你站得越高,控制力就越小,性能损失就越大,这是计算机界的一个悖论,概莫能外。所以,有时候我还是喜欢往低走。走到地处往往才能有左右逢源,随心所欲的境界。
另外,thread heap是怎么回事?我想你是指thread的stack吧?就我所知,绝大多数的编程语言没有为thread allocate heap的概念,也没有heap context这么一说。所以我猜你一定指的是stack。
写太快,回去一看不忍卒读。把指出的地方修改了一下。其实主要想说明在server端并发粒度越来越小。主要推动力就是web service越来越大的并发规模。
微软的robotics studio都到了第三代了,好几年前的东西了,现在好像都并到什么并行库里去了。记得当时看CCR/DSS的case study,Fedex还谁用它写的一个分类邮件的程序,还有好多华尔街在偷偷地用等等,可惜那个team后来没成气候。也是从CCR/DSS开始才找到erlang上,才发现erlang早就大名鼎鼎了几十年了,呵呵。现在又开始流行F#,Scala了等等,这上面的话题可就多了。
我想你想说的是这两者的关系。我觉得对于大多数程序员来说,高层的编程模式越来越重要。目前新的编程语言如雨后春笋,不断涌现。说明现有的编程模式已经不适合软件应用的需要,大家迫切期待新的技术或者实现的成熟。随便数来就有不少:clojure, scala, erlang, go, D, Rust, Ocaml, Lua...一个潮流就是越来越多采用functional language的概念,另一个潮流就是Actor或者CSP模型。其实这些概念早就都有了,但为什么到现在才开始被重视呢?我想硬件资源越来越丰富,并行度越来越大,而程序也越来越复杂,这些都是重要的推动力。Functional language的特点就是尽量消除side effect,这样程序的可靠性比较好(不展开了,有兴趣问Google吧)。Actor/CSP模型是适应大规模并行的网络服务的特点。整个趋势就是语言提供越来越高层的抽象。
至于控制力,自然是越低越强,用C基本可以实现任何东西。但同时你也要付出巨大的代价来建立一个庞大的系统,很多时候都是reinventing the wheel。基本上,除了一些特殊的领域,比如codec之类的,没有谁会在底层写了,因为基本上不现实。
当然,我的观点而已。:-)
顾非是微软的吗?
可以展开说说。尤其是F#和robotics studio。后面这个东西是否会加入kinect的支持?Scala有一阵看了一下,觉得搞得太复杂了。现在号称要替代Java,我觉得有点跟Java背道而驰(Java还是一个简单好用的语言,就是有点落后于时代了)。
这个是不是与cpu的性能提高方式转变有关,过去cpu的主要通过提高主频的方式提高性能,这种方式,因为线程的开销小,而进程提供的并行优势无法发挥.
现在方法是提高cpu core的数目,进程的并行优势就很明显了,加上你提出的其他的好处.
至少在非移动处理的领域是这样的.
不过对于移动处理,或许会有些不同.