13 node 程序后台执行加上 tail 命令, 中断 tail 命令, 同时也中断了 node 程序

news2024/11/25 12:53:58

前言

呵呵 最近帮朋友解决问题[2022.09.08] 

需要启动一个 node 程序, 然后 需要一个 startUp.sh 脚本 

然后 反手写了一个过去, 按道理 来说 应该是 后台启动了对应的 node 程序, 然后将 标准输出, 错误输出 输出到 logs/nohup.log 日志文件中, 然后基于 tail 命令 来查看 logs/nohup.log 的日志 

但是 问题是 我中断了 tail 命令之后, startUp.sh 中启动的 node 程序 也被中断了?? 

按之前的经验, 这个 node 程序应该不会被中断才对, 但是 这个挺出乎我的意料的, 因此 当时记录了一个 todo 

这里主要是 两个参照, 一个是 启动 node 程序, 另外一个是 启动一个普通的 java 程序, 然后 来作为对比 

然后 这里来看一下, 当然 依然 还是存在很多 不明白的地方  

以下 node 部分代码参考自 关于node.js:Nodejs-进程在退出时挂起(Ctrl + C) | 码农家园

测试用例

编写一个 node 程序 如下 


console.log('PID: ', process.pid);

var http = require('http'); // HTTP server to keep the script up long enough
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\n');
}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');

新建一个 nodeStartUp.sh 如下

nohup node nodeProcess.js > ./logs/nodeNohup.log 2>&1 &

tail -f ./logs/nodeNohup.log

执行 nodeStartUp.sh 程序正常启动, 并打印 相关日志 

master:11_singal2Parent jerry$ ./nodeStartUp.sh 
PID:  3612
Server running at http://127.0.0.1:1337/

访问服务, 拿到正确的结果  

问题复现效果如下 

master:11_singal2Parent jerry$ ps -ef | grep node
  501  3612  3611   0  9:56PM ttys000    0:00.17 node nodeProcess.js
  501  3613  3611   0  9:56PM ttys000    0:00.00 tail -f ./logs/nodeNohup.log
  501  3635   515   0  9:59PM ttys001    0:00.00 grep node

// 中断 tail -f 之后, 发现 node 进程也挂了 
master:11_singal2Parent jerry$ ps -ef | grep node
  501  3638   515   0  9:59PM ttys001    0:00.00 grep node

参照的 java 程序

注册了各个 SignalHandler, 主要是因为 怀疑这个 node 进程的挂掉可能和进程之间的 信号机制 有关系 

/**
 * Test07Signal2ParentProcess
 *
 * @author Jerry.X.He <970655147@qq.com>
 * @version 1.0
 * @date 2022-09-10 19:18
 */
public class Test07Signal2ParentProcess {

    // Test07Signal2ParentProcess
    public static void main(String[] args) throws Exception {

        String lines = "HUP\n" +
                "INT\n" +
//                "QUIT\n" +
//                "ILL\n" +
                "TRAP\n" +
                "ABRT\n" +
                "EMT\n" +
//                "FPE\n" +
//                "KILL\n" +
                "BUS\n" +
//                "SEGV\n" +
                "SYS\n" +
                "PIPE\n" +
                "ALRM\n" +
                "TERM\n" +
                "URG\n" +
//                "STOP\n" +
                "TSTP\n" +
                "CONT\n" +
                "CHLD\n" +
                "TTIN\n" +
                "TTOU\n" +
                "IO\n" +
                "XCPU\n" +
                "XFSZ\n" +
                "VTALRM\n" +
                "PROF\n" +
                "WINCH\n" +
                "INFO\n" +
//                "USR1\n" +
                "USR2";
        for (String sigNo : lines.split("\n")) {
            Signal.handle(new Signal(sigNo), new SignalHandler() {
                @Override
                public void handle(Signal signal) {
                    System.out.println(sigNo);
                }
            });
        }

        Signal.raise(new Signal("ALRM"));
        Signal.raise(new Signal("INT"));
        Thread.sleep(300 * 1000);

    }

}

模拟 nodeStartUp.sh 同样编写一个 startUp.sh 

java -classpath /Users/jerry/IdeaProjects/HelloWorld/target/classes com.hx.test13.Test07Signal2ParentProcess > ./logs/nohup.log 2>&1 &

tail -f ./logs/nohup.log

startUp.sh 启动程序之后, 输出如下 

master:11_singal2Parent jerry$ ./startUp.sh 
ALRM

关于进程结构 

node 进程的进程结构 

