首页 > 系统相关 >前后台进程、孤儿进程和 daemon 类进程的父子关系

前后台进程、孤儿进程和 daemon 类进程的父子关系

时间:2023-09-20 22:44:45浏览次数:38  
标签:daemon Shell 30 父子关系 nginx sleep 进程 bash


回到Shell系列文章大纲


前后台进程、孤儿进程和daemon类进程的父子关系

前台进程、后台进程和进程父子关系

前台进程是占用当前终端的进程,只有该进程执行完成或被终止之后,才会释放终端并将终端交还给shell进程。

例如:

$ sleep 30

执行该命令后,将创建sleep进程,sleep进程是当前bash进程(假如当前的shell为bash)的子进程:

$ pstree -p | grep sleep
     |-bash(31207)---sleep(31800)

在30秒内,sleep进程将占用终端,所以此时的sleep称为前台进程。当睡眠30秒之后,前台进程sleep退出,终端控制权交还给当前shell进程,shell进程可继续向下运行命令或等待用户输入新命令。

如果给命令加上一个&符号,该命令将在后台运行。

$ sleep 30 &

此时,sleep仍然是当前bash的子进程,但是它不会占用终端,而是在后台默默地运行,并且在30秒之后默默的退出。

如果是在一个子Shell环境中运行一个前台进程呢?例如:

$ ( sleep 30 )

执行这个命令时,小括号会开启一个子Shell环境,这相当于当前的bash进程隔离了一个bash运行时环境。sleep进程将在这个新的子Shell环境中运行,sleep仍然是当前bash的子进程。由于它会占用当前的终端,所以它是前台进程。

30秒之后,sleep进程退出,它将释放终端,与此同时,子Shell环境也会随着sleep进程的终止而关闭。

如果不了解子Shell,也可以通过shell脚本来理解,或程序内部使用system()来理解,它们都是提供了一种执行外部命令的运行环境。

例如bash -c 'sleep 30',sleep进程将在该bash进程提供的环境下运行,它是该bash进程的子进程。

再例如shell脚本:

#!/bin/bash

sleep 30

sleep将在这个bash脚本进程提供的环境下运行,它是该脚本进程的子进程。

再例如Perl脚本:

#!/bin/perl

system('sleep 30')

sleep将在这个Perl脚本进程提供的环境下运行。

需注意,编程语言(如Perl)可能提供多种调用外部程序的方式,比如system('sleep 30')system('sleep',30),这两种方式有区别:

举几个例子帮助理解,假设有Perl脚本a.pl,其内三行内容为:

system('sleep',30);               #(1)
system('sleep 30 ; echo hhh');    #(2)
system('sleep 30');               #(3)

对于(1),命令和参数分开,perl将直接调用sleep程序,这时的sleep进程是perl进程a.pl的子进程,且不支持使用管道|、重定向> < >>&&等等属于Shell支持的符号。

$ pstree -p | grep sleep
     |    `-bash(31696)---a.pl(32707)---sleep(32708)

对于(2),perl将调用sh,并将参数sleep 30; echo hhh作为sh -c的参数运行,等价于sh -c 'sleep 30; echo hhh',所以sh进程将是perl进程的子进程,sleep进程将是sh进程的子进程。

$ pstree -p | grep sleep
     |    `-bash(31696)---a.pl(32747)---sh(32748)---sleep(32749)

另外需要注意的是,(2)中的命令是多条命令,而不是简简单单的单条命令,因为识别多条命令并运行它们的能力是Shell解析提供的,所以上面涉及了Shell的解析过程。由于会调用sh命令,所以允许命令中使用Shell特殊符号,比如管道符号。

对于(3),perl本该调用sh,并将sleep 30作为sh -c的参数运行。但此处是一个简单命令,不涉及任何Shell解析过程,所以会优化为等价于system('sleep', 30)的方式,即不再调用sh,而是直接调用sleep,也即sleep不再是sh的子进程,而是perl进程的子进程:

