UNIX非阻塞I / O:O_NONBLOCK与FIONBIO

时间:2009-07-19 19:17:09

标签: c network-programming unix sockets

在我在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方法的那个方面没有对话的程度。

3 个答案:

答案 0 :(得分:124)

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

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

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

答案 1 :(得分:6)

我相信 fcntl() 是一个POSIX函数。其中ioctl()是标准的UNIX事物。以下是POSIX io的列表。 ioctl() 是特定于内核/驱动程序/操作系统的东西,但我确信您使用的是大多数Unix版本。其他一些ioctl()内容可能只适用于某些操作系统,甚至可能适用于某些操作系统内核。

答案 2 :(得分:5)

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