在Node中向文件描述符3写入数据

3

console.log 使用 process.stdout.write 来写入文件描述符 (fd) 1

console.error 使用 process.stderr.write 来写入文件描述符 (fd) 2

我如何创建一个新的文件描述符3?并向其写入内容?

更新:使用 net

import net from 'net'

const example = new net.Socket({ fd: 3, writable: true })

const buffBaby = Buffer.from('meow', 'utf8')

example.write(buffBaby)

我遇到了这个错误:

错误:ENOTTY:设备不支持的 ioctl,uv_pipe_open

我已经尝试了以下方法:

const fs = require('fs');
const fd3 = fs.createWriteStream(null, {fd: 3});
fd3.write("Hello to FD 3!\n");

错误:ENXIO:没有这样的设备或地址,写入

fd 3 不是进程从父进程继承的众所周知的文件描述符之一。您必须打开一个文件或复制现有的 fd。然后操作系统将分配一个最低可用数字,可能是 3。 - Kenji Noguchi
2个回答

2

我假设你的程序已经有了这样一个FD(文件描述符)的想法,否则你会问如何写入文件或套接字而不是FD。如果是这样,那么以下代码可以解决问题:

const fs = require('fs');
const fd3 = fs.createWriteStream(null, {fd: 3});
fd3.write("Hello to FD 3!\n");

这将创建一个与FD 3关联的新流,就像process.stdout与FD 1关联,process.stderr与FD 2关联一样。

我假设您的FD是阻塞的(在Linux中FD的默认设置)。如果由于某种原因它是非阻塞的,那么您需要将其改回阻塞状态或使用net.Socket

如果您在没有给它写入FD的情况下运行该代码,那么可能会发生类似于以下的糟糕事情:

events.js:292
      throw er; // Unhandled 'error' event
      ^

Error: EINVAL: invalid argument, write
Emitted 'error' event on WriteStream instance at:
    at emitErrorNT (internal/streams/destroy.js:96:8)
    at emitErrorCloseNT (internal/streams/destroy.js:68:3)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {
  errno: -22,
  code: 'EINVAL',
  syscall: 'write'
}
Aborted (core dumped)

或者是这样的:
events.js:292
      throw er; // Unhandled 'error' event
      ^

Error: EBADF: bad file descriptor, close
Emitted 'error' event on WriteStream instance at:
    at emitErrorNT (internal/streams/destroy.js:96:8)
    at emitErrorCloseNT (internal/streams/destroy.js:68:3)
    at processTicksAndRejections (internal/process/task_queues.js:84:21) {
  errno: -9,
  code: 'EBADF',
  syscall: 'close'
}

如果你确实想要从节点打开FD,那么你可以使用任何普通的打开文件的方法来实现。但是,在这种情况下,你无法保证FD 3的可用性,因为Node内部很可能已经在使用FD 3,而你不能将其窃取。


我不认为我完全掌握了FD的工作原理。我的期望是不创建文件(如果需要可能会创建临时文件),并通过命令管道输出可访问的任意字符串。例如 node ./index.js | loop-file-descriptor - ThomasReggi
我已经尝试了 net,但是在问题更新中出现了错误。 - ThomasReggi
@ThomasReggi 如果你正在使用bash的|,那么它总是FD 1,所以只需使用已经存在的内容即可。 - Joseph Sible-Reinstate Monica

1
这个问题困扰了我,因为我没有看到任何可以选择创建fd号的POSIX或其他syscall,比如socketopen - 如果我错了,请纠正我,我想在nodejs文档中澄清net.Socket选项:

fd:(数字)如果指定,则使用给定文件描述符包装现有套接字,否则将创建新套接字。

这意味着它不会在您选择的fd上创建套接字,而是重用现有套接字,我假设这是为了避免为性能原因创建新连接或与其他线程或消费者共享连接。

最后,请注意,每个进程域(或进程控制块)的fd号是唯一的。也就是说,从3开始创建fd,因为0、1和2被保留。基于此,如果您确定正确的打开连接顺序,则可以预先知道fd号。每次打开连接、套接字或文件都会创建一个fd号,如果您不关闭它们。


有一个系统调用可以间接选择FD: dup2。首先使用 open/等方法获取一些FD,然后执行 dup2(fd_from_open, fd_number_you_want);close(fd_from_open);。但是,除非您确定所需的FD号码尚未被Node运行时使用,否则这样做很危险。 - Joseph Sible-Reinstate Monica
谢谢,我想这个完全从我的脑海中滑落了,你说得很对,你可以设置一个在dup2中创建的fd,但像你所说的那样,它是间接的,因为它从未打算这样使用,并且极不推荐。 - Abdullah Shahin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接