Two ways to make “slow” systems calls nonblocking:
open()
with O_NONBLOCK
fcntl()
to turn on O_NONBLOCK
file status flag
Nonblocking slow system call returns -1 with errno
set to EAGAIN
if it would have blocked
set_fl()
and clr_fl()
wrappers on fcntl()
from APUE:
#include "apue.h"
#include <fcntl.h>
void set_fl(int fd, int flags) /* flags are file status flags to turn on */
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
err_sys("fcntl F_GETFL error");
val |= flags; /* turn on flags */
if (fcntl(fd, F_SETFL, val) < 0)
err_sys("fcntl F_SETFL error");
}
void clr_fl(int fd, int flags) /* flags are file status flags to turn off */
{
int val;
if ((val = fcntl(fd, F_GETFL, 0)) < 0)
err_sys("fcntl F_GETFL error");
val &= ~flags; /* turn flags off */
if (fcntl(fd, F_SETFL, val) < 0)
err_sys("fcntl F_SETFL error");
}
Nonblocking write example:
#include "apue.h"
#include <errno.h>
#include <fcntl.h>
char buf[500000];
int
main(void)
{
int ntowrite, nwrite;
char *ptr;
ntowrite = read(STDIN_FILENO, buf, sizeof(buf));
fprintf(stderr, "read %d bytes\n", ntowrite);
set_fl(STDOUT_FILENO, O_NONBLOCK); /* set nonblocking */
ptr = buf;
while (ntowrite > 0) {
errno = 0;
nwrite = write(STDOUT_FILENO, ptr, ntowrite);
fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);
if (nwrite > 0) {
ptr += nwrite;
ntowrite -= nwrite;
}
}
clr_fl(STDOUT_FILENO, O_NONBLOCK); /* clear nonblocking */
exit(0);
}
Consider the read-write loop in cat
:
while ((n = read(STDIN_FILENO, buf, BUFSIZ)) > 0)
if (write(STDOUT_FILENO, buf, n) != n)
err_sys("write error");
How would netcat (nc
) perform two such loops at the same time?
select()
API for I/O multiplexing:
#include <sys/select.h>
int select(int maxfdp1, // max fd plus 1, or simply pass FD_SETSIZE
fd_set *restrict readfds, // see if they're ready for reading
fd_set *restrict writefds, // see if they're ready for writing
fd_set *restrict exceptfds, // see if exceptional condition occurred
// ex) urgent out-of-band data in TCP
struct timeval *restrict tvptr); // timeout
// Returns: count of ready descriptors, 0 on timeout,–1 on error
int FD_ISSET(int fd, fd_set *fdset);
// Returns: nonzero if fd is in set, 0 otherwise
void FD_CLR(int fd, fd_set *fdset);
void FD_SET(int fd, fd_set *fdset);
void FD_ZERO(fd_set *fdset);
select()
API:
maxfdp1
parameter - just pass FD_SETSIZE
, which is 1024poll()
does the same thing and has a better APIselect()
(as well as poll()
) will always get interrupted on signals,
even when the SA_RESTART
flag was usedNonblocking write example, augmented with select()
:
ptr = buf;
while (ntowrite > 0) {
errno = 0;
// Wait until stdout is ready before we call write()
fd_set write_fds;
FD_ZERO(&write_fds);
FD_SET(STDOUT_FILENO, &write_fds);
select(STDOUT_FILENO + 1, NULL, &write_fds, NULL, NULL);
nwrite = write(STDOUT_FILENO, ptr, ntowrite);
fprintf(stderr, "nwrite = %d, errno = %d\n", nwrite, errno);
if (nwrite > 0) {
ptr += nwrite;
ntowrite -= nwrite;
}
}
Last updated: 2023-02-09