c - UNIX非阻塞I / O:O_NONBLOCK与FIONBI

在我在BSD套接字编程的上下文中运行的每个示例和讨论中,似乎将文件描述符设置为非阻塞I / O模式的推荐方法是使用ioctl_list(2)标志来实现例如ioctl

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

我已经在UNIX中进行了十多年的网络编程,并且一直使用ioctl_list(2)调用来执行此操作:

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

从来没有真正考虑过为什么。 刚学会这种方式。

有没有人对其中一个或两个可能各自的优点有任何评论? 我想可移植性轨迹有所不同,但不知道ioctl_list(2)在多大程度上不涉及个别ioctl方法的那个方面。

Alex Balashov asked 2019-09-04T14:51:53Z
3个解决方案
123 votes

在标准化之前,有fcntl( ... O_NONBLOCK ... )fcntl( ... O_NDELAY ... ),但这些在系统之间表现不一致,甚至在同一系统中也是如此。 例如,FIONBIO通常用于套接字,而O_NDELAY用于处理ttys,管道,fifos和设备等方面存在很多不一致之处。 如果你不知道你有什么样的文件描述符,你必须设置两者以确定。 但此外,没有数据的非阻塞读取也表明不一致; 取决于操作系统和文件描述符的类型,read可以返回0,或者使用errno EAGAIN返回-1,或者使用errno EWOULDBLOCK返回-1。 即使在今天,在Solaris上设置FIONBIOO_NDELAY会导致没有数据的读取在tty或管道上返回0,或者在套接字上使用errno EAGAIN返回-1。 但是0是不明确的,因为它也返回EOF。

POSIX通过引入fcntl(解决了这个问题,该文档具有跨不同系统和文件描述符类型的标准化行为。 由于现有系统通常希望避免对可能破坏向后兼容性的行为进行任何更改,因此POSIX定义了一个新标志,而不是强制要求其他任何一个行为的特定行为。 像Linux这样的系统对所有3个系统都是相同的,并且还将EAGAIN和EWOULDBLOCK定义为相同的值,但是为了向后兼容而希望保留其他一些遗留行为的系统可以在使用旧机制时这样做。

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

mark4o answered 2019-09-04T14:52:40Z
6 votes

我相信ioctl()是POSIX功能。 ioctl()是标准UNIX的东西。 这是POSIX io的列表。 ioctl()是一个非常内核/驱动程序/操作系统特定的东西,但我相信你使用的东西适用于大多数Unix版本。 其他一些东西可能只适用于某些操作系统甚至某些内核的转速。

EdH answered 2019-09-04T14:53:20Z
5 votes

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

Jonathan Leffler answered 2019-09-04T14:53:54Z
translate from https://stackoverflow.com:/questions/1150635/unix-nonblocking-i-o-o-nonblock-vs-fionbio