UNIX非阻塞I / O:O_NONBLOCK与FIONBIO

在每一个在BSD套接字编程环境中运行的例子和讨论中,似乎将文件描述符设置为非阻塞I / O模式的推荐方式是使用fcntl()O_NONBLOCK标志,例如

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

我已经在UNIX上进行了十多年的networking编程,并一直使用FIONBIO ioctl()调用来执行此操作:

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

从来没有真正想过为什么。 刚刚就这样学习了。

有没有人有任何评论可能各自的优点之一? 我认为可移植性轨迹有所不同,但不知道ioctl_list(2)对于个体ioctl方法的方面没有说明的程度。

在标准化之前,有ioctl(FIONBIO)fcntl(O_NDELAY) ,但是这些系统之间的行为不一致,甚至在同一个系统内。 例如, FIONBIO通常在FIONBIO上工作, O_NDELAY在ttys上工作,pipe道,fifo和设备之类的东西很多不一致。 如果你不知道你有什么types的文件描述符,那么你必须确定这两个文件描述符。 但是,另外,还没有提供可用数据的非阻塞读取也不一致; 取决于操作系统和文件描述符的types,读取可能返回0,或者带有errno EAGAIN的-1,或者带有errno EWOULDBLOCK的-1。 即使在今天,在Solaris上设置FIONBIOO_NDELAY FIONBIO导致无数据的读取在tty或pipe上返回0,或在套接字上使用errno EAGAIN返回-1。 然而0是不明确的,因为它也返回了EOF。

POSIX通过引入O_NONBLOCK解决这个问题, O_NONBLOCK在不同的系统和文件描述符types中具有标准化的行为。 因为现有的系统通常要避免任何可能破坏向后兼容性的行为改变,所以POSIX定义了一个新的标志而不是强制其他标志的特定行为。 像Linux这样的系统对待所有3都是一样的,并且也将EAGAIN和EWOULDBLOCK定义为相同的值,但是为了向后兼容而维护一些其他遗留行为的系统可以在使用较旧的机制时这样做。

新程序应该使用POSIX标准化的fcntl(O_NONBLOCK)

我相信fcntl()是一个POSIX函数。 作为ioctl()是一个标准的UNIX的东西。 这是一个POSIX io的列表。 ioctl()是一个非常内核/驱动程序/操作系统的具体事情,但我相信你使用什么工作在大多数Unix的口味。 一些其他ioctl()东西可能只适用于某些操作系统,甚至某些内核的转速。

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