Team-FLY |
4.8 File ControlThe fcntl function is a general-purpose function for retrieving and modifying the flags associated with an open file descriptor. The fildes argument of fcntl specifies the descriptor, and the cmd argument specifies the operation. The fcntl function may take additional parameters depending on the value of cmd . SYNOPSIS #include <fcntl.h> #include <unistd.h> #include <sys/types.h> int fcntl(int fildes, int cmd, /* arg */ ...); POSIX The interpretation of the return value of fcntl depends on the value of the cmd parameter. However, if unsuccessful , fcntl returns “1 and sets errno . The following table lists the mandatory errors for fcntl .
The fcntl function may only be interrupted by a signal when the cmd argument is F_SETLKW (block until the process acquires an exclusive lock). In this case, fcntl returns “1 and sets errno to EINTR . Table 4.3 lists the POSIX values of the cmd parameter for fcntl . An important example of the use of file control is to change an open file descriptor to use nonblocking I/O. When a file descriptor has been set for nonblocking I/O, the read and write functions return “1 and set errno to EAGAIN to report that the process would be delayed if a blocking I/O operation were tried. Nonblocking I/O is useful for monitoring multiple file descriptors while doing other work. Section 4.4 and Section 4.5 discuss the select and poll functions that allow a process to block until any of a set of descriptors becomes available. However, both of these functions block while waiting for I/O, so no other work can be done during the wait. Table 4.3. POSIX values for cmd as specified in fcntl.h .
To perform nonblocking I/O, a program can call open with the O_NONBLOCK flag set. A program can also change an open descriptor to be nonblocking by setting the O_NONBLOCK flag, using fcntl . To set an open descriptor to perform nonblocking I/O, use the F_GETFL command with fcntl to retrieve the flags associated with the descriptor. Use inclusive bitwise OR of O_NONBLOCK with these flags to create a new flags value. Finally, set the descriptor flags to this new value, using the F_SETFL command of fcntl . Example 4.37 setnonblock.cThe following function sets an already opened file descriptor fd for nonblocking I/O. #include <fcntl.h> #include <stdio.h> #include <unistd.h> int setnonblock(int fd) { int fdflags; if ((fdflags = fcntl(fd, F_GETFL, 0)) == -1) return -1; fdflags = O_NONBLOCK; if (fcntl(fd, F_SETFL, fdflags) == -1) return -1; return 0; } If successful, setnonblock returns 0. Otherwise, setnonblock returns “1 and sets errno . The setnonblock function of Example 4.37 reads the current value of the flags associated with fd , performs a bitwise OR with O_NONBLOCK , and installs the modified flags. After this function executes, a read from fd returns immediately if no input is available. Example 4.38 setblock.cThe following function changes the I/O mode associated with file descriptor fd to blocking by clearing the O_NONBLOCK file flag. To clear the flag, use bitwise AND with the complement of the O_NONBLOCK flag. #include <fcntl.h> #include <stdio.h> #include <unistd.h> int setblock(int fd) { int fdflags; if ((fdflags = fcntl(fd, F_GETFL, 0)) == -1) return -1; fdflags &= ~O_NONBLOCK; if (fcntl(fd, F_SETFL, fdflags) == -1) return -1; return 0; } If successful, setblock returns 0. If unsuccessful, setblock returns “1 and sets errno . Example 4.39 process_or_do_work.cThe following function assumes that fd1 and fd2 are open for reading in nonblocking mode. If input is available from either one, the function calls docommand with the data read. Otherwise, the code calls dosomething . This implementation gives priority to fd1 and always handles input from this file descriptor before handling fd2 . #include <errno.h> #include <unistd.h> #include "restart.h" void docommand(char *, int); void dosomething(void); void process_or_do_work(int fd1, int fd2) { char buf[1024]; ssize_t bytesread; for ( ; ; ) { bytesread = r_read(fd1, buf, sizeof(buf)); if ((bytesread == -1) && (errno != EAGAIN)) return; /* a real error on fd1 */ else if (bytesread > 0) { docommand(buf, bytesread); continue; } bytesread = r_read(fd2, buf, sizeof(buf)); if ((bytesread == -1) && (errno != EAGAIN)) return; /* a real error on fd2 */ else if (bytesread > 0) docommand(buf, bytesread); else dosomething(); /* input not available, do something else */ } } |
Team-FLY |