linux进程管理总结

一、进程相关的概念

进程需要了解 进程,父进程,进程组,会话和控制终端的相关概念。

  1. 进程和父进程:每个进程都有父进程,而所有的进程以init进程为根,形成一个树状结构

  2. 进程组:每个进程都会属于一个进程组(process group),每个进程组中可以包含多个进程。进程组会有一个进程组领导进程 (process group leader),领导进程的PID成为进程组的ID (process group ID, PGID),以识别进程组。

    kill给组发送信号进程组号前加负号如:kill -9 -2189

  3. 会话:一个或是多个进程组集合。 进程可以通过调用 pid_t setsid(); 来建立一个新会话,如果调用此函数的进程不是进程组长,就会创建一个新的会话,那么此时会:
    1. 该进程称为会话首进程 (session leader)
    2. 该进程称为进程组组长
    3. 该进程没有控制终端,即使之前有控制终端这种联系也会断掉

      可以使用第三个特性来创建 daemon 进程。 调用 getsid 可以获得会话首进程进程组 pid,也就是会话首进程进程 id。

  4. 控制终端:
    1. 一个会话持有一个控制终端 (controlling terminal),可以是终端设备也可以是伪终端
    2. 建立与控制终端连接的会话首进程被称为控制进程 (controlling process)
    3. 一个会话有多个进程组,允许存在多个后台进程组 (backgroup process group) 和一个前台进程组 (foregroup process group)
    4. 键入终端的中断键 (Ctrl+C) 会发送中断信号给前台进程组所有进程
    5. 键入终端的退出键 (Ctrl+) 会发送退出信号给前台进程组所有进程
    6. 终端或是网络断开会将挂断信号发送给会话首进程

可以看到执行ps -fj结果如下:


UID         PID   PPID   PGID    SID  C STIME TTY          TIME CMD
chen      36829  36825  36829  36829  0 10:56 pts/0    00:00:00 -bash
chen      37247  36829  37247  36829  0 10:57 pts/0    00:00:00 vim
chen      90490  36829  90490  36829  0 11:57 pts/0    00:00:00 ps -fj

其中PID就是进程id,PPID是父进程id,PGID为进程组id,SID为会话ID

二、关闭会话时子进程进程被杀死

终端在关闭时会发送SIGHUP信号给session leader,此处就是bash进程,bash收到后向session内的所有进程发送SIGHUP然后退出。
SIGHUP信号如果为注册处理函数默认行为就是退出。所以会话退出时子进程都被杀死。

解决方案:

  1. 注册SIGHUP信号处理函数:可以在代码中处理或者使用nohup命令(nohup daemon & &>daemon.log)
  2. 重新设置setsid:可以在代码中处理或者使用setsid命令(setsid daemon)

三、nohup的原理

其实很简单就是注册了SIGHUP的一个处理函数,忽略这个信号,然后去执行实际的命令。
源码地址:https://github.com/MaiZure/coreutils-8.3/blob/master/src/nohup.c,nohup的使用也推荐< /dev/null来重定向stdin

关键代码:

   // 注册处理函数
  signal (SIGHUP, SIG_IGN);

  char **cmd = argv + optind;
  //执行实际的代码
  execvp (*cmd, cmd);

四、setsid原理

fork进程之后的子进程共享父进程的很多东西,并且会话组长就是父进程的会长组长,所以会收到来自父进程会话组长的信号。
setsid用余新建一个会话,调用这个函数之后会当当前进程成为进程组组长和会话组组长,那么原来的会话产生的信号便不会发送到这个进程,从而不会受影响。

五、daemon &和守护进程的区别

因为守护进程的实现是用的setsid,所以其实就是setsid和nohup的区别,两者都可以用来防止进程在终端断开的时候被杀死,nohup还需要配合&放入后台运行。区别的的话守护进程已经脱离了终端,不受终端控制,也就没有 了stdin,stdout和stderr,而使用nohup之后的进程还是有一个终端,只是忽略了其中的SIGHUP信号,存在正常的stdin,stdout和stderr,nohup默认将stdout和stderr重定向到了nohup.out。

最佳实践:

  1. 如果是一次性的后台任务,可以使用nohup十分方便
  2. 如果是长期运行的服务,则推荐使用系统的systemd来管理服务
  3. 如果是定时运行的任务则推荐使用cron来运行

