UNIX非阻塞I/O:O_NONBLOCK与FIONBIO

Ale*_*hov 90 c unix sockets network-programming

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

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
Run Code Online (Sandbox Code Playgroud)

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

int opt = 1;
ioctl(fd, FIONBIO, &opt);
Run Code Online (Sandbox Code Playgroud)

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

有没有人对其中一个或两个可能各自的优点有任何评论?我认为可移植性轨迹在某种程度上有所不同,但不知道在多大程度上与ioctl_list(2)单个ioctl方法的那个方面没有对话.

mar*_*k4o 129

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

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

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

  • 我倾向于使用ioctl(),因为它只花费一个系统调用来启用非阻塞模式而不是两个用于fcntl().此外,出于此功能的目的,Windows ioctlsocket()API等同于ioctl(). (6认同)

EdH*_*EdH 6

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


Jon*_*ler 5

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