Linux中的信号——shell编程学习_八

Linux中的信号

简单的说,Linux shell中的信号是解决如何对运行中的脚本进行干预以及如何控制脚本运行的时机的一种解决方法。

Linux中的信号

什么是信号?类似于cpu中中断机制,用来进行进程之间的异步通信。由一个进程发出,由另一个进程接收并处理。

使用man命令对signel信号进行查看。下面是部分常用的信号含义:

信号 名称 描述
1 SIGHUP 挂起进程
2 SIGINT 终止进程
3 SIGQUIT 停止进程
9 SIGKILL 无条件终止进程
15 SIGTERM 尽可能终止进程
17 SIGSTOP 无条件停止进程,不时终止
18 SIGTSTP 停止或暂停进程,但不止进程
19 SIGCONT 继续运行停止的进程

产生信号的方式:

  • 代码实现

  • 键盘组合键

    信号名 快捷键 含义
    SIGINT Ctrl+C 终止当前进程
    SIGSTP Ctrl+Z 暂停当前进程
  • shell命令

    killkillall命令,本质是传递一个信号给别的进程。

    使用man kill查看kill信息。

使用示例:


[root@shell mnt]# ping 172.25.254.111       //使用ping命令,然后`ctrl+z`暂停进程
PING 172.25.254.111 (172.25.254.111) 56(84) bytes of data.
64 bytes from 172.25.254.111: icmp_seq=1 ttl=64 time=0.176 ms
64 bytes from 172.25.254.111: icmp_seq=2 ttl=64 time=0.191 ms
^Z
[1]+  Stopped                 ping 172.25.254.111
[root@shell mnt]# ps axu |grep ping //使用ps命令可以看到ping命令还存在,进程PID为1068
root      1068  0.0  0.1 103248   708 pts/0    T    21:48   0:00 ping 172.25.254.111
root      1070  0.0  0.1 103244   832 pts/0    S+   21:48   0:00 grep ping
[root@shell mnt]# kill -9 1068      //使用kill命令,-9为选项表示无条件杀死 PID用来作为进程标识,表示用来操作哪个进程
[root@shell mnt]# ps axu |grep ping //进程消失
root      1072  0.0  0.1 103244   832 pts/0    S+   21:48   0:00 grep ping
[1]+  Killed                  ping 172.25.254.111

程序中信号处理

默认方式

遵循信号原本的含义进行处理。如果不指定任何的信号操作,系统会默认按照默认方式处理信号

忽略信号

自定义方式

  • 自定义方式捕捉处理信号

trap命令用来捕捉信号

脚本示例:


[root@shell signal]# cat trap_1.sh 
#!/bin/bash

trap "echo signal traped signal for ctrl+c" SIGINT  //trap命令使用方法 trap + "命令" + 信号关键字

count=1
while [ $count -le 10 ]     //当count小于10时,进入循环
do
    echo "loop # $count"        //输出数字
    sleep 2
    count=$[ $count + 1 ]       //数字自增
done
echo "loop done"        //输出loop done

执行测试:

可以看到当我们再在键盘上键入crtl+c时,已经不能中断进程,因为脚本中trap的定义捕获了ctrl+c


[root@shell signal]# bash trap_1.sh 
loop # 1
loop # 2
^Csignal traped signal for ctrl+c       //键盘键入一次crtl+c 脚本继续执行,并输出捕获内容
loop # 3
loop # 4
^Csignal traped signal for ctrl+c
loop # 5
loop # 6
loop # 7
loop # 8
loop # 9
loop # 10
loop done

trap也能捕捉到程序的退出状态

具体用法,是将上述脚本的SINGAL关键字改称EXIT

示例:


[root@shell signal]# cat trap_2.sh 
#!/bin/bash

trap "echo 'get exit status'" EXIT      //捕获退出状态

count=1
while [ $count -le 3 ]
do
    echo "loop # $count"
    sleep 2
    count=$[ $count + 1 ]
done
echo "loop done"


[root@shell signal]# bash trap_2.sh     //执行脚本
loop # 1
loop # 2
loop # 3
loop done
get exit status         //最后,成功捕捉到trap的退出状态。因为捕获到,所以输出这一行

在脚本中对已经添加的trap进行移除,即不想让trap在某一块继续捕获处理信号。则可以:

