trap 的用法 /etc/init.d/rcS trap :1 2 3 24

本文介绍了如何通过信号处理来防止Shell脚本被中断,特别是当脚本作为特定用户的默认Shell时,确保用户不能通过常规手段(如Ctrl+C)中断脚本执行的方法。文章列举了系统中的信号名称,并详细说明了如何忽略特定信号。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载地址:https://www.cnblogs.com/leaven/archive/2011/03/16/1986275.html

在有些情况下,我们不希望自己的shell脚本在运行时刻被中断,比如说我们写得shell脚

本设为某一用户的默认shell,使这一用户进入系统后只能作某一项工作,如数据库备份, 我
们可不希望用户使用ctrl+C之类便进入到shell状态,做我们不希望做的事情。这便用到了信号
处理。
kill -l可以列出系统的信号名称,如下:
-bash-3.00# kill -l
1) SIGHUP    2) SIGINT    3) SIGQUIT   4) SIGILL
5) SIGTRAP   6) SIGABRT   7) SIGBUS    8) SIGFPE
9) SIGKILL   10) SIGUSR1   11) SIGSEGV   12) SIGUSR2
13) SIGPIPE   14) SIGALRM   15) SIGTERM   17) SIGCHLD
18) SIGCONT   19) SIGSTOP   20) SIGTSTP   21) SIGTTIN
22) SIGTTOU   23) SIGURG   24) SIGXCPU   25) SIGXFSZ
26) SIGVTALRM  27) SIGPROF   28) SIGWINCH  29) SIGIO
30) SIGPWR   31) SIGSYS   34) SIGRTMIN  35) SIGRTMIN+1
36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5
40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9
44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13
52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9
56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5
60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1
64) SIGRTMAX
-bash-3.00#
通常我们需要忽略的信号有四个,即:HUP, INT, QUIT, TSTP,也就是信号1, 2, 3, 24
使用这样的语句可以使这些中断信号被忽略:
trap "" 1 2 3 24 或 trap "" HUP INT QUIT TSTP
用 trap :1 2 3 24 或 trap HUP INT QUIT TSTP使其回复默认值。
用stty -a可以列出中断信号与键盘的对应,分别执行上面的命令后,运行
tail -f /etc/passwd, 然后尝试用键盘中断,试试两种情况(默认和忽略)下有何不同。
更方便的是我们可以用在shell中用trap定义我们自己的信号处理程序,就象在c中用
signal一样,
如:trap “echo 'GO Away'” INT
<think>我们被要求通过/etc/init.d配置进程在崩溃后自动重启。注意,/etc/init.d是传统的System V init脚本存放目录,而现代Linux系统大多使用systemd。但是,用户明确要求使用/etc/init.d配置,因此我们将使用传统的init脚本来实现。 传统init脚本中,我们可以通过编写一个守护进程的init脚本,并在其中循环检查进程状态,如果进程退出则重新启动它。 步骤如下: 1. 创建一个init脚本,放在/etc/init.d/目录下,例如:/etc/init.d/my_service 2. 在脚本中,我们需要实现start, stop, restart等基本功能。 3. 为了实现崩溃后自动重启,我们可以在启动服务时使用一个循环,当进程退出时重新启动它。 但是注意:传统的init脚本通常不会内置自动重启功能,因此我们需要在启动命令中使用一个循环来监控和重启。 示例脚本思路: 我们可以使用一个while循环,在循环中启动进程,并等待其退出。如果进程退出,则根据退出状态决定是否重启(例如,非正常退出时重启)。为了避免进程崩溃后频繁重启导致系统负载过高,可以加入一定的延时。 以下是一个示例脚本: ```bash #!/bin/bash # chkconfig: 2345 20 80 # description: My custom service # 进程的可执行文件路径 SERVICE_EXEC="/path/to/your/executable" # 进程名(用于检查进程是否存在) SERVICE_NAME="your_service_name" # 日志文件(可选) LOG_FILE="/var/log/my_service.log" start() { echo "Starting $SERVICE_NAME ..." # 使用一个循环来启动服务,如果服务退出则重新启动 while true; do $SERVICE_EXEC >> $LOG_FILE 2>&1 # 如果程序正常退出(退出码0)则不重启,否则重启 if [ $? -eq 0 ]; then echo "Service $SERVICE_NAME exited normally. Restart is not needed." exit 0 else echo "Service $SERVICE_NAME crashed. Restarting in 5 seconds..." sleep 5 fi done } stop() { echo "Stopping $SERVICE_NAME ..." # 使用进程名来停止,注意:这里假设只有一个进程实例 pkill -f "$SERVICE_NAME" } restart() { stop start } case "$1" in start) start ;; stop) stop ;; restart) restart ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 esac exit 0 ``` 注意: 1. 脚本开头的`chkconfig`行用于设置服务在哪些运行级别启动(2345),以及启动和关闭的顺序(20和80)。 2. 这个脚本中的`start`函数会一直循环运行,每次进程崩溃后等待5秒重启。如果进程正常退出(退出码0),则循环也会退出,这意味着服务将停止。但通常服务进程应该是持续运行的,所以正常退出可能意味着正常停止,因此我们不再重启。 3. 但是,如果服务是设计为一次性运行然后退出的,那么这种循环重启就不合适。所以这个脚本适用于持续运行的服务(如守护进程)。 4. 停止服务时,我们使用`pkill`根据进程名停止。注意,这可能会停止同名的其他进程,所以最好确保进程名唯一。 安装脚本: 1. 将脚本保存到/etc/init.d/(例如:/etc/init.d/my_service) 2. 设置可执行权限:`chmod +x /etc/init.d/my_service` 3. 使用chkconfig添加服务(如果系统支持chkconfig): ```bash chkconfig --add my_service ``` 4. 启动服务:`service my_service start` 或 `/etc/init.d/my_service start` 但是,这种方法有一个缺点:如果系统重启,init系统会启动这个脚本,然后脚本会进入循环启动服务。但是停止服务时(比如系统关机),init系统会发送stop命令,而我们的stop命令会通过pkill来停止进程。但是,由于我们的start函数是在一个循环中,并且运行在前台,所以init系统在发送stop命令时,实际上会先停止服务进程,然后脚本(start函数)会检测到进程退出,并尝试重启。这显然不是我们想要的。 因此,我们需要在脚本中增加一个机制,使得在系统关机时,stop命令能够停止整个脚本(包括循环)。我们可以考虑在start函数中,当接收到停止信号时,退出循环。 改进方法: 我们可以使用一个标记文件或者利用进程信号。这里我们可以使用一个陷阱(trap)来捕获停止信号,然后退出循环。 但是,在传统的init脚本中,当init系统停止服务时,它会向整个进程组发送TERM信号。我们的start函数是在子shell中运行的吗?实际上,整个脚本在运行start函数时是在一个单独的进程中,但循环是在这个进程中运行的。所以,我们需要确保在stop命令被调用时,能够终止这个循环。 然而,在stop函数中我们只是杀死了服务进程,并没有杀死启动脚本的循环。因此,我们需要记录start函数运行时的进程ID,并在stop函数中杀死这个循环进程。 修改后的脚本: ```bash #!/bin/bash # chkconfig: 2345 20 80 # description: My custom service SERVICE_EXEC="/path/to/your/executable" SERVICE_NAME="your_service_name" LOG_FILE="/var/log/my_service.log" # 保存启动循环的进程ID的文件 PID_FILE="/var/run/my_service_wrapper.pid" start() { echo "Starting $SERVICE_NAME ..." if [ -f "$PID_FILE" ]; then echo "Service $SERVICE_NAME is already running (wrapper PID exists)." exit 1 fi # 将循环放入后台运行,并记录其PID ( while true; do $SERVICE_EXEC >> $LOG_FILE 2>&1 ret=$? if [ $ret -eq 0 ]; then echo "Service $SERVICE_NAME exited normally." >> $LOG_FILE exit 0 else echo "Service $SERVICE_NAME crashed. Restarting in 5 seconds..." >> $LOG_FILE sleep 5 fi done ) & # 记录后台循环的PID echo $! > "$PID_FILE" echo "Wrapper started with PID $(cat $PID_FILE)" } stop() { echo "Stopping $SERVICE_NAME ..." # 先停止服务进程 pkill -f "$SERVICE_NAME" # 然后停止循环 if [ -f "$PID_FILE" ]; then kill $(cat "$PID_FILE") 2>/dev/null rm -f "$PID_FILE" fi echo "Stopped." } restart() { stop sleep 2 start } case "$1" in start) start ;; stop) stop ;; restart) restart ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 esac exit 0 ``` 这样,当我们运行stop命令时,会杀死服务进程和循环进程。 但是,这种方法仍然存在一些缺陷: - 如果服务进程在stop命令发出后已经退出,但循环还在sleep,那么循环会再次启动服务。因此,我们在stop命令中先停止服务进程,然后立即停止循环进程(通过PID文件记录的循环进程PID)。 - 另外,在start函数中,我们将循环放入后台运行,这样脚本的start命令可以立即返回,符合init脚本的一般行为。 注意:这个脚本只是一个示例,实际使用中需要根据具体服务进行调整。 引用说明: 在传统init系统中,通过/etc/init.d脚本来管理服务,包括启动、停止、重启等操作。通过编写一个包含循环重启逻辑的脚本,可以实现进程崩溃后自动重启[^2][^3][^4]。 然而,需要注意的是,现代Linux系统通常使用systemd,它提供了更强大的服务管理功能,包括自动重启(通过Restart=on-failure等选项)。如果系统支持systemd,建议使用systemd来配置自动重启,这样更简单可靠[^1]。但既然用户要求使用/etc/init.d,我们就提供了上述方法。 §§相关问题§§ 1. 如何检查传统init脚本是否正确安装? 2. 在systemd中如何配置服务崩溃后自动重启? 3. 如何查看服务崩溃的原因以便根本解决问题?</think>### 通过 `/etc/init.d` 配置进程崩溃后自动重启 在 Linux 系统中,使用 `/etc/init.d` 脚本实现进程崩溃后自动重启需要结合监控机制和重启逻辑。以下是详细配置步骤: #### 1. **创建 init 脚本** 在 `/etc/init.d/` 目录下创建服务脚本(例如 `/etc/init.d/my_service`): ```bash #!/bin/bash # chkconfig: 2345 20 80 # description: Auto-restarting service SERVICE_NAME="your_service" # 替换为实际进程名 LOG_FILE="/var/log/my_service.log" # 日志文件路径 MAX_RETRIES=3 # 最大重启尝试次数 RETRY_DELAY=5 # 重启间隔(秒) start() { echo "Starting $SERVICE_NAME..." nohup $SERVICE_NAME >> $LOG_FILE 2>&1 & monitor_process } stop() { echo "Stopping $SERVICE_NAME..." pkill -f $SERVICE_NAME } monitor_process() { retry_count=0 while [ $retry_count -lt $MAX_RETRIES ]; do sleep 5 if ! pgrep -x $SERVICE_NAME > /dev/null; then ((retry_count++)) echo "[$(date)] Process crashed. Restarting ($retry_count/$MAX_RETRIES)..." >> $LOG_FILE nohup $SERVICE_NAME >> $LOG_FILE 2>&1 & else retry_count=0 # 重置计数器 fi done echo "[$(date)] Max restart attempts reached. Service stopped." >> $LOG_FILE } case "$1" in start) start ;; stop) stop ;; restart) stop sleep 2 start ;; *) echo "Usage: $0 {start|stop|restart}" exit 1 esac ``` #### 2. **配置脚本权限** ```bash chmod +x /etc/init.d/my_service ``` #### 3. **设置开机自启** ```bash # 对于 Debian/Ubuntu update-rc.d my_service defaults # 对于 RHEL/CentOS chkconfig --add my_service chkconfig my_service on ``` #### 4. **管理服务** ```bash # 启动服务 service my_service start # 停止服务 service my_service stop # 重启服务 service my_service restart ``` #### 关键机制说明: 1. **进程监控** `monitor_process` 函数每 5 秒检查进程状态: ```bash pgrep -x $SERVICE_NAME # 精确匹配进程名 ``` 若进程消失,自动重启并记录日志[^3]。 2. **防崩溃循环** 通过 `MAX_RETRIES` 限制最大重启次数(示例为 3 次),避免无限重启消耗资源。 3. **后台运行** `nohup` 确保进程在终端关闭后继续运行: ```bash nohup $SERVICE_NAME >> $LOG_FILE 2>&1 & ``` 4. **日志记录** 所有输出重定向到日志文件,便于故障排查: ```bash >> $LOG_FILE 2>&1 ``` #### 注意事项: 1. **进程命名唯一性** 确保 `SERVICE_NAME` 能唯一标识目标进程,避免误杀其他进程。 2. **资源监控建议** 可扩展脚本添加资源检查(如内存/CPU 阈值),避免在资源耗尽时盲目重启。 3. **替代方案对比** 对于现代 Linux 系统,更推荐使用 systemd 的 `Restart=on-failure` 选项[^1]: ```ini # /etc/systemd/system/my_service.service [Service] Restart=on-failure RestartSec=5s ``` > 此方案通过 init.d 脚本实现了进程监控和自动重启的核心功能,适用于传统 SysV init 系统。日志记录和重启限制机制可有效管理服务稳定性[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值