简单的说,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命令
kill
和killall
命令,本质是传递一个信号给别的进程。使用
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
文件