信号控制

信号控制

一 信号说明

在脚本执行过程中, 可能会被一些键盘操作快捷方式所打断, 影响脚本运行

# HUP(1):  1、挂起信号 2、往往可以让进程重新加载配置
本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。

登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都 属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进 程组和后台有终端输出的进程就会中止。不过,可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也 能继续下载。

此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。

# INT(2):  中断, 通常因为按下ctrl+c而产生的信号,用于通知前台进程组终止进程。
# QUIT(3): 退出,和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。

# TSTP(20): 停止进行运行,通常因为按下ctrl+z而产生的信号

# KILL (9)
用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
# TERM(15): 
终止,是不带参数时kill默认发送的信号,默认是杀死进程,与SIGKILL不同的是该信号可以被阻塞和处理。通常用TERM信号来要求程序自己正常退出,如果进程终止不了,我们才会尝试SIGKILL。
  
# ===============了解===============
# ABRT(6): 中止, 通常因某些严重错误产生的引号   

# SIGCHLD 
子进程结束时, 父进程会收到这个信号。

如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸 进程。这种情 况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程 来接管)。

# 更多详见:man 7 signal
示例–>hup1信号重新加载配置文件
####yum安装vsftpd
[root@localhost ~]# yum install vsftpd -y
####关闭防火墙以及selinux
[root@localhost ftp]# setenforce 0
[root@localhost ftp]# systemctl stop firewalld
###vsftpd有一个文件路径: cd /var/ftp/
[root@localhost ~]# cd /var/ftp/
[root@localhost ftp]# ls
pub
####建立一个文件a.txt
[root@localhost ftp]# echo 123 >>a.txt
[root@localhost ftp]# ls
a.txt  pub
####启动服务
[root@localhost ftp]# systemctl start vsftpd
####查看进程PID
[root@localhost ftp]# ps aux |grep vsftpd 
root       4098  0.0  0.0  53264   572 ?        Ss   17:43   0:00

####现在需要用test的b.txt文件替换a.txt文件
#####创建test下的b.txt文件
[root@localhost ftp]# mkdir /test
[root@localhost ftp]# echo 4444 >>/test/b.txt
####修改vsftpd的配置文件最后面加一行anon_root=/test
[root@localhost ftp]# vim /etc/vsftpd/vsftpd.conf

####hup1重新加载配置文件
[root@localhost ftp]# kill -1 4098
####用浏览器进行查看:ftp://10.0.0.100/
ftp://10.0.0.100/
03/13/2021 09:48上午              5 b.txt



二 捕捉信号

我们可以用trap命令捕捉信号(trap命令并不能捕获所有信号,但是常用信号HUP、INT、QUIT、TERM都是可以捕获的),执行我们规定的操作

操作1:捕捉信号、执行引号内的操作

trap “echo 已经识别中断信号:ctrl+c” INT

示例2:捕捉信号、不执行任何操作

trap “” INT

示例3:也可以同时捕获多个信号

trap “” HUP INT QUIT TSTP
例子
#!/bin/bash

trap "" INT HUP TSTP QUIT

echo $$
sleep 100

####执行查看结束不了
[root@localhost ftp]# vim signal.sh
[root@localhost ftp]# ./signal.sh 
4308
^C^C^C^Z^Z^Z^Z^\^\^\^\^\^\[root@localhost ftp]# ^C

注意:不能捕捉kill信号,如果不需要的话可以直接执行kill-9进行杀死

三、关于HUP信号

1.什么是HUP信号

hup信号除了在上一篇讲的从新加载配置文件功能外还具有另外一种功能
当用户注销(exit, logout, Ctrl + d),或者网络断开时,终端会收到Linux HUP 信号
HUP信号会使其关闭所有子进程, 这样就会关闭你不想关闭的进程
解决方法:
1.让进程忽略Linux HUP信号
2.让进程运行在新的终端里,从而不属于当前终端
2.nohup命令

nohup 顾名思义,就是忽略 hup 信号
nohup 通常与 & 符号连用, 让提交的命令忽略 Linux HUP 信号
用法
nohup使用十分方便,只需在要处理的命令前加上"nohup"即可,一般配合"&"符号将其放入后台
nohup [命令] &

