《UNIX网络编程:卷2》P169:图9-9
它获取某个完整文件的一个写入锁,然后fork两个子进程。第一个子进程首先尝试获取一个写入锁(它将阻塞,因为父进程已持有整个文件的一个读出锁),然后由第二个进程尝试获取一个读出锁。
----------------------------------------
/* * test3.c * P169 图9-9 等待着的写入者是否比等待着的读出者优先 */ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <time.h> #include <sys/time.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define read_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_RDLCK, offset, whence, len) #define readw_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLKW, F_RDLCK, offset, whence, len) #define write_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_WRLCK, offset, whence, len) #define writew_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLKW, F_WRLCK, offset, whence, len) #define un_lock(fd, offset, whence, len) \ lock_reg(fd, F_SETLK, F_UNLCK, offset, whence, len) #define is_read_lockable(fd, offset, whence, len) \ !lock_test(fd, F_RDLCK, offset, whence, len) #define is_write_lockable(fd, offset, whence, len) \ !lock_test(fd, F_WRLCK, offset, whence, len) int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len); int lock_test(int fd, int type, off_t offset, int whence, off_t len); char *gf_time(void); int main(int argc, char *argv[]) { int fd; pid_t pid; /* * 父进程创建文件并获取写入锁 */ if ((fd = open("test1.data", O_RDWR | O_CREAT, FILE_MODE)) < 0) { fprintf(stderr, "open error: %s\n", strerror(errno)); exit(1); } if (write_lock(fd, 0, SEEK_SET, 0) < 0) { fprintf(stderr, "write_lock error: %s\n", strerror(errno)); exit(1); } printf("%s: parent has write lock\n", gf_time()); /* * 创建第一个子进程,睡眠1秒,然后请求整个文件的一个写入锁 * 取得写入锁后,持有2秒,释放它 */ if ((pid = fork()) < 0) { fprintf(stderr, "fork error: %s\n", strerror(errno)); exit(1); } else if (pid == 0) { /* 第一个子进程 */ sleep(1); printf("%s: first child tries to obtain write lock\n", gf_time()); if (writew_lock(fd, 0, SEEK_SET, 0) < 0) { fprintf(stderr, "write_lockw error: %s\n", strerror(errno)); } printf("%s: first child obtains write lock\n", gf_time()); sleep(2); if (un_lock(fd, 0, SEEK_SET, 0) < 0) { fprintf(stderr, "unlock error: %s\n", strerror(errno)); } printf("%s: first child releases write lock\n", gf_time()); exit(0); } /* * 创建第二个子进程,睡眠3秒以允许第一个子进程的写入锁处于待处理状态 * 然后尝试获取文件的一个读出锁,该锁持有4秒时间 */ if ((pid = fork()) < 0) { fprintf(stderr, "fork error: %s\n", strerror(errno)); exit(1); } else if (pid == 0) { /* 第二个子进程 */ sleep(3); printf("%s: second child tries to obtain read lock\n", gf_time()); if (readw_lock(fd, 0, SEEK_SET, 0) < 0) { fprintf(stderr, "readw_lock error: %s: ", strerror(errno)); } printf("%s: second child obtains read lock\n", gf_time()); sleep(4); if (un_lock(fd, 0, SEEK_SET, 0) < 0) { fprintf(stderr, "un_lock error: %s\n", strerror(errno)); } printf("%s: second child releases read lock\n", gf_time()); exit(0); } /* 父进程持有读锁5秒后释放该锁 */ sleep(5); if (un_lock(fd, 0, SEEK_SET, 0) < 0) fprintf(stderr, "un_lock error: %s\n", strerror(errno)); printf("%s: parent release read lock\n", gf_time()); exit(0); } char *gf_time(void) { struct timeval tv; static char str[30]; char *ptr; if (gettimeofday(&tv, NULL) < 0) fprintf(stderr, "gettimeofday error: %s\n", strerror(errno)); ptr = ctime(&tv.tv_sec); strcpy(str, &ptr[11]); snprintf(str+8, sizeof(str) - 8, ".%06ld", tv.tv_usec); return(str); } int lock_reg(int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; return(fcntl(fd, cmd, &lock)); } int lock_test(int fd, int type, off_t offset, int whence, off_t len) { struct flock lock; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; if (fcntl(fd, F_GETLK, &lock) == -1) return(-1); if (lock.l_type == F_UNLCK) return(0); return(lock.l_pid); }
运行程序:
$ ./test3 08:29:10.636042: parent has write lock 08:29:11.637071: first child tries to obtain write lock 08:29:13.637212: second child tries to obtain read lock 08:29:15.637201: first child obtains write lock 08:29:15.637185: parent release read lock $ 08:29:17.637427: first child releases write lock 08:29:17.637450: second child obtains read lock 08:29:21.637659: second child releases read lock
将读锁和写锁的顺序调换后运行程序:
$ ./test3 08:34:10.252801: parent has write lock 08:34:11.253853: second child tries to obtain read lock 08:34:13.253723: first child tries to obtain write lock 08:34:15.253826: second child obtains read lock 08:34:15.253811: parent release read lock $ 08:34:17.254048: second child releases read lock 08:34:17.254162: first child obtains write lock 08:34:21.254364: first child releases write lock
两个测试结果表明,系统是以FIFO顺序处理上锁请求的,而不管上锁请求的类型。