六、服务进程为什么要fork两次

首先说明两次不是必须的,有很多程序都采用了一次fork。

第一次:为了调用setsid,这也解释了为什么调用setsid之前需要先fork的原因:
linux规定调用这个函数之前,当前进程不允许是session leader。进程组leader是该进程组的第一个进程,fork出来的进程必定不是第一个,所以可以调用setsid。另外父进程一般直接退出,可以让shell收到进程结束的通知继续执行,而不是等待他结束。

第二次:为了限制进程打开控制终端,只有会话组长能打开控制终端(非必须,相当于加了个限制条件Daemon不需要打开终端)

七、systemd管理daemon

现在很多的linux发行版都采用systemd来代替原来的init程序,systemd提供了很优秀的进程管理功能,我们需要注册服务时可以利用systemd功能,可以参看鸟哥的systemd介绍。

另外补充点内核进程和Systemd进程:
0号进程为内核进程,1号为Systemd进程,其他还有些内核进程在ps命令查看时以[]包裹。具体关系见:LINUX PID 1 和 SYSTEMD

八、僵尸进程

这个定义摘抄自维基百科:在类UNIX系统中,僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致)但在操作系统的进程表中仍然有一个表项(进程控制块PCB),处于"终止状态"的进程。这发生于子进程需要保留表项以允许其父进程读取子进程的exit status:一旦退出态通过wait系统调用读取,僵尸进程条目就从进程表中删除,称之为"回收(reaped)"。

九、进程名字和启动时指定进程名字

kill,ps,top,pstree这些命令都比较熟悉就不再提了。

至于还有一组命令则不是通过进程号而是通过进程名字来操作进程,pkill和killall一样都是通过名字来杀死进程,而pgrep是通过名字来寻找进程。
他们的原理都是通过查找/proc这个内存文件系统。

在启动的时候可以通过exec命令重命名:
bash -c "exec -a myname sleep 500 &"

你可以通过ps -ef|grep myname来查看进程的详细信息

十、source command和./command 和exec命令的区别

通常执行脚本有三种方式

  1. ./command(同sh command)
  2. source command(同. command)
  3. exec command

简单说明下上面三种方式:

第一种其实就是对应了linux的fork系统调用,在执行command时候,command是在子进程中执行的,当前shell等待直到子进程的command运行完毕在返回到当前shell。第二种则是直接在当前的进程中直接执行,执行完继续接受用户输入。第三种则对应了linux的exec系统调用,当前进程的执行流程会转向command,command是在当前进程直接执行,但是执行完之后便会直接退出。

所以我们一般用的是第一和第二两种,这种的主要区别就是开不开新的进程(开进程是要一定开销的),另外因为第二种是在当前进程执行的,所以如果在command中设置了变量,那么相当于在当前进程中设置了变量,所以我们一般是用第一种去执行避免当前进程的变量被污染。

思考:

现在加入你在终端已经运行了一个非常耗时的任务,你按ctrl+z放入了后台,然后利用bg开始任务,因为终端断开就会收到SIGHUP信号,有没有办法忽略这个信号或者终端断开不收到这个信号?

遗留:

进程调试工具:ltrace strace ftrace

参考链接:

  1. Linux进程组和会话
  2. 在线APUE译文
  3. linux终端关闭时为什么会导致在其上启动的进程退出?
  4. What's the difference between nohup and a daemon?
这篇文章就总结到这里,希望能够对你有所帮助!
作者:陈芳志
本文版权归作者和博客园共有,欢迎保留出处转载!