示例:nohup
在终端 a 使用"nohup"运行一条"ping"命令
[root@shawn ~]# nohup ping baidu.com &> /dev/null &

在终端 b 过滤出"ping"进程的信息
[root@shawn ~]# ps -elf | grep [p]ing
4 S root  57838  57801  0  80  0 - 37522 poll_s 19:10 pts/1  00:00:00 ping baidu.com

我们"kill"掉该进程的父进程"57801"(终端 a),并再次查看
[root@shawn ~]# kill -9 57801
[root@shawn ~]# ps -elf | grep [p]ing
4 S root   57838  1  0  80   0 - 37522 poll_s 19:10 pts/1  00:00:00 ping baidu.com
可以发现终端 a 关闭后,其下的子进程并没有关闭,但父进程PID变成了"1","systemd"进程
————————————————
版权声明:本文为CSDN博主「weixin_46837396」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46837396/article/details/114293660
setsid命令

原理与 nohup 一样
setsid 是直接将进程的父进程PID设置成 1
即直接让 systemd 成为该进程的父进程, 那么除非 systemd 结束,该子进程才会结束

  • 用法
# setsid [命令]   ( & 符号可加可不加)

  • 示例
在终端 a 中使用"setsid"命令运行一条"sleep"命令
[root@shawn ~]#setsid sleep 200000

关闭终端 a, 在终端 B 中查看进程信息,发现进程还在运行,并且父进程PID为 "1"
[root@shawn ~]#ps -elf | grep [s]leep
0 S root  63319  1  0  80  0 - 27013 hrtime 20:56 ?  00:00:00 sleep 100000 

————————————————
版权声明:本文为CSDN博主「weixin_46837396」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46837396/article/details/114293660
在子shell中提交任务

什么是子 Shell
就是从当前的的"shell"环境中开的一个新"shell"

  • 用法
# ([命令] &)

示例
在终端 a 中使用"( )"结构运行一条"ping"命令
[root@shawn ~]#(ping baidu.com&>/dev/null &)

关闭终端 a, 在终端 B 中查看进程信息,发现进程还在运行,并且父进程PID为 "1"
[root@shawn ~]#ps -elf | grep [p]ing
4 S root  64003  1  0  80  0 - 37522 poll_s 21:09 pts/1  00:00:00 ping baidu.com

————————————————
版权声明:本文为CSDN博主「weixin_46837396」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_46837396/article/details/114293660
3.3.5 screen命令
# 1、安装
[root@egon ~]# yum install screen -y

# 2、运行命令
方式一:开启一个窗口并用-S指定窗口名,也可以不指定
[root@egon ~]# screen -S egon_dsb 
'''
Screen将创建一个执行shell的全屏窗口。你可以执行任意shell程序,就像在ssh窗口中那样。
在该窗口中键入exit则退出该窗口,如果此时,这是该screen会话的唯一窗口,该screen会话退出,否则screen自动切换到前一个窗口。
'''

方式二:Screen命令后跟你要执行的程序
[root@egon ~]# screen vim test.txt
'''
Screen创建一个执行vim test.txt的单窗口会话,退出vim将退出该窗口/会话。
'''

# 3、原理分析
screen程序会帮我们管理运行的命令,退出screen,我们的命令还会继续运行,若关闭screen所在的终端,则screen程序的ppid变为1,所以screen不会死掉,对应着它帮我们管理的命令也不会退出
测试略

# 4:重新连接会话
在终端1中运行
[root@egon ~]# screen
[root@egon ~]# n=1;while true;do echo $n;sleep 1;((n++));done

[root@egon ~]# 按下ctrl+a,然后再按下ctrl+d,注意要连贯,手别哆嗦,

[root@egon ~]# 此时可以关闭整个终端,我们的程序并不会结束

打开一个新的终端
[root@egon ~]# screen -ls
There is a screen on:
	109125.pts-0.egon	(Detached)
1 Socket in /var/run/screen/S-root.

[root@egon ~]# screen -r 109125  # 会继续运行
[root@egon ~]# 

注意:如果我们刚开始已经用screen -S xxx指定了名字,那么我们其实可以直接
screen -r xxx ,就无须去找进程id了

远程演示

# 在终端1:
[root@egon ~]# screen -S egon_av

