现象

经常在linux下开发的人应该都有这样的经验,就是在终端上启动的程序,在关闭终端时,这个程序的进程也被一起关闭了。看下面这个程序,为了使进程永远运行,在输出helloworld后,循环调用sleep:

直接关闭这个终端,在另一个终端上查找该进程,已经找不到了:

这个行为看起来似乎是理所当然的,也符合人的第一感觉:”在终端上启动的程序是属于终端的,所以当关闭终端时,这个终端里的一包裹进程都一起被解决掉了”。但这种说法是不能使一个会思考且充满好奇心的人信服的。

下面我们就从linux进程管理的细节来剖析其根本原因。

终端进程

linux系统是基于进程的,几乎每个命令都可以在相应的目录下找到它们的程序,执行一个命令相当于启动一个或多个程序,终端也不例外,在我centos下面终端对应一个bash程序(不同操作系统终端的bash程序可能不一样),它位于/usr/bin/下面:

每当打开一个终端都会启动一个bash进程,我这里启动了两个终端,可以看到有两个bash进程:

终端进程与启动进程的关系

linux系统里面所有的进程的关系可以看做一个树形结构,系统持续运行,进程的不断启动就是不断fork的过程(fork是linux系统api,作用是复制自己来生成子进程),从系统启动、初始化、登录终端、到执行命令都是生成子进程的过程:

init进程是所有进程的祖先,它的pid(进程id)为1,ppid(父进程id)也为1,因为它没有父进程,系统内的其他进程都是由它或者它的子进程fork而来。

我们在linux上作业的终端对应了一个bash进程,在其上运行的命令和程序都是bash的子进程,或由bash的子进程衍生。

用hw程序验证一下,可以看到hw进程的父进程正好是bash进程:

但这并不能解释为什么终端关闭了在上面运行的程序也跟着退出,因为在linux下,进程之间的关系并不像线程那样,当主线程退出时,子线程一起被强制退出。进程之间没有主次的区别,但有父子关系,而父子进程的运行是相对独立的,一方的退出不会导致另一方退出。

进程session-揭开真相

在linux下,一个session是由一组进程组构成的,每个进程组又由多个进程构成。

在一个bash上运行的程序都归属于一个session(除非特别处理),而这个bash就是这个session的leader。每个session又可以关联一个控制终端(Controlling Terminal)。

图片:

  1. hw进程的ppid=5933,说明父进程为第一个bash,这个bash的父进程为gnome-ternimal进程,gnome-ternimal是centos可视化界面的终端管理进程,每打开一个终端,它都会启动一个bash进程,而用户的命令也是直接由bash进程执行的。
  2. hw程序和第一个bash同属于一个session(sid=5933),这个sid等于bash的pid,所以第一个bash是这个session的leader。
  3. 图片中还显示了bash和hw进程拥有共同的终端设备pts/2,它是一种字符设备,不同于上面提到的gnome-ternimal进程。
  4. 当控制终端(对应gnome-ternimal)检测到终端设备断(对应pts/2)开连接时,会通知设备的控制进程,即发送SIGHUP信号给session leader(对应bash进程)。
  5. bash进程在收到SIGHUP后,将信号发给session下的所有进程,导致用户启动的进程退出。

下面通过strace命令来验证以上结论:

  1. 跟踪hw进程(命令意为跟踪pid为6367的进程上与signal有关的系统调用):

    strace -e trace=signal -p 6367

  2. 跟踪bash进程(命令意为跟踪pid为5933的进程上与signal有关的系统调用):

    strace -e trace=signal -p 5933

  3. 关闭启动hw程序的终端,观察strace输出.

hwd的strace如下,si_pid=5933说明是5933这个进程发了SIGHUP给它,也就是bash进程:

bash的strace略微复杂:

  1. kill(4294960929, SIGHUP)

    kill第一个参数是32位有符号整数,转换成int就是-6367,当参数为负时表示发送给这个数绝对值的进程组,即pgrp=6367的所有进程,在上面的图片中可以看到hw进程正好属于该进程组。

  2. kill(5933, SIGHUP)

    5933是自己的pid,bash在第一次收到SIGHUP时先把信号发给session内其他进程,然后再次发送SIGHUP命令给自己,将自己杀死,后面的si_pid=5933也证实了这一点。

如何让终端关闭时进程不退出

根据上面的结论,要使终端关闭时进程不退出,有以下几种情况:

  1. 用户进程拦截SIGHUP信号。
  2. 用户进程和bash进程不在一个session。

下面依次验证这两种情况

拦截SIGHUP

修改hw程序,忽略SIGHUP信号:

signal(SIGHUP, SIG_IGN);
  • 1
  • 2

执行hw程序,并查看进程,可以看到hw进程和父进程bash:

关闭终端,在另一个终端查看进程:

bash进程已经退出,但hw进程还在,符合预期!!而且hw进程的ppid变成了1,说明hw在父进程bash退出后变成孤儿进程被init进程收养。

新建session&setsid

为了使用户进程和bash不在同一个session,需要调用setsid方法,该方法的作用是新建一个新的session,并使自己成为leader。