491     1   0  9:51AM ??         2:44.93 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
    492   491   0  9:51AM ttys000    0:00.03 login -pf jerry
        // shell
        493   492   0  9:51AM ttys000    0:00.80 -bash
            // startUp.sh
            3651   493   0 10:01PM ttys000    0:00.00 -bash
                3652  3651   0 10:01PM ttys000    0:00.14 node nodeProcess.js
                3653  3651   0 10:01PM ttys000    0:00.00 tail -f ./logs/nodeNohup.log

参照的 java 程序的进程结构 

491     1   0  9:51AM ??         2:52.94 /Applications/Utilities/Terminal.app/Contents/MacOS/Terminal
    527   491   0  9:52AM ttys002    0:00.02 login -pf jerry
        // shell
        528   527   0  9:52AM ttys002    0:00.19 -bash
            // startUp.sh 
            3708   528   0 10:07PM ttys002    0:00.00 -bash
                3709  3708   0 10:07PM ttys002    0:00.11 /usr/bin/java -classpath /Users/jerry/IdeaProjects/HelloWorld/target/classes com.hx.test13.Test07Signal2ParentProcess
                3710  3708   0 10:07PM ttys002    0:00.00 tail -f ./logs/nohup.log

按正常情况来看, 因为 启动 node/java 程序是后台执行的 

正产情况应该是 ctrl + c 中止 "tail -f" 了之后, startUp.sh 脚本执行完成退出, 但是 node/java 程序还在才对, 但是 node 程序缺退出了, 而 java 程序没有退出  

从 node 退出的现象来看, 像是 "tail -f" 的进程接收到了 ctrl + c, 之后发送了信息到 node 进程, 或者 "tail -f" 进程接收到了 ctrl + c, 之后发送了信息到 "startUp.sh" 进程, 然后 "startUp.sh" 进程再发送了信息到 node 进程, 来中止了 node 进程 ?

测试 node 进程

信号交互 是一种进程之间通信的方式, 这里也是我们 首先怀疑的交互方式 

ctrl + c 发送的是中断信号, 对应于信号变量 SIGINT 

在 nodeProcess.js 最前面注册 中断信号的处理, ALRM 信号的处理, 主要是输出一下 中断信号处理的处理函数 

process.on('SIGINT', function() {
    console.log('SIGINT');
    process.exit();
});

process.on('SIGALRM', function() {
    console.log('Nice SIGINT-handler');
    var listeners = process.listeners('SIGINT');
    console.log(" lisetner size : " + listeners.length);
    for (var i = 0; i < listeners.length; i++) {
        console.log(listeners[i].toString());
    }

//    process.exit();
});


console.log('PID: ', process.pid);

var http = require('http'); // HTTP server to keep the script up long enough
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('Hello World\
');
}).listen(1337, '127.0.0.1');

console.log('Server running at http://127.0.0.1:1337/');

然后开始测试, 可以看到的是 ctrl + c 的时候, 确实有程序向 node 进程发送了 SIGINT 的信号 

但是是 哪一个进程呢?, 为什么要发送呢 ?, 对应于 参照的 java 进程又会是怎样的情况呢?

^Cmaster:11_singal2Parent jerry$ ./nodeStartUp.sh 
PID:  3799
Server running at http://127.0.0.1:1337/
^Cmaster:11_singal2Parent jerry$ cat logs/nodeNohup.log 
PID:  3799
Server running at http://127.0.0.1:1337/
SIGINT
master:11_singal2Parent jerry$ 

测试 java 进程

使用 startUp.sh 启动, 并 ctrl + c 中断 "tail -f" 

可以发现 java 进程这边并没有收到 SIGINT 的信号 

这是怎么回事?? 

master:11_singal2Parent jerry$ ./startUp.sh 
ALRM
^Cmaster:11_singal2Parent jerry$ cat logs/nohup.log 
ALRM

向 java 进程发送信号, 其他的大部分信号均能够使用 代码注册的 handler

但是 SIGINT 似乎是不行 

# cmd 中发送命令给 java 程序
master:11_singal2Parent jerry$ kill -20 3872
master:11_singal2Parent jerry$ kill -20 3872
master:11_singal2Parent jerry$ kill -28 3872
master:11_singal2Parent jerry$ kill -13 3872
master:11_singal2Parent jerry$ kill -22 3872
master:11_singal2Parent jerry$ kill -2 3872
master:11_singal2Parent jerry$ kill -2 3872
master:11_singal2Parent jerry$ kill -2 3872
master:11_singal2Parent jerry$ kill -2 3872