# 开启一个新的终端,在该终端执行的命令,终端1会同步显示
[root@egon ~]# screen -x egon_av

四 僵尸进程与孤儿进程

1.什么是僵尸进程

这是Linux出于好心的设计
一个父进程开启了一堆子进程, 当子进程比父进程先运行完(死掉)
操作系统会释放子进程占用的重型资源(内存空间, CPU资源, 打开的文件)
但会保留子进程的关键信息(PID, 退出状态, 运行时间等)
目的是为了让父进程能随时查看自己的子进程信息(不管该子进程有没有死掉)
这种已经死掉的子进程都会进入僵尸状态, '‘僵尸进程’'是Linux系统的一种数据结构
2.僵尸进程回收----概念

操作系统保留子进程信息供父进程查看
当父进程觉得不再需要查看的时候, 会向操作系统发送一个 wait / waitpid 系统调用
于是操作系统再次清理僵尸进程的残余信息
3.僵尸进程回收----实际
优秀的开源软件
这些软件在开启子进程时, 父进程内部会及时调用"wait" / “waitpid” 通知操作系统来回收僵尸进程
水平良好的开发者
功底深厚,知道父进程要对子进程负责
会在父进程内部考虑到调用 “wait” / “waitpid” 通知操作系统回收僵尸进程
但是发起系统调用时间可能慢了一点
于是我们就可以使用 “ps aux | grep [z]+” 命令查看到僵尸进程
水平非常低的开发者
技术半吊子,只知道开子进程,父进程也不结束,并在那一直开子进程,不知道什么是僵尸进程
系统调用 “wait” / “waitpid” 也没有听说过
于是计算机会堆积许多的僵尸进程,占用着大量的"pid",(每启动一个进程就会分配一个"pid号")
计算机进入一个奇怪的现象: 内存够用,硬盘充足,CPU空闲,但新的程序无法启动
这就是因为"PID"不够用了
4.如何清理僵尸进程
针对良好的开发者
我们可以手动发信号给父进程: "# kill -CHLD [父进程的PID]"
通知父进程快点向操作系统发起系统调用 “wait” / “waitpid” 来清理变成僵尸的儿子们
针对半吊子水平的开发者
这种情况子下,我们只能将父进程终结,因为你发给它的信号不会得到回应
父进程被杀死,“僵尸进程"将会变成"僵尸孤儿进程”
但凡是"孤儿进程"都会被Linux系统中"PID"为"1"的顶级进程"systemd"回收
“systemd"会发起系统调用 “wait” / "waitpid” 来通知操作系统清理僵尸进程
Centos7 的顶级进程为 systemd
Centos6 的顶级进程为 init

五、孤儿进程

父进程先死掉,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被进程号为1的顶级进程(init或systemd)所收养,并由顶级进程对它们完成状态收集工作。
进程就好像是一个民政局,专门负责处理孤儿进程的善后工作。每当出现一个孤儿进程的时候,内核就把孤儿进程的父进程设置为顶级进程,而顶级进程会循环地wait()它的已经退出的子进程。这样,当一个孤儿进程凄凉地结束了其生命周期的时候,顶级进程就会代表党和政府出面处理它的一切善后工作。因此孤儿进程并不会有什么危害。

我们来测试一下(创建完子进程后,主进程所在的这个脚本就退出了,当父进程先于子进程结束时,子进程会被顶级进程收养,成为孤儿进程,而非僵尸进程),文件内容

import os
import sys
import time

pid = os.getpid()
ppid = os.getppid()
print ‘im father’, ‘pid’, pid, ‘ppid’, ppid
pid = os.fork()
#执行pid=os.fork()则会生成一个子进程
#返回值pid有两种值:

如果返回的pid值为0,表示在子进程当中
如果返回的pid值>0,表示在父进程当中

if pid > 0:
print ‘father died…’
sys.exit(0)

保证主线程退出完毕

time.sleep(1)
print ‘im child’, os.getpid(), os.getppid()

执行文件,输出结果:
im father pid 32515 ppid 32015
father died…
im child 32516 1

看,子进程已经被pid为1的顶级进程接收了,所以僵尸进程在这种情况下是不存在的,存在只有孤儿进程而已,孤儿进程声明周期结束自然会被顶级进程来销毁。

你可能感兴趣的:(shell编程,linux)