当侦听出口事件时,node.js createReadStream会挂起process.exit()

时间:2019-11-19 11:26:43

标签: node.js fs

我正在尝试从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

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

1 个答案:

答案 0 :(得分:0)

这似乎是node.js内核内部的错误,该错误从开始就存在,并且没有计划予以修复(Readline文档中对此有警告)。因此,我使用了一个简单的cat子进程,并使用了它的stdout而不是创建自己的流,就像一个符咒一样,但是除了检查流本身是否存在ENODEV错误外,您还需要检查进程退出,因为cat已经存在当设备停止存在时。

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