In every example and discussion I run across in the context of BSD socket programming, it seems that the recommended way to set a file descriptor to nonblocking I/O mode is using the O_NONBLOCK
flag to fcntl()
, e.g.
int flags = fcntl(fd, F_GETFL, 0); fcntl(fd, F_SETFL, flags | O_NONBLOCK);
I've been doing network programming in UNIX for over ten years, and have always used the FIONBIO ioctl()
call to do this:
int opt = 1; ioctl(fd, FIONBIO, &opt);
Never really gave much thought to why. Just learned it that way.
Does anyone have any commentary on the possible respective merits of one or the other? I imagine the portability locus differs somewhat, but do not know to what extent as ioctl_list(2)
doesn't speak to that aspect of individual ioctl
methods.
Prior to standardization there was ioctl(
...FIONBIO
...)
and fcntl(
...O_NDELAY
...)
, but these behaved inconsistently between systems, and even within the same system. For example, it was common for FIONBIO
to work on sockets and O_NDELAY
to work on ttys, with a lot of inconsistency for things like pipes, fifos, and devices. And if you didn't know what kind of file descriptor you had, you'd have to set both to be sure. But in addition, a non-blocking read with no data available was also indicated inconsistently; depending on the OS and the type of file descriptor the read may return 0, or -1 with errno EAGAIN, or -1 with errno EWOULDBLOCK. Even today, setting FIONBIO
or O_NDELAY
on Solaris causes a read with no data to return 0 on a tty or pipe, or -1 with errno EAGAIN on a socket. However 0 is ambiguous since it is also returned for EOF.
POSIX addressed this with the introduction of O_NONBLOCK
, which has standardized behavior across different systems and file descriptor types. Because existing systems usually want to avoid any changes to behavior which might break backward compatibility, POSIX defined a new flag rather than mandating specific behavior for one of the others. Some systems like Linux treat all 3 the same, and also define EAGAIN and EWOULDBLOCK to the same value, but systems wishing to maintain some other legacy behavior for backward compatibility can do so when the older mechanisms are used.
New programs should use fcntl(
...O_NONBLOCK
...)
, as standardized by POSIX.
As @Sean said, fcntl()
is largely standardized, and therefore available across platforms. The ioctl()
function predates fcntl()
in Unix, but is not standardized at all. That the ioctl()
worked for you across all the platforms of relevance to you is fortunate, but not guaranteed. In particular, the names used for the second argument are arcane and not reliable across platforms. Indeed, they are often unique to the particular device driver that the file descriptor references. (The ioctl()
calls used for a bit-mapped graphics device running on an ICL Perq running PNX (Perq Unix) of twenty years ago never translated to anything else anywhere else, for example.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With