[转帖]linux进程管理总结的更多相关文章

  1. [转帖]十二 个经典 Linux 进程管理命令介绍

    https://www.cnblogs.com/swordxia/p/4550825.html 接了 http referer 头 没法显示图片 可以去原始blog 里面去查看.   随笔- 109  ...

  2. Linux进程管理子系统分析【转】

    本文转载自:http://blog.csdn.net/coding__madman/article/details/51298732 Linux进程管理: 进程与程序: 程序:存放在磁盘上的一系列代码 ...

  3. 12个Linux进程管理命令介绍(转)

    12个Linux进程管理命令介绍 [日期:2015-06-02] 来源:Linux中国  作者:Linux [字体:大 中 小]   执行中的程序在称作进程.当程序以可执行文件存放在存储中,并且运行的 ...

  4. Linux进程管理知识整理

    Linux进程管理知识整理 1.进程有哪些状态?什么是进程的可中断等待状态?进程退出后为什么要等待调度器删除其task_struct结构?进程的退出状态有哪些? TASK_RUNNING(可运行状态) ...

  5. Linux性能及调优指南(翻译)之Linux进程管理

    本文为IBM RedBook的Linux Performanceand Tuning Guidelines的1.1节的翻译原文地址:http://www.redbooks.ibm.com/redpap ...

  6. Linux进程管理专题

    Linux进程管理 (1)进程的诞生介绍了如何表示进程?进程的生命周期.进程的创建等等? Linux支持多种调度器(deadline/realtime/cfs/idle),其中CFS调度器最常见.Li ...

  7. Linux进程管理 (1)进程的诞生

    专题:Linux进程管理专题 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 L ...

  8. Linux进程管理 (2)CFS调度器

    关键词: 目录: Linux进程管理 (1)进程的诞生 Linux进程管理 (2)CFS调度器 Linux进程管理 (3)SMP负载均衡 Linux进程管理 (4)HMP调度器 Linux进程管理 ( ...

  9. Linux进程管理 (7)实时调度

    关键词:RT.preempt_count.RT patch. 除了CFS调度器之外,还包括重要的实时调度器,有两种RR和FIFO调度策略.本章只是一个简单的介绍. 更详细的介绍参考<Linux进 ...

随机推荐

  1. 几种最常见的js array操作方法及示例

    1. 序言 操作array可谓前端最基础的工作,无论是从接口中取的数据,还是筛选数据,或者是添加按钮权限等等操作,array都是绕不开的东西.array的操作很多,初学者十分容易搞混,不是很熟练的情况 ...

  2. vue 中监听窗口发生变化,触发监听事件, window.onresize && window.addEventListener('resize',fn) ,window.onresize无效的处理方式

    // 开始这样写,不执行 window.onresize = function() { console.log('窗口发生变化') } // 改成window监听事件 window.addEventL ...

  3. 服务端获取参数(koa)

    1.获取query(问号后面的内容) ctx.query 2.获取路由参数(如'/user/:id'的id) ctx.param 3.获取body请求体 koa不能直接获取请求体里的body,需要安装 ...

  4. HGOI 20190708 题解

    Problem A 拿出勇气吧 幸运数字每一位是$4$或者$7$,现在给出一个数字每位数位上数的和为n,求出最小的幸运数n 对于100%的数据,$n\leq 10^6$ Sol : 显然本题要求数的长 ...

  5. max函数结合lambda使用

    说明:d.keys() 以及列表可以看做lambda函数的实参,max的判断对象是key的值.最终返回的是使得key的值最大的那个实参.

  6. Oracle根据列中的特殊符号进行分组

    原数据: 目标结果: 根据-符号将数据进行分组,思路是根据NAME列值进行复制,若为‘-’则赋值1,其他为0,这样就可以根据累加实现分组, 具体实现代码: /* Formatted on 2019/9 ...

  7. Java中for each与正常for循环效率对比

    循环ArrayList时,普通for循环比foreach循环花费的时间要少一点:循环LinkList时,普通for循环比foreach循环花费的时间要多很多. 当我将循环次数提升到一百万次的时候,循环 ...

  8. C++入门经典-例5.18-通过引用交换数值

    1:在C++中,函数参数的传递方式主要有两种,即值传递和引用传递.值传递是指在函数调用时,将实际参数的值赋值一份传递到调用函数中,这样如果在调用函数中修改了参数的值,其改变将不会影响到实际参数的值.而 ...

  9. LeetCode----两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 示例: 给定 1->2->3->4, 你应该返回 2->1->4->3. 说明: 你的算法只能使用常数的 ...

  10. yum安装Docker

    特别提示:本人博客部分有参考网络其他博客,但均是本人亲手编写过并验证通过.如发现博客有错误,请及时提出以免误导其他人,谢谢!欢迎转载,但记得标明文章出处:http://www.cnblogs.com/ ...