• Feeds

  • Posts Tagged ‘thread’


    关于阙宏宇lighttpd与mod_cache在twitter上的一些讨论

    6月28日的beta技术沙龙上阙宏宇介绍了lighttpdmodcache,视频及slide在 http://club.blogbeta.com/76.html,modcache是阙宏宇开发的一个lighttpd module, 它在部分程度上可以替代squid。在活动的介绍上提到

    lighttpd是基于事件驱动的高性能web服务器。mod_cache是在lighttpd上运行的缓存插件。lighttpd+mod_cache 搭建的缓存系统,具有配置简单,性能高,在很多大型系统得到了广泛应用。本次活动由mod_cache作者阙宏宇介绍lighttpd的基础知识,讲解 lighttpd高性能的原理,讨论lighttpd的扩展编写方法等高级话题。也将分享mod_cache插件的特点和使用经验。

    在看视频的过程中我在twitter上也分享了阙宏宇的一些观点,同时也引起了一些网友的关注及讨论,部分内容摘录如下(下文每行开头的链接是发言人的twitter ID)

    xmpp: 刚看到阙宏宇的lighttpd mod_cache视频,他的调优建议把keep-alive设成=0(相当于关掉),个人不是很认同

    xmpp: 阙宏宇称”使用lighttpd+mod_cache比squid快一个数量级,原因是lighttpd用event/file, squid用thread/db“ 视频第41分钟

    xmpp: 阙宏宇最后说web server使用多线程是倒退,并拿memcached单线程跑得很好来证明。但是我觉得并不能单纯这么考虑,一是多核CPU被浪费,二是web server(io密集, 慢连接)和cache server特性有区别不能简单相提并论。

    SnaiX: @xmpp 我觉得阙宏宇的言论很有问题,首先所谓的event based也是需要较大消耗的,且,squid有n中模式可以选择,包括了所谓的db based files based。

    xmpp: @SnaiX 是阿,他的软件对于某些场景可能有意义,但是大部分情况还是没法代替squid的,视频最后Q&A也看到,比如没法实现合并多个相同资源的并发请求等

    xmpp: @eishn: “对于任务的处理, 不可避免会涉及到 multithreading 的问题” 是 @yingfeng 提到的,在我的理解,“任务”是指为了返回请求,需要消耗cpu及io等待,直至输出内容返回网络这个执行过程

    yingfeng: @xmpp 读过POSA II便可知道event based只是解决了acceptor端的问题,对于任务的处理,不可避免会涉及到multithreading的问题

    xmpp: @SnaiX: 批评多线程者无非就是批评锁,所以1最极端者就用单线程,2退一步者就线程不共享内存,线程间用消息通讯,3普通多线程通常加锁,因此会带来等待锁(其实Semaphore锁对等待线程没什么开销的)及线程切换开销,因此开发多线程程序只要了解底层原理就没什么恐惧的

    xmpp: @eishn 你说的是实现方法是协程吗,我倒很想看到你说的实现方法详细描述。如果是协程,它虽然避免了锁,但还是存在切换开销,而且也不能随时切换,我觉得如要利用多核优势,多线程是大势所趋

    alpha86: @xmpp 静态文件的请求可以开keep-alive, 动态请求的话其实keep-alive起不到多大效果。

    Joshfeng: @xmpp 我个人觉得keep-alive的设置要看服务器上主要是什么类型的文件

    xiaoxiaolu: RT @xmpp: … 他的调优建议把keep-alive设成=0(相当于关掉),个人不是很认同//就web而言,我挺认同的

    mikespook: RT: @xiaoxiaolu: RT @xmpp: 刚看到….调优建议把keep-alive设成=0(相当于关掉),个人不是很认同//就web而言,我挺认同的// 具体情况具体处理,keep-alive 对小文件友好,对高并发不利~

    rockyzh: @xmpp 同意,现在已经进入多核时代了,当年的squid以高效的单线程著称,现在可得改改了

    TerryTsui: RT: @xmpp: 阙宏宇最后说web server使用多线程是倒退,并拿memcached单线程跑得很好来证明。。。 //memcache 也可以是多线程跑啊

    xiaoxiaolu: RT @mikespook: RT: @xiaoxiaolu: RT @xmpp:调优建议把keep-alive设成=0,个人不是很认同//我挺认同// 具体情况具体处理,keep-alive 对小文件友好,对高并发不利~//高并发看情况,若跳出率很高,就关掉,省事的话,一律关掉

    SnaiX: @xmpp 在他视频20分钟左右的位置,提到squid和lighty的比较。提到“能不用线程就不用线程,尤其是几个线程在做同一个事情,用这种事件驱动的模型更好“,我不赞同,这种并发的目的就是用来做同一个事情的。其次就是线程模型和事件驱动一点也不冲突。

    SnaiX: @xmpp memcached的模型明显是比较简单的模型,为了实现方便而已。所以mc这种东西如果你不适用线程模式或者不对其进行一些特殊优化, 是很难用来吞吐大数据的,因为单线程会使得在write较多数据的时候占用的CPU时间太长,从而直接影响了对别的请求的响应。

    tangfl: @SnaiX @xmpp snaix 应该大力推销一下 csf 模式:前端 event driven,中间任务队列调度,后面线程池处理,完美了。。。

    eishn: @xmpp 阙宏宇说webserv使用多线程是倒退,基本赞同。不过其mcachd例子不妥,常规evt负载亦不低。然单线程异步io性能甚于多线程,高并发场合由 是,故多线程非最佳。然实现逻辑,多线程是最易用的,故最佳形式是以模拟多线程写逻辑,而以单线程异步实现,这也是我的做法。

    jiaojing: RT: @jeffz_cn: rt @xmpp RT: @yingfeng: @xmpp 读过POSA II便可知道event based只是解决了acceptor端的问题,对于任务的处理,…//多线程也只是用来做cpu时间片的分配,协程也是一个不错的选择.切换代价更 小

    SnaiX: @xmpp 我认为,如果仅仅是为了避免多线程所造成的程序复杂度上升而在该使用多线程的地方却不使用多线程,是一种无能的表现。譬如说,有言论说:多线程造成你的程序一旦有问题,整个进程就core dump了,所以不稳定。可关键是,你的程序应该有问题么?

    SnaiX: RT @tangfl: @SnaiX @xmpp 其实CSF也没什么可高深的,只是我觉得需要有这么一个东西,这样很多东西就可控了。做技术的,不能整天靠凑合来凑合去的过日子。

    eishn: @xmpp stackless python 协程可以做到随时切换, 不过不能达到 erlang 那样的协程级别的多核分配, 尽管可以由用户自己非原生实现。协程虽然还是有切换开销, 但是没有无谓切换, 只在类似 io 等待的场合下才会发生切换, 所以切换次数远小于线程。

    jiaojing: RT: @eishn: @xmpp stackless python 协程可以做到随时切换, 不过不能达到 erlang 那样的协程级别的多核分配, …。// erlang的smp也是用多线程(一个核一个线程,ps保证)跑多个scheduler.

    flierlu: 多线程领域也是在不停发展的,近几年 lock-free 和 wait-free 算法逐渐成熟,加上线程局部内存池等等技术,只要设计上不出大问题,肯定能超越多进程

    zhuzhaoyuan: @xmpp: @yingfeng: 读过POSA II便可知道event based只是解决了acceptor端的问题,对于任务的处理,不可避免会涉及到multithreading的问题 || 反对,要看服务类型是IO密集型还是计算密集型的,IO密集型的不需要多线程。

    附加说明

    • 在Twitter中,@表示reply某人, 类似email中的答复,RT表示retweet,类似email中的转发。
    • 从上面看到,Twitter在讨论上处理线索功能上还是比较弱,需要重复RT原文来串联上下文,很需要类似gmail那样conversation的功能。
    • 我的twitter ID是xmpp,欢迎大家follow。

    了解Linux的进程与线程

    上周碰到部署在真实服务器上某个应用CPU占用过高的问题,虽然经过tuning, 问题貌似已经解决,但我对tuning的方式只是基于大胆的假设并最终生效了。我更希望更多的求证一下程序背后CPU及OS kernel当时的运作机制。所以我读了一些Linux内核设计与实现及其他一些相关资料,对Linux process的机制与切换有了更多一些体会。本文尽可能条理一点,但由于牵涉点较多,同时自己可能觉得某些点有记录的价值,因此文字可能会零散。

    • 进程状态

    Linux进程的状态比较容易理解,值得注意的是 UNINTERRUPTIBLE 及 ZOMBIE

    TASK_RUNNING
    TASK_INTERRUPTIBLE
    TASK_UNINTERRUPTIBLE 此时进程不接收信号,这就是为什么有时候kill一个繁忙的进程没有响应。
    TASK_ZOMBIE 我们经常 kill -9 pid 之后运行ps会发现被kill的进程仍然存在,状态为 zombie。zombie的进程实际上已经结束,占用的资源也已经释放,仅由于kernel的相关进程描述符还未释放。
    TASK_STOPPED

    • Kernel space and user space

    Kernel space是供内核,设备驱动运行的内存区域。user space是供普通应用程序运行的区域。每一个进程都运行在自己的虚拟内存区域,不能访问其他进程的内存空间。普通进程不能访问kernel space, 只能通过系统调用来间接进行。当系统内存比较紧张时,非当前运行进程user space可能会被swap到磁盘。

    使用命令 pmap -x <pid> 可以查看进程的内存占用信息; lsof -a -p <pid> 可以查看一个进程打开的文件信息。ps -Lf <pid> 可以查看进程的线程数。

    另外procfs也是一个分析进程结构的好地方。procfs是一个虚拟的文件系统,它把系统中正在运行的进程都显现在/proc/<pid>目录下。

    • 进程创建

    进程创建通常调用fork实现。创建后子进程和父进程指向同一内存区域,仅当子进程有write发生时候,才会把改动的区域copy到子进程新的地址空间,这就是copy-on-write技术,它极大的提高了创建进程的速度。

    • Linux的线程实现

    Linux线程是通过进程来实现。Linux kernel为进程创建提供一个clone()系统调用,clone的参数包括如 CLONE_VM, CLONE_FILES, CLONE_SIGHAND 等。通过clone()的参数,新创建的进程,也称为LWP(Lightweight process)与父进程共享内存空间,文件句柄,信号处理等,从而达到创建线程相同的目的。

    Linux 2.6的线程库叫NPTL(Native POSIX Thread Library)。POSIX thread(pthread)是一个编程规范,通过此规范开发的多线程程序具有良好的跨平台特性。尽管是基于进程的实现,但新版的NPTL创建线程的效率非常高。一些测试显示,基于NPTL的内核创建10万个线程只需要2秒,而没有NPTL支持的内核则需要长达15分钟。

    在Linux 2.6之前,Linux kernel并没有真正的thread支持,一些thread library都是在clone()基础上的一些基于user space的封装,因此通常在信号处理、进程调度(每个进程需要一个额外的调度线程)及多线程之间同步共享资源等方面存在一定问题。为了解决这些问题,当年IBM曾经开发一套NGPT(Next Generation POSIX Threads), 效率比 LinuxThreads有明显改进,但由于NPTL的推出,NGPT也完成了相关的历史使命并停止了开发。

    NPTL的实现是在kernel增加了futex(fast userspace mutex)支持用于处理线程之间的sleep与wake。futex是一种高效的对共享资源互斥访问的算法。kernel在里面起仲裁作用,但通常都由进程自行完成。

    NPTL是一个1×1的线程模型,即一个线程对于一个操作系统的调度进程,优点是非常简单。而其他一些操作系统比如Solaris则是MxN的,M对应创建的线程数,N对应操作系统可以运行的实体。(N<M),优点是线程切换快,但实现稍复杂。

    • 信号

    进程接收信号有两种:同步和异步。同步信号比如SEGILL(非法访问), SIGSEGV(segmentation fault)等。发生此类信号之后,系统会立即转到内核陷阱处理程序,因此同步信号也称为陷阱。异步信号如kill, lwp_kill, sigsend等调用产生的都是,异步信号也称为中断。

    kill <pid> 调用的是 SIGTERM, 此信号可以被捕获和忽略。

    kill -9 <pid> 调用的是 SIGKILL, 杀掉进程,不能被捕获和忽略。

    SIGHUP是在终端被断开时候调用,如果信号没有被处理,进程会终止。这就是为什么突然断网刚通过远程终端启动的进程都终止的原因。防止的方法是在启动的命令前加上 nohup 命令来忽略 SIGHUP信号。如 nohup ./startup.sh &

    很多应用程序通常捕获SIGHUP用来实现一些自定义特性,比如通过控制台传递信号让正在运行的程序重新加载配置文件,避免重启带来的停止服务的副作用。可惜的是,在JAVA中没法直接使用这一功能,SUN JVM没有官方的signal支持,尽管它已经可以实现,详情可参看Singals and Java.

    另外有个有趣的现象是 zombie 状态的进程 kill/kill -9 都没有任何作用,这是由于进程本身已经不存在,所以没有相应的进程来处理signal, zombie状态的进程只是kernel中的进程描述符及相关数据结构没有释放,但进程实体已经不存在了。

    关于僵尸进程,也可参看下酷壳上的这篇Linux 的僵尸(zombie)进程,从程序的角度解释了相关原理。