为什么UNIX文件描述符没有使用它们自己的类型实现,特别是在C++中?

21

我最近使用了大量的文件描述符,我一直在思考它们为什么被实现成整数?

这意味着它们很容易与其他整数混淆,并且没有办法在没有上下文的情况下知道它们是什么、指向什么、是否打开等等。

在 C 语言中,FILE 是一个不透明的结构体类型。许多人也使用 typedef 将例如 status_t 定义为整数,以便他们的函数目的明显。似乎最好的方法是将其实现为不透明类型,或者(例如在 C++ 中)实现为一个类,可以处理部分实现并使 namespace 更加干净(调用 pipe()open() 似乎很无害,但如果没有上下文,不清楚正在进行管道传输或打开哪个文件)。就像 std::file_descriptor 一样,具有用于创建管道或打开文件等的构造函数/工厂函数。

我希望这个问题适用于这个网站;我试图将其表述为“为什么做出这个特定的决定?”如果有人知道更适合的地方,请告诉我。


11
为什么?因为POSIX没有定义特定于C ++的API。 - R. Martinho Fernandes
4
C和C++都没有定义文件描述符;POSIX是目前标准化这个概念的(顺便说一下,它可能存在于任何给定的操作系统上或不存在)。 - PlasmaHH
1
@FrankH。除此之外,在修复时,“int”仅为16位(且内部实现将其限制为19个文件)。 - James Kanze
@Useless:typedef并不会创建一个新的类型,它只是为现有的类型创建一个新的名称。typedef int filedes_t并不能解决OP所问的任何问题。 - Keith Thompson
@KeithThompson 没错,但是 OP 特别提到了 typedef 以及不透明结构体。 - Useless
显示剩余5条评论
3个回答

27

历史遗留问题。在20世纪70年代,仅使用 int (实际上该值是一个索引,指向固定大小的表)似乎并不是问题。后来,将其更改为其他类型将会破坏代码。


10
仍然似乎没有问题 :) - user405725
4
确实,我记不得曾经因为像++fd这样的代码中的fd是一个int而导致出错。 - James Kanze
@JamesKanze - 我有过这样的经历。那是一个“关闭所有打开的文件描述符”的函数,它遍历了一些之前被限制数量的文件描述符。然后有人决定,既然文件描述符数量受到资源限制,那就把最大可能的文件描述符改成MAX_INT。这会有什么问题呢?! - Julie in Austin

19

您的问题可以分为两个部分:

为什么POSIX文件描述符是int类型?

像大多数已经建立的工具和库一样,答案可能是历史原因。詹姆斯的回答指出了这一点。

使文件描述符不透明可能是一个好主意,但不是您提到的原因。使类型不透明是为了基于某些参数拥有不同的类型。例如,在某些系统上,您可能希望使用long long作为文件描述符。然而,正如似乎发生的那样,没有人需要同时打开20亿个文件,因此没有人关心解决这个不存在的问题。

另一方面,typedef int file_descriptor;这样的东西不会解决您上述提到的任何问题:

这意味着它们很容易与其他整数混淆...

如果您混淆了变量,我有一个坏消息要告诉您。编译器也无法帮助您,因为file_descriptorint是相同的类型,因此对其中一个的任何操作都是允许的。

没有上下文,你无法知道这些指针指向什么,它们是否打开等等。

使用 FILE 也无法做到。这就是为什么有函数查询所需的信息并返回它,就像使用 FILE 一样。 使用 typedef 类型别名不会给您任何额外的信息。

为什么 POSIX 没有 C++ 封装器?

简而言之,因为除了 Microsoft 以外,没有操作系统开发人员热爱 C++。Windows 几乎不是 POSIX,因此 Microsoft 试图改进任何 POSIX 并没有希望。其他遵循 POSIX 的操作系统具有 C API 作为事实上的系统编程语言(部分原因是因为几乎所有语言都可以绑定到 C,正如 n.m. 所说)。

事实上,C++在应用程序开发者中很受欢迎,但在系统程序员中并不是那么受欢迎。POSIX委员会似乎对C++也没有特别感兴趣。这就是为什么您会看到关于POSIX API的解决方案和争论仅涉及C语言。还要注意,POSIX是创建用来规范UNIX接口的, 而UNIX主要使用C语言编写,它最重要的后代之一——Linux也与C语言紧密相关。

4
另外一点:UNIX最初开发时还没有C++。那个时候Bjarne在欧洲。 - xis
2
大多数来源都认为Java比C++更受欢迎。但是,POSIX为什么应该在支持Java之前支持C++呢?(它不应该这样做;它定义了任何语言都可以绑定到的API)。 - n. m.
@n.m. 哪些来源?如果我四处看看,除了 Web 服务器和智能手机中的小型嵌入式应用程序,Java 似乎几乎已经死亡。而且 Java 创建自己的环境,独立于周围的操作系统。据我所知,C 主要用于 Linux 内核出于历史原因。除了 Linux,大多数新的 Linux 开发都是使用 C++。(在 pthread_cleanup_push/pthread_cleanup_pop 的理由中,Posix 甚至声明理想的解决方案将涉及异常——这强烈表明他们喜欢 C++。) - James Kanze
  • 为了获得漂亮的徽章,给出一个好答案。这真是一个非常棒的答案。啊哈,我注意到你是博士!这就是你拥有深度知识的原因。
- Grijesh Chauhan
@Shahbaz: "尝试为微控制器找到一个C++编译器"。只需简单搜索即可发现许多可能性... - masoud
显示剩余6条评论

2
Unix接口是用C语言描述的,但同样重要的是系统具有ABI而不仅仅是API。特定于编程语言的高级数据结构会使ABI变得复杂。在ABI级别上,您只有像“32位无符号整数”和简单聚合物等低级数据类型。尽管如此,Unix接口确实使用了像pid_t之类的类型,那么为什么不为文件描述符之类的内容使用其中一个typedef呢?文件描述符具有某些众所周知的值,并且当打开新文件描述符时,始终使用可用的最小正值。文件描述符值有效地充当描述符表中的数组索引,并且设计故意是这样的。程序员对文件描述符的模型是内核中有一个类似数组的结构。dup2函数实际上可以将文件描述符从一个插槽复制到另一个插槽。这样的数组索引也可以是int,负值用于发出错误信号。C typedefs并不提供额外的类型检查,但它们确实带来了一点可读性和抽象:独立于特定的整数类型。fd_t类型在一个系统上可以是int,在另一个系统上可以是long。但是,由于几十年前int已经普遍发展到几乎普遍的32位宽度,因此没有真正需要为了能够在相同名称下使其更宽而进行抽象。程序很少需要打开超过20亿个文件描述符。相比之下,如果使用纯int而不是pthread_t,则对于实现者来说将非常不方便。int描述符对于Windows Socket API的设计者来说确实难以接受,他们发明了一个SOCKET typedef,其值不是最低可用正整数之一;这只是导致可移植性烦恼的怪癖之一。但是,在依赖这些描述符作为范围内的小值的代码中,存在实际的语义差异,该代码将无法工作或效率低下。 Unix曾经有过历史性的例子,即用某些typedef替换纯int类型。例如,在accept函数中,远程地址结构的大小只是int。然后变成了socklen_t。socklen_t不存在技术上的必要性;它被发明为一个临时解决方案,以弥合使用传统int和那些狂热地更改参数以使用size_t的系统之间的差异。尽管这两种类型导致了相同的ABI,但是直到具有64位size_t和32位int的系统出现之前,就没有问题。

1
你能给出你引用这句话的来源链接吗?阅读文章的其余部分可能会很有意思。 - Shahbaz

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