了解Linux的进程与线程

http://timyang.net/linux/linux-process/

上周碰到部署在真实服务器上某个应用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就这个范儿P518)

在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),优点是线程切换快,但实现稍复杂。

  • 信号

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

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

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

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

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

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

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

如想及时阅读Tim Yang的文章,可通过页面右上方扫码订阅最新更新。

« 高性能网站经验-读杨建的Blog有感 | QCon Beijing qconbeijing全部演讲资料下载 »

9 Comments  »

    1. Practicing all over again exercises on the subject of day to day lows will be exceptionally sensible plus states big remedy while in the immediate plus new.

       
    2. 027xiatian

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

      是笔误么?
      终端有前台进程组和后台进程组,默认情况下,终端接收到任何信号都会发到前台进程组, 控制终端对SIGHUP信号没有做特别处理,默认就会退出, nohup实际是创建一个脱离控制终端的守护进程【控制终端产生的SIGHUP它是收不到的】,

      应该是这样的吧

       
    3. 总结的还是不错的

       
    4. linhuatan

      Linux 的僵尸(zombie)进程链接坏了

了解Linux的进程与线程的更多相关文章

  1. [转载]了解Linux的进程与线程

    本文转自Tim Yang的博客http://timyang.net/linux/linux-process/ .对于理解Linux的进程与线程非常有帮助.支持原创.尊重原创,分享知识! 上周碰到部署在 ...

  2. linux查看进程的线程数

    top -H -p $PID  #查看对应进程的那个线程占用CPU过高 1.top -H 手册中说:-H : Threads toggle 加上这个选项启动top,top一行显示一个线程.否则,它一行 ...

  3. 理解Linux的进程,线程,PID,LWP,TID,TGID

    在Linux的top和ps命令中,默认看到最多的是pid (process ID),也许你也能看到lwp (thread ID)和tgid (thread group ID for the threa ...

  4. 深入理解 Linux的进程,线程,PID,LWP,TID,TGID

    转载:https://www.linuxidc.com/Linux/2019-03/157819.htm 在Linux的top和ps命令中,默认看到最多的是pid (process ID),也许你也能 ...

  5. linux内核--进程与线程

    http://blog.csdn.net/yusiguyuan/article/details/12154823 在<linux内核设计与实现>中第三章讲解了进程管理,在关于进程和线程的概 ...

  6. [转] linux 下 进程和线程的区别

    1.进程与线程 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流,是C ...

  7. Linux中进程与线程的概念以及区别

    linux进程与线程的区别,早已成为IT界经常讨论但热度不减的话题.无论你是初级程序员,还是资深专家,都应该考虑过这个问题,只是层次角度不同罢了.对于一般的程序员,搞清楚二者的概念并在工作中学会运用是 ...

  8. linux 将进程或者线程绑定到指定的cpu上

    基本概念 cpu亲和性(affinity) CPU的亲和性, 就是进程要在指定的 CPU 上尽量长时间地运行而不被迁移到其他处理器,也称为CPU关联性:再简单的点的描述就将指定的进程或线程绑定到相应的 ...

  9. linux 下 进程和线程的区别

    1.进程与线程 进程是程序执行时的一个实例,即它是程序已经执行到课中程度的数据结构的汇集.从内核的观点看,进程的目的就是担当分配系统资源(CPU时间.内存等)的基本单位. 线程是进程的一个执行流,是C ...

随机推荐

  1. Material Design系列第六篇——Defining Custom Animations

    Defining Custom Animations //自定义动画 This lesson teaches you to //本节课知识点 Customize Touch Feedback //1. ...

  2. LeetCode 46 Permutations(全排列问题)

    题目链接:https://leetcode.com/problems/permutations/?tab=Description   Problem:给出一个数组(数组中的元素均不相同),求出这个数组 ...

  3. spring-data-redis的事务操作深度解析--原来客户端库还可以攒够了事务命令再发?

    一.官方文档 简单介绍下redis的几个事务命令: redis事务四大指令: MULTI.EXEC.DISCARD.WATCH. 这四个指令构成了redis事务处理的基础. 1.MULTI用来组装一个 ...

  4. CentOS 添加环境变量

      1.修改环境变量需要修改/etc/profile export PATH="$PATH:/usr/src/ruby-1.9.3-p0/ruby:/usr/local/bin/gem&qu ...

  5. Mysql语句优化

    总结总结自己犯过的错,网上说的与自己的Mysql语句优化的想法. 1.查询数据库的语句的字段,尽量做到用多少写多少. 2.建索引,确保查询速度. 3.orm框架自带的方法会损耗一部分性能,这个性能应该 ...

  6. JavaScript通知浏览器,更改通知数目

    http://lab.ejci.net/favico.js/ http://www.zhangxinxu.com/study/201607/web-notifications.html http:// ...

  7. 【CF908E】New Year and Entity Enumeration 位运算+DP

    [CF908E]New Year and Entity Enumeration 题意:给定$M=2^m-1$,我们称一个集合S是好的,当且仅当它满足:1.$\forall a\in S,a\  \ma ...

  8. 关于Jmeter3.0,你必须要知道的5点变化

    2016.5.18日,Apache 发布了jmeter 3.0版本,本人第一时间上去查看并下载使用了,然后群里或同事都会问有什么样变化呢?正好在网上看到一遍关于3.0的文章,但是是英文的.这里翻译一下 ...

  9. Xcode快速排错

    EXTENDS:http://blog.csdn.net/guo_hongjun1611/article/details/8063009 1,模拟器运行完全没问题,真机运行失败. 有时候我们在模拟器上 ...

  10. Penn Treebank

    NLP中常用的PTB语料库,全名Penn Treebank.Penn Treebank是一个项目的名称,项目目的是对语料进行标注,标注内容包括词性标注以及句法分析. 语料来源为:1989年华尔街日报语 ...