【发布时间】: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
但这并没有真正解决问题,只是强制一些异常终止进程。有什么方法可以强制流在退出之前关闭?不幸的是,我需要这些信号监听器,因为我在退出之前使用它们来做其他事情。
【问题讨论】: