首页   注册   登录
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
Distributions
Ubuntu
Fedora
CentOS
中文资源站
网易开源镜像站
wsgzao
V2EX  ›  Linux

Linux 后台运行任务 nohup 结合&用法以及如何精准查找进程并 kill 后台任务实践

  •  4
     
  •   wsgzao · 39 天前 · 3781 次点击
    这是一个创建于 39 天前的主题,其中的信息可能已经有所发展或是发生改变。

    前言

    nohup 为什么要跟着&一起使用,不知道大家有没有思考过其中的小区别,另外很多人会推崇使用 screen,但是实际生产环境应用场景有限我就不展开了。这次实际遇到的问题主要是因为使用 crontab 调用了多个不同的 nohup 后台执行任务,然而代码逻辑中出现对相同文件的占用导致任务进程产生冲突使得 system load 负载达到 600 之高,需要 kill 任务时也不能盲目操作,需要杀掉的进程多且进程名与其它正常的进程名字有交集,需要合理运用 pstree 找到问题的源头。

    Linux 后台运行任务 nohup 结合&用法以及如何精准查找进程并 kill 后台任务实践

    更新历史

    2019 年 11 月 15 日 - 初稿

    阅读原文 - https://wsgzao.github.io/post/nohup/

    扩展阅读


    nohup 介绍

    用途:不挂断地运行命令。

    语法:nohup Command [Arg …] [ & ]

    • 无论是否将 nohup 命令的输出重定向到终端,输出都将附加到当前目录的 nohup.out 文件中。
    • 如果当前目录的 nohup.out 文件不可写,输出重定向到 $HOME/nohup.out 文件中。
    • 如果没有文件能创建或打开以用于追加,那么 Command 参数指定的命令不可调用。

    退出状态:该命令返回下列出口值:

    • 126 可以查找但不能调用 Command 参数指定的命令。
    • 127 nohup 命令发生错误或不能查找由 Command 参数指定的命令。否则 nohup 命令的退出状态是 Command 参数指定命令的退出状态。

    nohup 和&的关系

    使用 nohup 运行程序:

    • 输出重定向,默认重定向到当前目录下 nohup.out 文件
    • 使用 Ctrl + C 发送 SIGINT 信号,程序关闭
    • 关闭 Shell Session 发送 SIGHUP 信号,程序免疫

    使用 & 运行程序:

    • 程序转入后台运行
    • 结果会输出到终端
    • 使用 Ctrl + C 发送 SIGINT 信号,程序免疫
    • 关闭 Shell session 发送 SIGHUP 信号,程序关闭

    nohup 和&使用实例

    一般两个一起组合使用不会受 Ctrl C 和 Shell 关闭的影响:

    # 最简单的后台运行
    nohup command &
    # 输出默认重定向到当前目录下 nohup.out 文件
    nohup python main.py &  
    # 自定义输出文件(标准输出和错误输出合并到 main.log)
    nohup python main.py >> main.log 2>&1 & 
    # 与上一个例子相同作用的简写方法
    nohup python main.py &> main.log &
    # 不记录输出信息
    nohup python main.py &> /dev/null &
    # 不记录输出信息并将程序的进程号写入 pidfile.txt 文件中,方便后续杀死进程
    nohup python main.py &> /dev/null & echo $! > pidfile.txt
    
    

    由于使用 nohup 时,会自动将输出写入 nohup.out 文件中,如果文件很大的话,nohup.out 就会不停的增大,我们可以利用 Linux 下一个特殊的文件 /dev/null 来解决这个问题,这个文件就相当于一个黑洞,任何输出到这个文件的东西都将消失 只保留输出错误信息 nohup command >/dev/null 2>log & 所有信息都不要 nohup command >/dev/null 2>&1 &

    这里解释一下后面的 2>&1。 这涉及到 Linux 的重定向,其中 0、1、2 分别是标准输入、标准输出、标准错误输出,用来指定需要重定向的标准输入输出。默认情况下是标出输出,也就是 1。例如我们而上文提到的 2>&1 是 将错误信息重定向到标准输出。

    还有就是如果不想让程序输出,Linux 下有一个 /dev/null 的特殊文件,就像一个黑洞,所有输出到这个文件的信息全部会消失,如果你不需要输出日志,这样做就不会导致输出日志文件越来越大,占用存储空间的问题了

    其他相关命令

    # 结束当前任务
    ctrl+c
    # 将一个正在前台执行的命令放到后台,并且处于暂停状态
    ctrl+z
    # 查看任务,返回任务编号 和 进程号
    jobs -l
    # 将一个在后台暂停的命令,变成在后台继续执行。如果后台中有多个命令,可以用 bg %jobnumber 将选中的命令调出。
    bg %jobnumber
    # 将后台中的命令调至前台继续运行。如果后台中有多个命令,可以用 fg %jobnumber (是命令编号,不是进程号)将选中的命令调出
    fg %jobnumber
    

    编写一个测试脚本

    #!/bin/sh
    for ((i=1; i<1000; i++))
    do
        d=`date '+%Y-%m-%d %H:%M:%S'`
        echo "$d print ${i}"
        sleep 2s
    done
    

    查找后台运行程序

    1. 已知 pid 进程号当然最好了
    2. 使用ps -ef 或者 ps -aux 结合grep过滤
    3. 使用pstree -p确认复杂进程树结构
    4. 使用lsof -i:80查端口获得进程号
    5. 使用netstat -anp | grep 80查端口获得进程号,推荐使用lsof

    杀死后台运行程序

    kill

    kill 命令用来删除执行中的程序或工作。kill 可将指定的信息送至程序。预设的信息为 SIGTERM (15), 可将指定程序终止。若仍无法终止该程序,可使用 SIGKILL (9) 信息尝试强制删除程序,即 kill -9。程序或工作的编号可利用 ps 指令或 job 指令查看。

    语法 kill(选项)(参数)

    选项

    -a:当处理当前进程时,不限制命令名和进程号的对应关系;
    -l <信息编号>:若不加<信息编号>选项,则-l 参数会列出全部的信息名称;
    -p:指定 kill 命令只打印相关进程的进程号,而不发送任何信号;
    -s <信息名称或编号>:指定要送出的信息;
    -u:指定用户。
    

    参数 进程或作业识别号:指定要删除的进程或作业。

    实例 列出所有信号名称:

     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     16) SIGSTKFLT
    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
    

    只有第 9 种信号 (SIGKILL) 才可以无条件终止进程,其他信号进程都有权利忽略,下面是常用的信号:

    HUP     1    终端断线
    INT     2    中断(同 Ctrl + C )
    QUIT    3    退出(同 Ctrl + \)
    TERM   15    终止
    KILL    9    强制终止
    CONT   18    继续(与 STOP 相反,fg/bg 命令)
    STOP   19    暂停(同 Ctrl + Z )
    

    先用 ps 查找进程,然后用 kill 杀掉:

    ps -ef | grep vim
    root      3268  2884  0 16:21 pts/1    00:00:00 vim install.log
    root      3370  2822  0 16:21 pts/0    00:00:00 grep vim
    
    kill 3268
    kill 3268
    -bash: kill: (3268) - 没有那个进程
    

    killall

    killall 和 pill 命令差不多都是使用进程的名称来杀死进程,使用此指令可以杀死一组同名进程。我们可以使用 kill 命令杀死指定进程 PID 的进程,如果要找到我们需要杀死的进程,我们还需要在之前使用 ps 等命令再配合 grep 来查找进程,而 killall 把这两个过程合二为一,是一个很好用的命令。

    语法 killall(选项)(参数)

    选项

    -e:对长名称进行精确匹配;
    -l:忽略大小写的不同;
    -p:杀死进程所属的进程组;
    -i:交互式杀死进程,杀死进程前需要进行确认;
    -l:打印所有已知信号列表;
    -q:如果没有进程被杀死。则不输出任何信息;
    -r:使用正规表达式匹配要杀死的进程名称;
    -s:用指定的进程号代替默认信号“SIGTERM”;
    -u:杀死指定用户的进程。
    

    参数 进程名称:指定要杀死的进程名称。

    实例 杀死所有同名进程

    killall vi
    

    使用 pstree 查找和杀死复杂进程

    常见的 3 个 kill 命令

    • kill
    • pkill
    • killall

    搭配查找进程命令

    • pidof
    • pstree
    # 已知进程号,启动时输出后台运行程序的进程号,然后读取进程号杀死后台程序:
    kill -9 `cat pidfile.txt`
    
    # 进程数量较多且有规律,不和其他正常进程冲突
    killall 进程名
    kill -9 $(pidof 进程名)
    
    # 进程数量多,规律不明显,混了正常进程
    pstree -p
    
    # 复杂点的情况比如像我遇到的真实案例
    |-crond(127436)-+-crond(138887)---bash(138892)---bash(138895)---grep(140604)
    |               |-crond(139310)---bash(139323)---bash(139324)---python(139431)
    |               |-crond(139311)---bash(139325)---bash(139331)---python(139452)
    |               |-crond(139312)---bash(139318)---bash(139319)---python(139442)
    |               |-crond(139313)---bash(139317)---bash(139320)---python(139444)
    |               |-crond(139314)---bash(139329)---bash(139340)---python(139443)
    |               |-crond(139315)---bash(139327)---bash(139339)---grep(140768)
    |               |-crond(139651)---bash(139660)---bash(139661)---python(139915)
    |               |-crond(139652)---bash(139664)---bash(139666)---python(139916)
    |               |-crond(139653)---bash(139663)---bash(139665)---python(139914)
    |               |-crond(139654)---bash(139675)---bash(139683)---python(139918)
    |               |-crond(139655)---bash(139668)---bash(139677)---python(139913)
    |               `-crond(139656)---bash(139669)---bash(139682)---grep(139780)
    
    # 如果你喜欢用 grep 也没有问题,多设置几个过滤条件语句组合在一起即可
    ps -ef | grep 'python' | grep -v grep | awk '{print $2}' |xargs kill -9
    
    

    参考文章

    Linux 后台运行任务 nohup 和 &

    3 Easy Ways to Kill or Terminate a Process on Linux

    34 回复  |  直到 2020-01-02 17:04:57 +08:00
    kakarott1883
        1
    kakarott1883   39 天前
    其实用 screen 更方便……
    gwy15
        2
    gwy15   39 天前
    有 supervisord 这种现成的解决方案为什么不用呢?或者自带的 systemd 也行啊?
    jasonyang9
        3
    jasonyang9   39 天前   ♥ 1
    个人感觉 systemd 已经优雅的解决了文中提到的大部分问题,包括有控制的并发等。

    实做中, 如果是定期后台任务可以用 systemd 的 Timer 和 Service 甚至通过 Target 单元搭桥来很好的规划。如果是零散的手动后台任务可以用`screen -dm ...`。

    当然文中这些知识点补充或复习一遍也是很好的
    hujianxin
        4
    hujianxin   39 天前
    screen 走起
    jasonyang9
        5
    jasonyang9   39 天前   ♥ 1
    感觉真的要讲清楚这些需要了解 Linux 的进程模型,包括 pid、ppid、pgid、sid,session leader、pg leader 等等。

    ps,我还没仔细看这篇文章啊,我瞎说的
    lxk11153
        6
    lxk11153   39 天前   ♥ 1
    我选择 pgrep
    Lax
        7
    Lax   39 天前
    怎么能漏掉正宗的 pgrep。不到万不得已别直接 ps | grep | kill,容易掉坑里
    wsgzao
        8
    wsgzao   39 天前
    @gwy15 #2 我们有在使用 monit 和 supervisord,不过已经开始按计划分批迁移至 k8s 容器,可能这个才是真正的趋势
    @jasonyang9 赞同,一般标准化的服务我们都写入到 systemd 作为初始化的一部分,当然容器的出现让我们看到更多的可能
    wsgzao
        9
    wsgzao   39 天前
    @lxk11153 #6

    @Lax #7

    感谢 2 位补充非常棒的命令,不过还是需要提醒大家如果有命名冲突千万要谨慎处理
    love
        10
    love   39 天前   ♥ 1
    systemd 不香吗? 哪怕只是临时起个进程也可以用 systemd-run
    love
        11
    love   39 天前
    @gwy15 supervisord 不行,不能杀掉一堆衍生进程
    yukiloh
        12
    yukiloh   39 天前 via Android
    用 screen 耗资源吗,我一般都是 screen 只有某些残疾 docker 不想拉 screen 才 nohup
    chenqh
        13
    chenqh   39 天前
    @love 能呀,asgroup 就行了,能杀子进程
    有个问题,对于 systemd tornado 怎么写 notify service?
    crc8
        14
    crc8   39 天前
    问一下,nohup 使用是不是有什么限制?我一直程序。在 CLI 下加参数执行成功,客户端也可以连接。但如果用 nohup+&就不行,不知道什么原因。
    CivAx
        15
    CivAx   39 天前
    活用 pgrep -f 和 [] 框选可以避免大量 grep 带来的蛋疼问题,比如 grep -v grep
    guokeke
        16
    guokeke   39 天前
    写的非常好!后续可以写一篇 k8s 的文章吗?
    charlie21
        17
    charlie21   39 天前
    sigh
    mengzhuo
        18
    mengzhuo   39 天前 via iPhone   ♥ 2
    线上系统用 nohup 堪称灾难,但是我厂老系统还特别爱用

    保活脚本写不好,跟烂代码一起直接搞出一堆僵尸进程,拖垮其他机器。

    日志也得不到有效管理,钩子还要自己实现。


    所以我用 systemd。
    WoodenRobot
        19
    WoodenRobot   39 天前   ♥ 1
    参考文章里看到自己写的文章了,看了一下博主的博客发现 RSS 订阅了(逃
    wsgzao
        20
    wsgzao   39 天前   ♥ 1
    @WoodenRobot #19 我写文章之前先用 Google 搜索了关键词,如果已经有写的比较好的就留言申请转载了,谢谢你分享的文章哈

    @guokeke #16 极客时间上张磊的《深入剖析 Kubernetes 》和《 Linux 性能优化实战》作者倪朋飞的《 Kubernetes 指南》都是优质的学习资料

    https://time.geekbang.org/column/intro/116

    https://kubernetes.feisky.xyz/
    KentY
        21
    KentY   39 天前
    @CivAx 我同意你说的 pgrep, 但是 grep -v grep 是可以避免的, 比如: grep python|grep -v grep 可以 grep 'pytho[n]'
    CivAx
        22
    CivAx   39 天前
    @KentY #21 所以我说的整句话有什么问题
    KentY
        23
    KentY   39 天前
    @CivAx 我不是挑刺儿的....你说的也没有任何问题, 只是说了个小 trick..
    算了,请忽视我说的吧.
    NerverLibis
        24
    NerverLibis   39 天前
    @crc8 遇到需要输入参数的,你得先手动开守护进程,挂起,输入参数,再恢复进程到后台运行……
    lumotian
        25
    lumotian   39 天前
    pm2 也很好用
    dghgyxu
        26
    dghgyxu   39 天前 via Android
    setsid command & 和 nohup command & 效果差不多,都可以防止 command 被 ctrl-c 终止。有没有大佬讲讲这俩的区别?
    conn4575
        27
    conn4575   39 天前 via Android
    早就不用这样的方式了,简单点的用 screen 或者 tmux,线上环境一律用 docker
    scriptB0y
        28
    scriptB0y   39 天前   ♥ 1
    不错的文章,深入浅出,我来补充一下 :)

    > 关闭 Shell Session 发送 SIGHUP 信号,程序免疫
    这么说其实不太对,nohup 只是让进城忽略 SIGHUP 信号,shell 关闭的时候还是会给自己的子进程发送 SIGHUP 的。

    另外 pkill 是 procs-ng 里面提供的命令,除此外这个项目里面还有其他一些耳熟能详的命令:
    /usr/bin/free
    /usr/bin/pgrep
    /usr/bin/pidof
    /usr/bin/pkill
    /usr/bin/pmap
    /usr/bin/ps
    /usr/bin/pwdx
    /usr/bin/skill
    /usr/bin/slabtop
    /usr/bin/snice
    /usr/bin/tload
    /usr/bin/top
    /usr/bin/uptime
    /usr/bin/vmstat
    /usr/bin/w
    /usr/bin/watch

    killall 是 psmisc 里面的,里面有 6 个比较常用的命令,比如 pstree, fuser
    wsgzao
        29
    wsgzao   39 天前
    @scriptB0y #28 谢谢分享细节,容器化想必都是大家正在做的事情
    Tink
        30
    Tink   39 天前 via iPhone
    supervisor
    james122333
        31
    james122333   38 天前
    haha
    只是基本的
    等你熟悉才知道如何流程控制
    4ever911
        32
    4ever911   38 天前
    screen 比较好 attach detach 看状态
    crc8
        33
    crc8   35 天前
    @NerverLibis 谢谢,找机会试下。
    sunriz
        34
    sunriz   18 天前
    tmux 也行
    关于   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1413 人在线   最高记录 5168   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.3 · 26ms · UTC 17:23 · PVG 01:23 · LAX 09:23 · JFK 12:23
    ♥ Do have faith in what you're doing.