# 日志文件中展示日志如下 
master:11_singal2Parent jerry$ cat logs/nohup.log
ALRM
CHLD
CHLD
WINCH
PIPE
TTOU
# 在 startUp.sh 中启动的 java 进程, 似乎对于 SIGINT 的处理有其他的转换, 没有走 handler, 但是也没有走默认的处理 

在 idea 中输出如下 

# cmd 中发送命令给 java 程序
master:11_singal2Parent jerry$ kill -20 3926
master:11_singal2Parent jerry$ kill -20 3926
master:11_singal2Parent jerry$ kill -28 3926
master:11_singal2Parent jerry$ kill -13 3926
master:11_singal2Parent jerry$ kill -22 3926
master:11_singal2Parent jerry$ kill -2 3926
master:11_singal2Parent jerry$ kill -2 3926
master:11_singal2Parent jerry$ kill -2 3926

# idea 中显示的日志
ALRM
CHLD
CHLD
WINCH
PIPE
TTOU
INT
INT
INT

# 当点击了 idea 的停止按钮, 发送了一个 SIGINT 给进程, idea 关闭了 jpda 客户端, 停止按钮变成了骷髅头
Disconnected from the target VM, address: '127.0.0.1:54296', transport: 'socket'
INT
# 再次点击 idea 的停止按钮, 发送了一个 SIGKILL 给进程, 强制杀掉进程 
Process finished with exit code 137 (interrupted by signal 9: SIGKILL)

假设我们去掉 java 程序中的 SIGINT 的手动注册的 handler, 使用系统默认的处理 

 在 cmd 中使用 startUp.sh 效果如下 

# cmd 中发送命令给 java 程序
master:11_singal2Parent jerry$ kill -20 3897
master:11_singal2Parent jerry$ kill -20 3897
master:11_singal2Parent jerry$ kill -28 3897
master:11_singal2Parent jerry$ kill -13 3897
master:11_singal2Parent jerry$ kill -22 3897
master:11_singal2Parent jerry$ kill -2 3897
master:11_singal2Parent jerry$ kill -2 3897
master:11_singal2Parent jerry$ kill -2 3897

# 日志文件中展示日志如下 
master:11_singal2Parent jerry$ cat logs/nohup.log 
ALRM
CHLD
CHLD
WINCH
PIPE
TTOU
# 在 startUp.sh 中启动的 java 进程, 似乎对于 SIGINT 的处理有其他的转换, 没有走 handler, 但是也没有走默认的处理 

在 idea 中输出如下 

# cmd 中发送命令给 java 程序
master:11_singal2Parent jerry$ kill -20 3893
master:11_singal2Parent jerry$ kill -20 3893
master:11_singal2Parent jerry$ kill -28 3893
master:11_singal2Parent jerry$ kill -13 3893
master:11_singal2Parent jerry$ kill -22 3893
master:11_singal2Parent jerry$ kill -2 3893
master:11_singal2Parent jerry$ kill -2 3893
-bash: kill: (3893) - No such process

# idea 中显示的日志
ALRM
CHLD
CHLD
WINCH
PIPE
TTOU

# 当点击了 idea 的停止按钮, 发送了一个 SIGINT 给进程, idea 关闭了目标进程, SIGINT 的默认处理是终止进程 
Disconnected from the target VM, address: '127.0.0.1:53095', transport: 'socket'

Process finished with exit code 130 (interrupted by signal 2: SIGINT)

怎么解决 node 中这个问题?

很简单, 只需要注册一下 SIGINT 的自定义处理函数, 不要调用关闭进程的相关处理即可 

process.on('SIGINT', function() {
    console.log('SIGINT');
//    process.exit();
});

演示如下, 可以发现 ctrl + c 之后, node 进程依然还在 

master:11_singal2Parent jerry$ ps -ef | grep node
  501  3957   493   0 10:48PM ttys000    0:00.00 grep node
master:11_singal2Parent jerry$ ./nodeStartUp.sh 
PID:  3959
Server running at http://127.0.0.1:1337/
^Cmaster:11_singal2Parent jerry$ ps -ef | grep node
  501  3959     1   0 10:48PM ttys000    0:00.04 node nodeProcess.js
  501  3962   493   0 10:48PM ttys000    0:00.00 grep node
master:11_singal2Parent jerry$ cat logs/nodeNohup.log 
PID:  3959
Server running at http://127.0.0.1:1337/
SIGINT
master:11_singal2Parent jerry$ 

遗留的问题?