$ pstree -p | grep sleep
     |      `-bash(31696)---a.pl(32798)---sleep(32799)

其实子shell中运行命令和system()运行命令的行为是类似的:

# sleep进程是当前shell进程的子进程
$ (sleep 30)

# 当前shell进程会创建一个子bash进程
# sleep进程和echo进程是该子bash进程的子进程
$ (sleep 30 ; echo hhh)

了解以上插曲后,想必能清晰地理解如下结论:

孤儿进程和Daemon类进程

如果在进程B退出前,父进程先退出了呢?这时进程B将成为孤儿进程,因为它的父进程已经死了

孤儿进程会被PID=1的systemd进程收养,所以进程B的父进程PPID会从原来的进程A变为PID=1的systemd进程。

注意,孤儿进程会继续保持运行,而不会随父进程退出而终止,只不过其父进程发生了改变。

例如,在子Shell中运行后台命令:

$ (sleep 30 &)

因为后台符号&是属于Shell的,所以涉及到shell的解析过程,所以当前bash进程会创建一个子bash进程来解析命令并提供sleep进程的运行环境。

sleep进程将在这个子bash进程环境中运行,但因为它是一个后台命令,所以sleep进程创建成功之后立即返回,由于小括号内已经没有其它命令,子bash进程会立即终止。这意味着sleep将成为孤儿进程:

$ ps -o pid,ppid,cmd $(pgrep sleep) 
   PID   PPID CMD
 32843      1 sleep 30

再比如,Shell脚本内部运行一个后台命令,并且让Shell脚本在后台命令退出前先退出。

#!/bin/bash

sleep 300 &
echo over

当上述脚本运行时,sleep在后台运行并立即返回,于是立即执行echo进程,echo执行完成后脚本进程退出。

脚本进程退出前,sleep进程的父进程为脚本进程,脚本进程退出后,sleep进程成为孤儿进程继续运行,它会被systemd进程收养,其父进程变成PID=1。

当一个进程脱离了Shell环境后,它就可以被称为后台服务类进程,即Daemon类守护进程,显然Daemon类进程的PPID=1。当某进程脱离Shell的控制,也意味着它脱离了终端:当终端断开连接时,不会影响这些进程

需特别关注的是创建Daemon类进程的流程:先有一个父进程,父进程在某个时间点fork出一个子进程继续运行代码逻辑,父进程立即终止,该子进程成为孤儿进程,即Daemon类进程。当然,要创建一个完善的Daemon类进程还需考虑其它一些事情,比如要独立一个会话和进程组,要关闭stdin/stdout/stderr,要chdir到/下防止文件系统错误导致进程异常,等等。不过最关键的特性仍在于其脱离Shell、脱离终端。

为什么要fork一个子进程作为Daemon进程?为什么父进程要立即退出

所有的Daemon类进程都要脱离Shell脱离终端,才能不受终端不受用户影响,从而保持长久运行。

在代码层面上,脱离Shell脱离终端是通过setsid()创建一个独立的Session实现的,而进程组的首进程(pg leader)不允许创建新的Session自立山头,只有进程组中的非首进程(比如进程组首进程的子进程)才能创建会话,从而脱离原会话。

而Shell命令行下运行的命令,总是会创建一个新的进程组并成为leader进程,所以要让该程序成为长久运行的Daemon进程,只能创建一个新的子进程来创建新的session脱离当前的Shell。

另外,父进程立即退出的原因是可以立即将终端控制权交还给当前的Shell进程。但这不是必须的,比如可以让子进程成为Daemon进程后,父进程继续运行并占用终端,只不过这种代码不友好罢了。

换句话说,当用户运行一个Daemon类程序时,总是会有一个瞬间消失的父进程

前面演示的几个孤儿进程示例已经说明了这一点。为了更接近实际环境,这里再用nginx来论证这个现象。

默认配置下,nginx以daemon方式运行,所以nginx启动时会有一个瞬间消失的父进程。

$ ps -o pid,ppid,comm; nginx; ps -o pid,ppid,comm $(pgrep nginx)
   PID   PPID COMMAND
 34126  34124 bash
 34194  34126 ps
   PID   PPID COMMAND
 34196      1 nginx
 34197  34196 nginx
 34198  34196 nginx
 34200  34196 nginx
 34201  34196 nginx

第一个ps命令查看到当前分配到的PID值为34194,下一个进程的PID应该分配为34195,但是第二个ps查看到nginx的main进程PID为34196,中间消失的就是nginx main进程的父进程。

可以修改配置文件使得nginx以非daemon方式运行,即在前台运行,这样nginx将占用终端,且没有中间的父进程,占用终端的进程就是main进程。

$ ps -o pid,ppid,comm; nginx -g 'daemon off;' &
   PID   PPID COMMAND
 34126  34124 bash
 34439  34126 ps     #--> ps PID=34439
[1] 34440            #--> NGINX PID=34440

[~]->$ ps -o pid,ppid,comm $(pgrep nginx)
   PID   PPID COMMAND
 34440  34126 nginx
 34445  34440 nginx
 34446  34440 nginx
 34447  34440 nginx
 34448  34440 nginx

最后,需要区分后台进程和Daemon类进程,它们都在后台运行。但普通的后台进程仍然受shell进程的监督和管理,用户可以将其从后台调度到前台运行,即让其再次获得终端控制权。而Daemon类进程脱离了终端、脱离了Shell,它们不再受Shell的监督和管理,而是接受pid=1的systemd进程的管理。

标签:daemon,Shell,30,父子关系,nginx,sleep,进程,bash
From: https://www.cnblogs.com/f-ck-need-u/p/17718649.html

相关文章

  • 进程注入之ListPlanting——滥用listview控件的消息回调函数
    效果:注入代码到“注册表编辑器”(当然,必须是要有listview这种列表显示才可以执行)  ProcessInjection: ListPlanting Othersub-techniquesofProcessInjection(12)看看官方的介绍Adversariesmayabuselist-viewcontrolstoinjectmaliciouscodeinto......
  • 25_linux c 多进程
    linuxc多进程什么时候用进程&线程?1、需要频繁创建销毁的优先使用线程;因为对进程来说创建和销毁一个进程代价是很大的。2、线程的切换速度快,所以在需要大量计算,切换频繁时用线程,还有耗时的操作使用线程可提高应用程序的响应。3、多进程可以使用在多机分布式系统,需要扩展......
  • MySQL高级11-后台进程
    一、前言MySQL的服务实现通过后台多个线程、内存池、文件交互来实现对外服务的,不同线程实现不同的资源操作,各个线程相互协助,共同来完成数据库的服务。MySQL常用的后台线程概括如下,分为MasterThread,IOThread,PurgeThread,PageCleanerThread二、MasterThread在MySQL......
  • MSSQL 维护小记(清理进程、重建索引)
    ------------------------------清理进程----------------------------------- declare@deleteSleepSessionnvarchar(100)--申明一个变量declaretablelistcursorlocal--申明一个本地游标forselect'kill'+rtrim(spid)frommaster.dbo.sysprocesses--数据库系统进......
  • 【逆向专题】【危!!!刑】(一)使用c#+Win32Api实现进程注入到wechat
    引言自从上篇使用Flaui实现微信自动化之后,这段时间便一直在瞎研究微信这方面,目前破解了Window微信的本地的Sqlite数据库,使用Openssl,以及Win32Api来获取解密密钥,今天作为第一张,先简单写一下,获取微信的一些静态数据,以及将自己写的c语言dll通过Api注入到微信进程里面去,最后......
  • 目标进程已退出,但未引发 CoreCLR 启动事件
    百度之多数是说运行库没有安装。https://www.cnblogs.com/lingxi-ljl/p/17082020.html运行以下命令都能找到相关版本的内容dotnet--list-sdksdotnet--list-runtimes重新创建相同版本的一个项目,可以正常启动。怀疑是net6依赖记录的json文件有问题,于是改一下项目的名称,使生......
  • Linux 安装守护进程supervisor
    1.使用yum安装yuminstallsupervisor2.开机自启动systemctlenablesupervisord.service3.启动supervisorsystemctlstartsupervisord4.导航至相应目录cd/etc/supervisord.d/5.新建进程文件viApp.ini6.修改进程文件配置如下:[program:App]......
  • 线程劫持-进程注入C++示例和检测思考
    线程劫持:运行方法C:\Users\l00379637\source\repos\thread_hijack\x64\Release\thread_hijack.exe18132C:\Users\l00379637\source\repos\injected_dll\x64\Release\injected_dll.dllProcessID:18132Injected!劫持效果: 劫持代码如下:#include<iostream......
  • 【原创】BGP协议的主要进程
    BGP的主要进程在思科路由器上,我们查看对应的BGP协议使用的进程导致CPU及内存利用率,可以用如下命令进行查看(锐捷路由器上不适用) 分别有Scanner进程、I/O进程、Router进程以及其他Scheduler进程、Event进程和Task这三个进程。前三个进程的主要作用如下:Scanner进程:主要是对BG......
  • 强制卸载目标进程模块
    代码来源于网络,卸载模块后通过查询PEB得到进程信息的程序没有得到更新,(如:Windows优化大师和360的进程查看),可以通过冰刃查看。注:强制卸载可能导致目标进程崩溃。哈哈,又有了种结束进程的方式,卸载目标进程的ntdll.dll。下面是代码:classForceQuit{public:boolEnablePriv()......