UNIX nonblocking I/O: O_NONBLOCK vs. FIONBIO

Alex Balashov picture Alex Balashov · Jul 19, 2009 · Viewed 114.9k times · Source

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.

Answer

mark4o picture mark4o · Jul 20, 2009

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.