ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Linux 在连接断开后让子进程正常执行或终止子进程

Linux 在连接断开后让子进程正常执行或终止子进程 当你使用远程工具连接后对于 Linux 来说连接会话自身也是一个进程拥有一个进程号在会话中所有启动的子进程以会话进程为父进程并组成了一个进程组当你结束本次会话Linux 在回收资源的时候就视为从会话进程开始往下的所有子进程都应当结束因此向下传递SIGHUP中断信号。所以想要在结束远程连接所做的任务还不结束那就有两种思路第一种是让子进程忽略来自父进程的中断信号此时父进程结束后子进程由于成为孤岛会被交给系统初始化进程PID 为 1 的 init 进程并在系统关闭时被 init 进程清理 第二种就是使用类似容器服务的插件把任务跑在其中使得你的会话结束但是任务不受影响第一种忽略来自父进程的中断信号操作上很简单适用于针对某个单独的进程操作只需在命令的前面加上nohup就可以把它交给父进程nohuppython-mSimpleHTTPServer10040/opt/start_cdh_log/cdh_start.log这种方式 Linux 系统自带本质上的作用是告诉启动的子进程忽略来自于父进程的中断信号随后想要结束进程就只能手动 kill[roothdp2 ~]# nohup --help用法nohup 命令[参数]... 或nohup 选项 忽略挂起信号运行指定的命令。--help显示此帮助信息并退出--version显示版本信息并退出 If standard input is a terminal, redirect it from /dev/null. If standard output is a terminal, append output tonohup.outifpossible,$HOME/nohup.outotherwise. If standard error is a terminal, redirect it to standard output. To save output to FILE, usenohup COMMAND FILE.注意您的shell 内含自己的nohup 程序版本它会覆盖这里所提及的相应版本。请查阅您的shell 文档获知它所支持的选项。 GNU coreutils online help:http://www.gnu.org/software/coreutils/请向http://translationproject.org/team/zh_CN.html报告nohup 的翻译错误 要获取完整文档请运行info coreutilsnohup invocationnohup 常常同时出现在后台执行一个任务 -》如何后台执行进程第二种用插件Linux有个三方插件工具叫tmux它可以为你以服务的形式持久的运行多个独立会话yuminstall-ytmux随后使用命令tmux new -s session_name就开启一个独立新会话服务tmux new-stestse在会话里正常进行需要的操作即可如果要回到原先的系统会话先按下ctrlb然后再单独按d出现上面这个界面就回到原本的系统远程会话不过此时tmux会话里的进程仍然在运行。想重新回到tmux会话里就运行tmux a -t 会话名称。在会话间切换 先按ctrlb再按s然后就可以在会话间选择其中一个按enter进入。列出会话使用tmux ls即使关闭当前远程工具连接的会话tmux的会话也仍然存在如果里面有进程会持续运行。除非进入会话中把进程关闭掉。彻底停止一个 tmux 会话使用tmux kill-session -t 会话名称tmux会自动维护你的用户身份这其中有如下好处1、tmux 会把会话独立于 SSH 连接断开后任务会继续执行2、完整的执行环境tmux 会保留当前用户的身份、环境变量和工作目录3、随时可恢复重新连接后可以随时重新进入回话如果不理解保留用户身份有什么用你可以自己尝试一下用一个shell脚本写一个循环每5秒写一句话到一个文件里后台执行并忽略父进程中断信号这个脚本后断开服务器连接过一会后查看日志你会发现会报认证相关的错误这就是脚本执行时已经获取不到执行用户的身份资源了。有些公司开发底蕴实力较强的话通常会开发自己的安全协议或者改造 kerberos 安全接口最终以扩展插件的形式维护服务器上的用户身份对于终止子进程方面上面提到的父进程中断结束子进程被同时释放只生效于登录会话所生成的进程而如果是通过 kill 中断一个日常操作中的进程会话只会终止目标进程自身而子进程的父级会直接变成孤岛进程并父进行向上提升注意并不是一下就给 PID 为 0 或 1 的进程了这是因为 kill 时默认发送SIGTERM信号或者 kill -9 发送SIGKILL 信号这两种信号本身就不会发给子进程只影响目标进程。还有一种子进程不受影响的情况父进程如果是正常结束退出子进程也不收到任何信号此时你想让父进程结束时子进程同样结束就需要用到 shell 的回调函数语法#!/bin/bash# 定义清理函数用于终止子进程cleanup(){if[-n$CHI_PID]ps-p$CHI_PID/dev/null;thenecho正在终止子任务 (PID:$CHI_PID)...kill$CHI_PID# 发送 SIGTERM 信号让子任务优雅关闭wait$CHI_PID# 等待子进程完全退出echo子任务已终止。fiexit0# 正常退出父脚本}# 捕获 SIGINT (CtrlC)、SIGTERM (普通 kill) 和 EXIT (正常退出) 信号trapcleanup SIGINT SIGTERM EXIT# 启动 子进程 并在后台运行echo正在启动 任务...sleep10# 捕获后台进程的 PIDCHI_PID$!echo子任务已启动PID:$CHI_PID# 父脚本正常干自己的事情。这里不要直接啥也不干不然效果还没出来就全停止了这点应该能理解sleep5# 或者如果父脚本没有其他的任务可以等待子脚本常见于子进程启动命令是个 Spark 任务提交命令#wait $CHI_PID# 亦或者等待所有子进程结束#wait要注意的是trap 语法会在kill -9命令下失效因为它发送的是SIGKILL信号该信号在 Linux 中属于内核级别的终极杀手锏不顾任何阻碍立刻停止一个进程且不可被任何手段检测到如果你的子进程比较多你可以使用 Linux 中 进程组ID等于组中第一个进程的 ID 这个特性去 kill 一整个进程组#!/bin/bash# -m 改变 shell 行为每一个新的直接子进程独立分配一个进程组而不再直接进入父进程所在的进程组set-m# 将所有的子进程放在一个子shell ( ... ) 中后台运行(sleep10spark-submit...# 开启子进程的进程就可以结束了不需要等待不然就没效果了exit0)# 获取这个独立进程组的 PID因为它等于第一个进程的PID注意获取的不是 sleep 10 而是 () 打开的 bash 进程它的 PID即便它退出了pid$!# 捕获退出信号只杀掉这个独立的进程组。 -- 是 Linux 中的特殊语法用途是告诉解析器 -- 后的 - 都是参数值的一部分而不是参数但这只是一个加强可读性的语法并不影响实际作用因为底层解析中 -123321 这本身就是一个完整的入参# kill 后面的进程号如果是 负数意味着指定的是进程组IDtrapif ps -p -\$pid /dev/null 21; then kill -- -\$pid; fiEXIT SIGINT SIGTERM# 等待所有子进程结束。此时当前这个 bash 会接手上面 () 退出后那些后台执行的子进程做它们的父进程waitexit0还有一种情况是当你只需要 kill 当前的子进程父进程不需要关闭可以配合 jobs 命令#!/bin/bash# 启动子进程sleep3000spark-submit...# 捕获退出信号只杀掉当前脚本的直接子进程trapkill $(jobs -p)EXIT SIGINT SIGTERMwaitexit0
返回列表