// 先fork
int pid = fork();
if(pid > 0){// 父进程, 直接退出return 1;
}else if(pid == 0){// 子进程// 创建新的sessionsetsid();//printf("Hello World!\n");printf("sleeping...\n");while(1){sleep(1);}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

调用setsid前先fork,因为若不fork,hw作为进程组的leader,是不允许重建session的,原因留给读者自己思考。

编译并执行hw,查看进程:

可以看到,相比之前,有几个不同的地方:

  1. 程序启动完,返回终端,hw切换到后台运行。
  2. hw进程的父进程不再是bash,而是init进程。
  3. hw没有关联的终端设备(pts/2)。

关闭终端,看到bash已经消失,但对hw进程没有任何影响:

更简单的方法

  1. setsid命令,用setsid来启动程序,这样就不用修改任何代码也可以做到使启动的进程在新的session中,并且终端关闭时,进程不退出。

    setsid ./hw

  2. nohup命令,被nohup启动的程序会忽略SIGHUP信号。

    nohup ./hw

其他

命令行中&的作用:

./hw &
  • 1
  • 2

&的作用是使程序在后台运行,输入fg命令又可以使程序切换到前台。虽然在后台运行,但并不能保证进程在终端关闭时不退出。

总结

简而言之,终端在关闭时会发送SIGHUP给对应的bash进程,bash进程收到这个信号后首先将它发给session下面的进程,如果你的程序没有对SIGHUP信号做特殊处理,那么进程就会随着终端关闭而退出。

linux终端关闭时为什么会导致在其上启动的进程退出?相关推荐

  1. java 不退出_Java项目不挂断运行,即当账户退出或终端关闭时,程序仍然运行,并附上执行脚本...

    把打包好的jar包上传服务器以后,直接运行java -jar+文件名 命令即可以完成启动. 若需要在公网上如何不挂断开启程序,可以使用nohup java -jar +文件名 &命令,其中no ...

  2. LINUX终端登录时提示 Login incorrect

    1. LINUX终端登录时提示 Login incorrect    问题:(1)通过 VMware虚拟机软件安装的 CentOS 操作系统,在通过选择图形界面窗口 GNOME(建议选择经典模式) 进 ...

  3. linux启动一个进程吗,你知道,当你在 Linux 上启动一个进程时会发生什么嘛?

    原标题:你知道,当你在 Linux 上启动一个进程时会发生什么嘛? 本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时,我惊叹不已 ...

  4. linux如何启动一个进程而不阻塞,当你在 Linux 上启动一个进程时会发生什么? | Linux 中国...

    原标题:当你在 Linux 上启动一个进程时会发生什么? | Linux 中国 本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时 ...

  5. linux启动一个进程吗,当你在Linux上启动一个进程时会发生什么?

    本文是关于 fork 和 exec 是如何在 Unix 上工作的.你或许已经知道,也有人还不知道.几年前当我了解到这些时,我惊叹不已. 我们要做的是启动一个进程.我们已经在博客上讨论了很多关于系统调用 ...

  6. linux终端关闭xmanager,xmanager功能和设置

    一下总结这些资料也是根据网上更改过来的,网上有很多人给的是错的或是版本不同的,浪费了我很长时间,总结了这些希望能对你有用,本人试过3个版本的系统都通过. Xmanager是跨平台的一个软件,win远程 ...

  7. linux终端关闭xmanager,xmanager之linux 解决方法

    xmanger防火墙[@more@] 转载]xManager与防火墙的问题解决过程文章作者:天天 最近部门进了一台V880R,用CDE,放在DMZ区,这样的话,如果要用X-manager访问的话就要涉 ...

  8. Linux终端关闭屏幕显示,使用命令行关闭监视器

    问题描述 我正在我的笔记本电脑上运行Ubuntu Server.屏幕没有理由打开.我一直在尝试使用此命令关闭屏幕: sleep 1 && xset dpms force off 问题是 ...

  9. linux终端关闭xmanager,关于使用Xmanager进行对linux远程桌面控制

    通过xmanager管理linux(本文用red hat linux)需要在linux下做相应的设置以启动服务.主要分为七步来进行设置. 配置linux. 1 打开 /etc/inittab文件,将 ...

最新文章

  1. fedora18 fedora17安装显卡驱动和网卡驱动
  2. 浏览器设置了打开会显示特定网页为什么还是显示2345_Edge没能取代Chrome成为我的主力浏览器...
  3. jQuery复制节点
  4. LeetCode 2110. 股票平滑下跌阶段的数目(滑动窗口)
  5. Java基础入门笔记-构造方法的继承
  6. (十一)Hibernate 高级配置
  7. 雷军:电视机越大才越舒服!
  8. C++的掐拷贝、深拷贝【面向对象程序设计细节】
  9. Portlet MVC框架
  10. 开发板识别不了SD/TF卡
  11. 深信服 SANGFOR 设备密码恢复和配置备份恢复
  12. Liunx安装Ubuntu系统
  13. 领域驱动设计(DDD)之领域专家
  14. 1000w+条中国大陆企业工商注册信息数据集
  15. 重置ubuntu密码
  16. mysql sysvar int_MySQL:如何编写daemon plugin
  17. 马化腾是学计算机的吗,马化腾大学实际上是病毒编写者,经常编写感染计算机的程序...
  18. python里load什么意思_【python】json中load和loads区别
  19. U启动后计算机能看到原系统文件吗,u启动一键急救系统使用
  20. Java 开发工具包 Java SE Development Kit 8/11LTS/15

热门文章

  1. linux里的进程简介
  2. html5录像功能限制时间,HTML5拍照和摄像机功能实战详解
  3. ft服务器设置传输协议,ft服务器设置成主动模式
  4. fst java_java快速序列化库FST
  5. mysql binlog redo_mysql的binlog与redo log
  6. 无法访问netflix服务_Choerodon 的微服务之路(三):服务注册与发现
  7. 西安4年java多少时间_西安学习java一般要多久
  8. java底层 文件操作_JAVA的文件操作【转】
  9. 查看环境列表_Xfce 4.14桌面环境正式发布,想要图形界面又想节省内存?就它了...
  10. 算法—递归实现 C(m,n)