呵呵 一些 还在知识盲区的问题 

1. 但是是 哪一个进程呢 向 node进程 发送的 SIGINT ?

2. 这个进程 为什么要发送 SIGINT 到 node 进程呢 ?

3. java 程序这边通过 "startUp.sh" 启动, 进程到底收到 SIGINT 没有? 

4. java 程序这边程序中注册的 SIGINT 的 handler 为什么没有生效?, 替换成了什么?

希望 后面能够解释的清楚以上的这些问题 

参考 

关于node.js:Nodejs-进程在退出时挂起(Ctrl + C) | 码农家园


完 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403413.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Kafka】MM2同步Kafka集群时如何自定义复制策略(ReplicationPolicy)

文章目录需求准备工作自定义复制策略编译代码需求 使用MM2同步集群数据&#xff0c;topic名称不能变&#xff0c;默认的复制策略为&#xff1a;DefaultReplicationPolicy&#xff0c;这个策略会把同步至目标集群的topic都加上一个源集群别名的前缀&#xff0c;比如源集群别名为…

设计模式-第13章(状态模式)

状态模式状态模式状态模式的好处和用处工作状态状态模式 状态模式&#xff08;State&#xff09;&#xff0c;当一个对象的内在状态改变时允许改变其行为&#xff0c;这个对象看起来像是改变了其类。 状态模式主要解决的是当控制一个对象状态转换的条件表达式过于复杂时的情况…

【大数据】HDFS客户端命令行(hdfs dfs)详细使用说明

DFS命令使用概览使用说明lsdfducountappendToFilecatchecksumchgrpchmodchownconcatcopyFromLocalcopyToLocalcpcreateSnapshotdeleteSnapshotexpungefindgetgetfaclgetfattrgetmergeheadmkdirmoveFromLocalmoveToLocalmvputrenameSnapshotrmrmdirsetfaclsetfattrsetrepstattai…

实现VOC数据集与COCO数据集格式转换

实现VOC数据集与COCO数据集格式转换2、将voc数据集的xml转化为coco数据集的json格式2、COCO格式的json文件转化为VOC格式的xml文件3、将 txt 文件转换为 Pascal VOC 的 XML 格式<annotation><folder>文件夹目录</folder><filename>图片名.jpg</file…

2020蓝桥杯真题凯撒加密 C语言/C++

题目描述 给定一个单词&#xff0c;请使用凯撒密码将这个单词加密。 凯撒密码是一种替换加密的技术&#xff0c;单词中的所有字母都在字母表上向后偏移 3 位后被替换成密文。即 a 变为 d&#xff0c;b 变为 e&#xff0c;⋯&#xff0c;w 变为z&#xff0c;x 变为 a&#xff0…

【QT网络编程】实现UDP协议通信

文章目录概要&#xff1a;本期主要讲解QT中对UDP协议通信的实现。一、UDP协议通信二、Qt中UDP协议的处理1.QUdpSocket三、Qt实现UDP通信1.客户端2.服务器端结尾概要&#xff1a;本期主要讲解QT中对UDP协议通信的实现。 一、UDP协议通信 Internet 协议集支持一个无连接的传输协…

SprintBoot打包及profile文件配置

打成Jar包 需要添加打包组件将项目中的资源、配置、依赖包打到一个jar包中&#xff0c;可以使用maven的package&#xff1b;运行: java -jar xxx(jar包名) 操作步骤 第一步: 引入Spring Boot打包插件 <!--打包的插件--> <build><!--修改jar的名字--><fi…

认识vite_vue3 初始化项目到打包

从0到1创建vite_vue3的项目背景效果vite介绍&#xff08;对比和vuecli的区别&#xff09;使用npm创建vitevitevuie3创建安装antdesignvite自动按需引入&#xff08;vite亮点&#xff09;请求代理proxy打包背景 vue2在使用过程中对象的响应式不好用新增属性的使用$set才能实现效…

FPGA 20个例程篇:20.USB2.0/RS232/LAN控制并行DAC输出任意频率正弦波、梯形波、三角波、方波(二)

通过上面的介绍相信大家对数字变频已经有了一个较为整体性的认识&#xff0c;下面笔者来对照XILINX的DDS IP核对数字变频技术展开更进一步的说明&#xff0c;做到了理论和实践很好地结合&#xff0c;这样大家再带入Modelsim进行仿真测试就不仅掌握了数字变频的理论知识&#xf…

【Linux】网络原理