在执行捕捉后,代码添加trap - SIGNAL/EXIT即可。

示例:


[root@shell signal]# cat trap_3.sh 
#!/bin/bash

trap "echo 'get exit status'" EXIT  //标识捕获EXIT状态

count=1
while [ $count -le 3 ]
do
    echo "loop # $count"
    sleep 2
    count=$[ $count + 1 ]
done    
trap - EXIT             //在此声明,不再捕获EXIT状态,实现移除trap捕获

echo "loop done"

测试:


[root@shell signal]# bash trap_3.sh 
loop # 1
loop # 2
loop # 3
loop done
//可以看到最后没有捕捉EXIT状态

后台运行脚本

什么是后台运行脚本:即不运行在终端显示器上的进程,称之为后台进程,控制进程的脚本叫后台脚本。

命令格式:SCRIPT &

示例:


[root@shell signal]# bash bg_1.sh &
[2] 1214        //1214是进程的PID,2是作业号,即当前执行的任务
[root@shell signal]# loop # 1
loop # 2
loop # 3

[2]-  Done                    bash bg_1.sh  //键入回车键,才会显示出来,程序已经结束   

这里出现个作业号概念**

作业是用户提交给系统的一个任务。作业号就是任务的标识。

进程和作业的区别:

区别:进程是一个程序在一个数据集上的一次执行,而作业是用户提交给系统的一个任务。

关系:一个作业通常包括几个进程,几个进程共同完成一个任务,即作业。

用户提交作业以后,当作业被调度,系统会为作业创建进程,一个进程无法完成时,系统会为这个进程创建子进程。

同时运行多个进程:

[root@shell signal]# bash bg_1.sh &     //第一次运行脚本
[2] 1218        //第一个进程:PID,1218;作业号,2
[root@shell signal]# loop # 1
bash bg_1.sh &      //第二次运行脚本
[3] 1220        //第二个进程:PID,1220;作业号,3
[root@shell signal]# loop # 1
bash bg_1.sh &loop # 2  //第三次运行脚本,**注意**这里的loop # 2是上面进程的输出

[4] 1223            //第三个进程:PID,1223;作业号,4
[root@shell signal]# loop # 1
bash bg_1.sh &loop # 2  //第四次运行脚本

[5] 1226            //第四个进程:PID,1226;作业号,5
[root@shell signal]# loop # 1
loop # 3
loop # 2
loop # 3
loop # 2
loop # 3
loop # 3

[2]   Done                    bash bg_1.sh      //四个进程一次结束
[3]   Done                    bash bg_1.sh
[4]   Done                    bash bg_1.sh
[5]-  Done                    bash bg_1.sh

虽然,上述例子,在前台有输出。但是不占用当前终端的命令行,整个进程是在后台运行的。jobs可以查看作业号和状态。

将停止进程调到前台继续运行示例:


[root@shell signal]# jobs       //使用jobs查看到一个停止的进程
[1]+  Stopped                 bash bg_1.sh
[root@shell signal]# fg 1   //`fg 作业号`将停止的指定作业号的进程重新在前台启动;如果要在后台使用`bg`
bash bg_1.sh
loop # 2
loop # 3

将停止进程调到后台继续运行示例:


[root@shell signal]# bash trap_2.sh 
loop # 1
^Z          //执行trap_2.sh后,使用ctrl+z组合键,暂停进程
[1]+  Stopped                 bash trap_2.sh
[root@shell signal]# jobs 
[1]+  Stopped                 bash trap_2.sh
[root@shell signal]# bg 1       //back ground,使用bg 1命令唤醒上面暂停的进程,并后台执行进程
[1]+ bash trap_2.sh &
[root@shell signal]# loop # 2
loop # 3
loop done
get exit status

[1]+  Done                    bash trap_2.sh

作业的优先级:

可以通过数字({-20,19})来指定作业优先级,-20最高,19最低

nice命令可以进行优先级的设定,renice命令可以进行优先级的重新设定

示例:


