node.js 里使用 fifo

0. 需求

前两天 Showman 遇到一个需求:

  1. 我们需要在服务器端录制视频
  2. 录制视频的过程主要由 node.js 控制,借助 puppeteer 操作浏览器
  3. 但是也会需要执行一些 shell 命令,此时为安全考虑,我们会启动一个封闭的临时环境给用户执行
  4. 这些封闭环境是用户进程间共用的,不会随时启动随时销毁
  5. 所以 node.js 就需要在其它环境里执行一些操作,返回内容,等待执行完毕后再继续下面的

于是我的同事就让我用 fifo。

1. 什么是 fifo

我以前没有用过 fifo,所以搜索了一下。

FIFO 特殊文件(同具名管道)与管道类似,只是可以用访问文件系统的方式来访问它。它可以被多个进程同时打开和读写。当进程通过 FIFO 交换数据时,内核将直接在内部交换数据,而不会写入到文件系统中。因此,FIFO 特殊文件在文件系统中没有内容;文件系统的入口(即文件)只是作为引用方式,让各进程能够使用文件名来访问管道。

原文:https://man7.org/linux/man-pages/man7/fifo.7.html

管道大家应该都知道,把 A 进程的输出直接输入到 B 进程里,加快处理速度。fifo 与管道的差别就是 fifo 可以通过文件路径直接访问,用起来更简单。

2. 在命令行里使用 fifo

创建 fifo,使用 mkfifo 命令:

mkfifo xxx.fifo

写入内容到 fifo:

echo "something" > xxx.fifo

读取 fifo:

cat xxx.fifo

因为 fifo 是管道,内容直接走内核,所以实际上硬盘上不会存储任何内容。如果我们在写入之后再 cat fifo,就不会得到任何内容。

3. 在 node.js 里使用 fifo

在 node.js 里使用 fifo 需要用 fs.opennet.Socket。因为我需要在执行完毕后继续下一步,所以进行了 Promise 封装:

try {
  // 为避免执行时间过长导致进程超时,不断输出些内容
  const interval = setInterval(() => {
    log('termlang is processing...');
  }, 3E4);
  await new Promise((resolve, reject) => {
    // 打开名为 $basename-$lineno.sp.fifo 的管道
    open('./$basename-$lineno.sp.fifo', constants.O_RDONLY | constants.O_NONBLOCK, (err, fd) => {
      if (err) {
        clearInterval(interval);
        reject(err);
      }
      const pipe = new Socket({fd});
      pipe.on('data', data => {
        data = data.toString();
        // 以输出内容包括 finished 或 errored 为结束标记
        if (/(finished|errored)/.test(data)) {
          resolve(data);
        }
      });
    });
  });
  clearInterval(interval);
} catch (err) {
  log(err);
}

4. 总结

作为半路出家的前端,我对系统、对 Linux 一直缺乏了解。所以类似管道这种东西,我一直也不太熟悉,这次算学会了一个新技能,记录分享一下。

如果您觉得文章内容对您有用,不妨支持我创作更多有价值的分享:


已发布

分类

来自

评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据