读书笔记:第9章 记录上锁 (5)

《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顺序处理上锁请求的,而不管上锁请求的类型。

你可能感兴趣的:(读书笔记,《UNIX网络编程》)