[root@shell signal]# nice -n 10 bash trap_1.sh > fsx.txt & 
[1] 1264        //创建一个进程,优先级设置为10,-n指定优先级
[root@shell signal]# ps -al     //查看进程
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S     0  1264  1016  0  90  10 - 26516 wait   pts/0    00:00:00 bash
0 S     0  1270  1264  0  90  10 - 25227 hrtime pts/0    00:00:00 sleep
4 R     0  1271  1016  0  80   0 - 27032 -      pts/0    00:00:00 ps
//可以看到进程的优先级(NI)为10
[root@shell signal]# nice -n 11 bash trap_1.sh > fsx.txt & 
[2] 1274        //创建一个新的进程,优先级设置为11
[root@shell signal]# ps -al //查看进程,出现一个10优先级进程(还没有运行结束),和11优先级的进程
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S     0  1264  1016  0  90  10 - 26516 wait   pts/0    00:00:00 bash
0 S     0  1274  1016  0  91  11 - 26516 wait   pts/0    00:00:00 bash
0 S     0  1275  1274  0  91  11 - 25227 hrtime pts/0    00:00:00 sleep
0 S     0  1276  1264  0  90  10 - 25227 hrtime pts/0    00:00:00 sleep
4 R     0  1277  1016  0  80   0 - 27032 -      pts/0    00:00:00 ps

[root@shell signal]# ps -al     //运行结束,执行ps命令可以看到两个进程结束
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 R     0  1289  1016  0  80   0 - 27024 -      pts/0    00:00:00 ps
[1]-  Done                    nice -n 10 bash trap_1.sh > fsx.txt
[2]+  Done                    nice -n 11 bash trap_1.sh > fsx.txt

注意使用nice命令指定优先级有用户权限限制,普通用户并不能指定优先级过高的nice值(一般来说,普通用户最高可以指定的优先级为0)

如何改变正在运行的程序的优先级?使用renice命令,使用方法:

renice 级别号 -p PID

级别号为要更改的级别

-p选项执行操作的进程的PID

PID为指定的进程PID号

更改优先级示例:

[root@shell signal]# nice -n 11 bash trap_1.sh > fsx.txt &  //开始使用nice设置优先级为11并运行
[1] 1290
[root@shell signal]# ps -al
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S     0  1290  1016  0  91  11 - 26516 wait   pts/0    00:00:00 bash
0 S     0  1291  1290  0  91  11 - 25227 hrtime pts/0    00:00:00 sleep
4 R     0  1292  1016  0  80   0 - 27032 -      pts/0    00:00:00 ps
[root@shell signal]# renice 10 -p 1290  //renice重新设置上面进程的优先级
1290: old priority 11, new priority 10  //提示旧优先级,和新优先级
[root@shell signal]# ps -al
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
0 S     0  1290  1016  0  90  10 - 26516 wait   pts/0    00:00:00 bash
0 S     0  1300  1290  0  90  10 - 25227 hrtime pts/0    00:00:00 sleep
4 R     0  1301  1016  0  80   0 - 27032 -      pts/0    00:00:00 ps

[root@shell signal]# ps -al
F S   UID   PID  PPID  C PRI  NI ADDR SZ WCHAN  TTY          TIME CMD
4 R     0  1315  1016  0  80   0 - 27024 -      pts/0    00:00:00 ps
[1]+  Done                    nice -n 11 bash trap_1.sh > fsx.txt

renice和nice类似,普通用户也不能设置比0更高的优先级

nohup命令

不挂断地运行命令(即当前的控制台关闭,从控制台执行的后台命令不结束),这样的目的需要使用nohup命令

示例:

[root@shell signal]# nohup bash trap_1.sh &     //在脚本执行前添加nobup命令,
[1] 1401
[root@shell signal]# nohup: ignoring input and appending output to `nohup.out'  //会有这样的提示
        //键入回车,跳出trap_1.sh
[root@shell signal]# ls     //使用ls命令查看,有一个nohup.out文件,里面内容就是脚本执行的输出
bg_1.sh  fsx.txt  nohup.out  trap_1.sh  trap_2.sh  trap_3.sh
[root@shell signal]# cat nohup.out  //nohup.out全部内容
loop # 1
loop # 2
loop # 3
loop # 4
loop # 5
loop # 6
loop # 7
loop # 8
loop # 9
loop # 10
loop done
//将后台脚本和控制台分离,不互相影响

定时运行脚本

在日常的服务器维护、应用程序维护时,需要定时的日志清理和操作。有一个定时的机制,来运行脚本实现目的。

