我试图研究文件描述符以及它们如何与Rust中的tcp连接交互,我对Rust编程和Unix都很陌生.

最终,我希望通过匹配它们的文件描述符将某个程序的stdio绑定到一个tcp发送/接收.现在,我只想了解文件描述符是如何分配的,以及为什么它们获得不同的值.

我写了这段简单的代码来做实验:

use std::{
    net::TcpListener,
    os::fd::AsFd
};

fn main() {
    let listener = TcpListener::bind("0.0.0.0:1024").unwrap();
    println!("Listener fd: {:?}", listener.as_fd());
    for stream in listener.incoming() {
        let stream = stream.unwrap();
        println!("Stream fd: {:?}", stream.as_fd());
    }
}

当我运行此命令时,我得到打印的第一行:

Listener fd: BorrowedFd { fd: 3 }

当我随后向tcp侦听器发送一些数据时,它会像我期望的那样打印出如下行:

Stream fd: BorrowedFd { fd: 4 }

我想了解的是:

  • 为什么文件描述符不同?
  • 每个文件描述符对应什么,即使用3对应于连接的传入数据,使用4对应于我可以写回流的数据?

推荐答案

文件描述符是内核向某些功能公开的不透明句柄.在您的例子中,文件描述符#3是通过端口的socket()bound获得的.这又允许您在其上调用accept(),以获得可用于与不同连接的客户端进行通信的更多文件描述符(例如您的#4).

文件描述符#4是连接的对等点的句柄.它支持像read()write()这样的操作(以及许多其他操作,比如select()).这些操作通常与对应于打开文件的文件描述符上提供的操作相同,这就是Unix提供多态的方式.

对同一文件描述符的读取和写入不会相互干扰,因为它们是不同的操作,并且操作系统足够智能来区分它们.在4号后面,内核保留了一个完整的数据 struct ,其中包含单独的读写缓冲区、控制流标志等.只有在用户空间中,它才被表示为单个数字,您可以将其视为对保存在内核中的表的索引.

Rust相关问答推荐

Tauri tauri—apps/plugin—store + zustand

如何对字符串引用的引用向量进行排序,而不是对最外层的引用进行排序?

为什么我不能从带有字符串的 struct 的引用迭代器中收集VEC<;&;str&>?

在UdpSocket上使用sendto时的隐式套接字绑定

如何正确地将App handler传递给Tauri中的其他模块?

使用极点数据帧时,找不到枚举结果的方法lazy()

为昂贵的for循环制作筛子

为什么我们需要std::thread::scope,如果我们可以使用thread.join()在函数的生命周期内删除引用?

将serde_json读入`VEC<;T&>;`( rust 色)时出现问题

为什么`AlternateScreen`在读取输入键时需要按Enter键?

rust 蚀生命周期 不匹配-不一定超过此处定义的生命周期

Cargo.toml:如何有条件地启用依赖项功能?

如何将带有嵌套borrow /NLL 的 Rust 代码提取到函数中

为什么将易错函数的泛型结果作为泛型参数传递 infer ()?不应该是暧昧的吗?

`use` 和 `crate` 关键字在 Rust 项目中效果不佳

使用 traits 时,borrow 的值不会存在足够长的时间

是否可以在 Rust 中的特定字符上实现特征?

更好的方法来模式匹配带有 Rust 的窥视孔装配说明窗口?

有没有办法阻止 rust-analyzer 使非活动代码变暗?

类型参数不受 impl 特征、自身类型或谓词的约束