14.1 | The test program is shown in Figure C.14
Figure C.14. Determine record-locking behavior
#include "apue.h" #include <fcntl.h> #include <errno.h> void sigint(int signo) { } int main(void) { pid_t pid1, pid2, pid3; int fd; setbuf(stdout, NULL); signal_intr(SIGINT, sigint); /* * Create a file. */ if ((fd = open("lockfile", O_RDWR|O_CREAT, 0666)) < 0) err_sys("can't open/create lockfile"); /* * Read-lock the file. */ if ((pid1 = fork()) < 0) { err_sys("fork failed"); } else if (pid1 == 0) { /* child */ if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0) err_sys("child 1: can't read-lock file"); printf("child 1: obtained read lock on file\n"); pause(); printf("child 1: exit after pause\n"); exit(0); } else { /* parent */ sleep(2); } /* * Parent continues ... read-lock the file again. */ if ((pid2 = fork()) < 0) { err_sys("fork failed"); } else if (pid2 == 0) { /* child */ if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0) err_sys("child 2: can't read-lock file"); printf("child 2: obtained read lock on file\n"); pause(); printf("child 2: exit after pause\n"); exit(0); } else { /* parent */ sleep(2); } /* * Parent continues ... block while trying to write-lock * the file. */ if ((pid3 = fork()) < 0) { err_sys("fork failed"); } else if (pid3 == 0) { /* child */ if (lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0) printf("child 3: can't set write lock: %s\n", strerror(errno)); printf("child 3 about to block in write-lock...\n"); if (lock_reg(fd, F_SETLKW, F_WRLCK, 0, SEEK_SET, 0) < 0) err_sys("child 3: can't write-lock file"); printf("child 3 returned and got write lock????\n"); pause(); printf("child 3: exit after pause\n"); exit(0); } else { /* parent */ sleep(2); } /* * See if a pending write lock will block the next * read-lock attempt. */ if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0) printf("parent: can't set read lock: %s\n", strerror(errno)); else printf("parent: obtained additional read lock while" " write lock is pending\n"); printf("killing child 1...\n"); kill(pid1, SIGINT); printf("killing child 2...\n"); kill(pid2, SIGINT); printf("killing child 3...\n"); kill(pid3, SIGINT); exit(0); }
On all four systems described in this book, the behavior is the same: additional readers can starve pending writers. Running the program gives us child 1: obtained read lock on file child 2: obtained read lock on file child 3: can't set write lock: Resource temporarily unavailable child 3 about to block in write-lock... parent: obtained additional read lock while write lock is pending killing child 1... child 1: exit after pause killing child 2... child 2: exit after pause killing child 3... child 3: can't write-lock file: Interrupted system call
|
14.6 | Figure C.15 shows an implementation using select.
Figure C.15. Implementation of sleep_us using select
#include "apue.h" #include <sys/select.h> void sleep_us(unsigned int nusecs) { struct timeval tval; tval.tv_sec = nusecs / 1000000; tval.tv_usec = nusecs % 1000000; select(0, NULL, NULL, NULL, &tval); }
Figure C.16 shows an implementation using poll.
Example C.16. Implementation of sleep_us using poll
#include <poll.h> void sleep_us(unsigned int nusecs) { struct pollfd dummy; int timeout; if ((timeout = nusecs / 1000) <= 0) timeout = 1; poll(&dummy, 0, timeout); }
As the BSD usleep(3) manual page states, usleep uses the setitimer interval timer and performs eight system calls each time it's called. It correctly interacts with other timers set by the calling process, and it is not interrupted if a signal is caught. |
14.8 | A solution is shown in Figure C.17.
Figure C.17. Calculation of pipe capacity using nonlocking writes
#include "apue.h" #include <fcntl.h> int main(void) { int i, n; int fd[2]; if (pipe(fd) < 0) err_sys("pipe error"); set_fl(fd[1], O_NONBLOCK); /* * Write 1 byte at a time until pipe is full. */ for (n = 0; ; n++) { if ((i = write(fd[1], "a", 1)) != 1) { printf("write ret %d, ", i); break; } } printf("pipe capacity = %d\n", n); exit(0); }
The following table shows the values calculated for our four platforms. Platform | Pipe Capacity (bytes) |
---|
FreeBSD 5.2.1 | 16,384 | Linux 2.4.22 | 4,096 | Mac OS X 10.3 | 8,192 | Solaris 9 | 9,216 | These values can differ from the corresponding PIPE_BUF values, because PIPE_BUF is defined to be the maximum amount of data that can be written to a pipe atomically . Here, we calculate the amount of data that a pipe can hold independent of any atomicity constraints. |