这里可以指定时间方式运行

at命令

at命令用于在指定时间执行命令。at允许使用一套相当复杂的指定时间的方法。可以用相对时间法指定,也可以用绝对时间法指定。

这里使用到at命令,具体格式:

at [ -f filename ] time

时间格式(部分):

10:12

10:12~PM

now、noon、midnight、teatime

MMDDYY、MM/DD/YY、MM.DD.YY

Jul 14、DEC 22

now+24min

默认情况下,at命令会将标准输入和标准错误以邮件的方式发送给用户。在日常操作,可以标准输出重定向

示例:


[root@shell signal]# cat at_1.sh 
#!/bin/bash

exec 1>>atresult        //将标准输出重定向到当前目录的atresult文件中

echo "script run at `date`" //输出时间
echo "end script"           //输出脚本运行结束

测试1:


[root@shell signal]# at -M -f at_1.sh 00:41 //-M指定不邮件,-f指定执行的脚本文件
job 10 at 2018-04-17 00:41
[root@shell signal]# date       //当前系统时间为凌晨12点40分,为了实验效率,这里设定at命令执行在12点41
Tue Apr 17 00:40:39 EDT 2018
[root@shell signal]# cat atresult   //刚开始atresult文件中没有内容

[root@shell signal]# date
Tue Apr 17 00:40:49 EDT 2018
[root@shell signal]# cat atresult       //到12点41后,执行了at命令,atresult文件中被输出内容,即脚本执行结果

script run at Tue Apr 17 00:41:00 EDT 2018
end script

测试2:


[root@shell signal]# rm -rf atresult        //删除上次的输出结果文件
[root@shell signal]# at -M -f at_1.sh now+1min      //设置当前时间一分钟后执行at命令
job 11 at 2018-04-17 00:43
[root@shell signal]# cat atresult           //当前没有该文件
cat: atresult: No such file or directory
[root@shell signal]# cat atresult           //等待一分钟后,查看atresult内容
script run at Tue Apr 17 00:43:00 EDT 2018
end script

cron时间表

CRONTAB具体原理及使用方法在之前博客有记录,在此不过多赘述。

cron的问题:cron假系统7*24运行,但如果在cron指定的时间内,没有开机,就无法执行计划任务。

要解决这个问题,就要使用异步cron,即anacron,配置文件/etc/anacrontab

# /etc/anacrontab: configuration file for anacron

# See anacron(8) and anacrontab(5) for details.

SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22

#period in days   delay in minutes   job-identifier   command
1       5       cron.daily              nice run-parts /etc/cron.daily
7       25      cron.weekly             nice run-parts /etc/cron.weekly
@monthly 45     cron.monthly            nice run-parts /etc/cron.monthly
//这里也读取的时cron的配置

启动时运行脚本

启动时运行分为:

  • 系统启动时运行:Linux启动时运行

  • shell启动时运行:当打开一个bash时启动的脚本

系统启动时

计算机启动过程在以前简单介绍过,不多赘述,这里主要讨论怎么在系统启动时,运行脚本。

自定义开机运行脚本,不同Linux发行版配置文件不同:

  • debian /etc/init.d/rc.local

  • Ubuntu /etc/rc.local

  • openSUSE /etc/init.d/boot.local

  • CentOS /etc/rc.d/rc.local

  • redhat /etc/rc.d/rc.local

本机时redhat6.5,查看/etc/rc.d/rc.local配置文件“:


#!/bin/sh
#
# This script will be executed *after* all the other init scripts.
# You can put your own initialization stuff in here if you don't
# want to do the full Sys V style init stuff.

touch /var/lock/subsys/local

出错如何查看:将我们需要添加的命令放到改文件内。为了防止配置时出错,可以将标准输出和标准错误重定向到不同的文件中。

一般情况是,在外面写好脚本,在rc.locl文件中进行调用。

shell启动

这里要明确启动时,调用了那几个shell配置文件:

  • 启动本地bash

调用/etc/profile/host/用户/.bashrc

  • 通过ssh登陆

调用/etc/profile/host/用户/.bashrc

  • 通过ssh执行命令

不会经过login过程,即不会调用/etc/profile文件

猜你喜欢

转载自blog.csdn.net/fsx2550553488/article/details/79974217