UNIX非阻塞I / O:O_NONBLOCK与FIONBIO


92

在BSD套接字编程的上下文中,我遇到的每个示例和讨论中,似乎都建议将文件描述符设置为非阻塞I / O模式的推荐方法是使用O_NONBLOCK标志fcntl(),例如

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

我从事UNIX的网络编程已经有十多年了,并且一直使用该FIONBIO ioctl()调用来执行此操作:

int opt = 1;
ioctl(fd, FIONBIO, &opt);

从来没有真正考虑过为什么。只是这样学习而已。

有没有人对一个或另一个可能有的优点发表评论?我认为可移植性的位置有所不同,但不知道在何种程度ioctl_list(2)上没有谈到单个ioctl方法的这一方面。

Answers:


134

在标准化之前有ioctl(... FIONBIO... )fcntl(... O_NDELAY... ),但这些表现不一致系统之间,以及同一系统即使在。例如,FIONBIO在套接字O_NDELAY上工作和在tty 上工作是很常见的,在管道,fifo和设备等方面存在很多不一致之处。而且,如果您不知道自己拥有哪种文件描述符,则必须同时设置两者以确保。但是此外,还不一致地指示了无可用数据的非阻塞读取。根据操作系统和文件描述符的类型,读取可能返回0,或者对于errno EAGAIN返回-1,或者对于errno EWOULDBLOCK返回-1。即使在今天,FIONBIO还是O_NDELAY在Solaris上,导致没有数据的读取在tty或管道上返回0,在套接字上返回errno EAGAIN则返回-1。但是0是模棱两可的,因为EOF也返回0。

POSIX通过引入来解决此问题O_NONBLOCK,该规范具有跨不同系统和文件描述符类型的标准化行为。因为现有系统通常希望避免对行为进行任何更改,否则可能会破坏向后兼容性,因此POSIX定义了一个新标志,而不是强制其他行为之一。某些系统(例如Linux)将这3个都相同,并且将EAGAIN和EWOULDBLOCK定义为相同的值,但是当希望使用较旧的机制时,希望维护其他一些旧行为以实现向后兼容性的系统也可以这样做。

新程序应使用fcntl(... O_NONBLOCK... ),在POSIX标准。


6
我倾向于为此使用ioctl(),因为它仅花费一个syscall来启用非阻塞模式,而不花费我两个fcntl()。另外,就此功能而言,Windows ioctlsocket()API与ioctl()等效。
Wez Furlong 2012年

nginx会这样做,并用注释“ ioctl(FIONBIO)设置单个syscall的非阻塞模式”对其进行标记。现在有accept2,它允许您接受连接并将其置于同一系统调用中的非阻塞模式。
Eloff 2014年

6

正如@Sean所说,fcntl()它在很大程度上是标准化的,因此可以跨平台使用。该ioctl()函数早fcntl()于Unix,但完全没有标准化。这将ioctl()在所有为你工作相关的平台,你是幸运的,但不能保证。特别是,用于第二个参数的名称是不可思议的,并且在各个平台之间都不可靠。实际上,它们通常是文件描述符引用的特定设备驱动程序所独有的。(例如,ioctl()用于运行在20年前运行PNX(Perq Unix)的ICL Perq上的位图图形设备的调用从未转换为其他任何地方。)


6

我相信fcntl()是POSIX函数。哪里ioctl()是标准UNIX?这是POSIX io的列表。 ioctl()是一个非常特定于内核/驱动程序/ OS的东西,但是我确定您使用的东西可以在大多数Unix版本上使用。其他一些ioctl()内容可能仅适用于某些操作系统,甚至其内核的某些版本。


我在AIX,Solaris,Linux,* BSD和IRIX上使用FIONBIO都没有问题。但是,是的,据我所知,例如,它在Windows上不起作用–它是非常特定的内核实现的低级接口。不过,我想知道是否还有其他差异化因素。
Alex Balashov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.