本篇博客让我们一起来了解一下网络的基本原理 1.网络发展背景 关于网络发展的历史背景这种东西就不多bb了&#xff0c;网上很容易就能找到参考资料&#xff0c;我的专业性欠缺&#xff0c;文章参考意义也不大。这里只做简单说明。 网络发展经过了如下几个模式 独立模式&…

创建线程的三种方法

文章目录1、创建一个类实现Runnable接口&#xff0c;并重写run方法。2、创建一个类继承Thread类&#xff0c;并重写run方法。3、实现Callable接口&#xff0c;重写call()方法&#xff0c;这种方式可以通过FutureTask获取任务执行的返回值。4、run()方法和start()方法有什么区别…

14 Day:同步锁与操作系统输入输出

前言&#xff1a;在上一期的线程章节中&#xff0c;我们的线程输出貌似有大问题&#xff0c;今天我们便要来学习同步锁来解决这个问题&#xff0c;同时再次基础上拿下键盘输入&#xff0c;实现操作系统的输入和输出。从今天开始我们的操作系统不在是一块“看板”了&#xff01;…

Python|数学|贪心|数组|动态规划|单选记录:实现保留3位有效数字(四舍六入五成双规则)|用Python来创造一个提示用户输入数字的乘法表|最小路径和

1、实现保留3位有效数字&#xff08;四舍六入五成双规则&#xff09;&#xff08;数学&#xff0c;算法&#xff09; 贡献者&#xff1a;weixin_45782673 输入&#xff1a;1234 输出&#xff1a;1234 12 12.0 4 4.00 0.2 0.200 0.32 0.320 1.3 1.30 1.235 1.24 1.245 1.24 1.…

Docker 入门建议收藏 第一部分

一、Docker 是什么&#xff1f; Docker&#xff0c;翻译过来就是码头工人 Docker是一个开源的应用容器引擎&#xff0c;让开发者可以打包他们的应用以及依赖包到一个可抑制的容器中&#xff0c;然后发布到任何流行的Linux机器上&#xff0c;也可以实现虚拟化。容器完全使用沙盒…

JVM概览:内存空间与数据存储

核心的五个部分虚拟机栈&#xff1a;局部变量中基础类型数据、对象的引用存储的位置&#xff0c;线程独立的。堆&#xff1a;大量运行时对象都在这个区域存储&#xff0c;线程共享的。方法区&#xff1a;存储运行时代码、类变量、常量池、构造器等信息&#xff0c;线程共享。程…

ClassMix: Segmentation-Based Data Augmentation for Semi-Supervised Learning学习笔记

ClassMix相关介绍主要思想方法Mean-Teacher损失函数交叉熵损失标签污染实验实验反思参考资料相关介绍 从DAFormer溯源到这篇文章&#xff0c;ClassMix主要是集合了伪标签和一致性正则化&#xff0c;思想来源于CutMix那条研究路线&#xff0c;但是优化了CutMix中的标签污染的情…

使用 HTML5 轻松验证表单插件

下载:https://download.csdn.net/download/mo3408/87559594 效果图: 当您通过表单从人们那里收集信息时,必须应用某种验证。如果不这样做,可能会导致客户流失、数据库中的垃圾数据甚至网站的安全漏洞。从历史上看,构建表单验证一直很痛苦。在服务器端,全栈框架会为您处理…

【AI绘图学习笔记】深度前馈网络(一)

有关深度前馈网络的部分知识&#xff0c;我们已经在吴恩达的机器学习课程中有过了解了&#xff0c;本章主要是对《深度学习》花书中第六章&#xff1a;深度前馈网络的总结笔记。我希望你在看到这一章的时候&#xff0c;能回忆起机器学习课程中的一些环节或者细节&#xff0c;这…

【现代机器人学】学习笔记十一:抓握与操作

本章是比较独特的一章&#xff0c;相对于前面的内容&#xff0c;内容较为独立&#xff0c;主要描述的是力学相关的一些理论。因此&#xff0c;读者也完全不必根据题目产生一些不必要的幻想&#xff0c;认为似乎看完这章我就可以学会机器人抓取。不过&#xff0c;我仍然认为这章…

新入职的项目经理,如何击破权力微薄的困境?

“从此找到了上班的意义”这个话题最近登上了热搜&#xff0c;在“铜三铁四”的招聘季&#xff0c;大家停止了内卷&#xff0c;给自己安排得明明白白&#xff0c;每天上班的动力就是&#xff1a;充电、蹭网、干饭、灌水、睡午觉、上厕所。但咱项目经理们却没办法Get这些动力&am…