【问题标题】:node.js createReadStream hangs process.exit() when exit events are listenednode.js createReadStream 在监听退出事件时挂起 process.exit()
【发布时间】:2019-11-19 11:26:43
【问题描述】:

我正在尝试从 Linux 上的 /dev/input/by-id/ 读取输入设备事件。我尝试在我的项目中使用 input-event npm 包,但是当我尝试使用 CTRL+C 停止它并且需要 kill -9 时,它正在挂起我的整个终端。我认为这是包的问题,​​但事实证明这实际上是我在 SIG 信号上设置监听器的问题:

process.on('SIGINT', function() { exitHandler('SIGINT'); });
process.on('SIGUSR1', function() { exitHandler('SIGUSR1'); });
process.on('SIGUSR2', function() { exitHandler('SIGUSR2'); });
process.on('SIGTERM', function() { exitHandler('SIGTERM'); });

当我删除侦听器时,它开始正常工作(可以 CTRL+C 退出节点)。如果我保留听众,但删除 readStreams,它也可以工作。我尝试在 process.exit() 之前关闭流和文件描述符,但它不起作用。 这是我的代码:

const fs = require('fs');

let monitoredDevices = { 
    devices: [],

    addDevice: function(path) {

        fs.open(path, 'r', (e, fd) => {
            if(e)
                return;
            let dev = { path: path, fd: null, stream: null};
            dev.fd = fd;
            // comment out the stream and it process can exit normally
            dev.stream = fs.createReadStream(null, { fd: dev.fd, mode: 'r'});
            let ix = this.devices.push(dev);

            if(dev.stream) {
                dev.stream.on('data', (data) => {
                    console.log('DATA('+dev.path+'):', data);
                });

                dev.stream.on('error', (e) => {
                    if(e.code == 'ENODEV') {
                        // disconnected
                        this.devices.splice(ix, 1);
                        fs.closeSync(dev.fd);
                        dev.stream.close();
                        delete dev;
                    }
                });
            }
        });
    },

    hasDevice: function(path) {
        return this.devices.find(x => x.path == path) ? true : false;
    }
};

setInterval(function() {
    // Query /dev/input/by-id/ for new connected devices and open readstream on new ones
    fs.readdir('/dev/input/by-id', (err, files) => {
        if(err)
            return;
        files.forEach(file => {
            if(fs.lstatSync('/dev/input/by-id/'+file).isDirectory())
                return;
            if(monitoredDevices.hasDevice('/dev/input/by-id/'+file))
                return;
            monitoredDevices.addDevice('/dev/input/by-id/'+file);
            console.log('EVENT:', file);
        });
    });
}, 500);


let _shutdownInProcess = false;
function exitHandler(signal='') {
    if(!_shutdownInProcess) {
        _shutdownInProcess = true;
        for(let i = 0; i < monitoredDevices.devices.length; i++) {
            console.log('Closing ' + monitoredDevices.devices[i].path + ' (' + monitoredDevices.devices[i].fd +')', fs.closeSync(monitoredDevices.devices[i].fd));
            monitoredDevices.devices[i].stream.close();
        }
        console.log('\033[31m Caught exit signal ' + signal + ', closing... \x1b[0m');
        process.exit();
    }
}

// Comment out the events and the process exist properly
process.on('SIGINT', function() { exitHandler('SIGINT'); });
process.on('SIGUSR1', function() { exitHandler('SIGUSR1'); });
process.on('SIGUSR2', function() { exitHandler('SIGUSR2'); });
process.on('SIGTERM', function() { exitHandler('SIGTERM'); });

这显然需要 root 权限才能读取输入事件。 当我 CTRL+C(或执行killall node)时,exitHandler 被触发,但它在红色控制台日志之后立即停止,我无法在该终端中执行任何操作。只有killall -9 node 有效。

我在这里发现了一个类似的问题:Node.js process.exit() will not exit with a createReadStream open

但这并没有真正解决问题,只是强制一些异常终止进程。有什么方法可以强制流在退出之前关闭?不幸的是,我需要这些信号监听器,因为我在退出之前使用它们来做其他事情。

【问题讨论】:

    标签: node.js fs


    【解决方案1】:

    这似乎是 node.js 核心中的一个错误,从一开始就存在,并且不打算修复(Readline 文档上有关于它的警告)。所以我使用了一个简单的cat 子进程并使用它的标准输出而不是创建我自己的流,就像一个魅力但不是检查流本身是否有 ENODEV 错误,你需要检查进程退出,因为 cat 只存在于设备不复存在了。

    dev.proc = require('child_process').spawn('cat', [path]);
    dev.stream = dev.proc.stdout;
    

    【讨论】:

    猜你喜欢
    • 2015-08-31
    • 2020-06-24
    • 1970-01-01
    • 2011-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-26
    相关资源
    最近更新 更多