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

《UNIX网络编程:卷2》

强制性上锁

--------------------------------------------

/*
 * lockmain.c
 * 图9-2 文件上锁例子的main函数
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define MAXLINE 1024
#define SEQFILE	"seqno"

void my_lock(int fd);
void my_unlock(int fd);

int main(int argc, char *argv[])
{
	int			fd;
	long		i, seqno;
//	pid_t		pid;
	ssize_t		n;
	char		line[MAXLINE + 1];
	int			times;

	if (argc != 2) {
		fprintf(stderr, "loopnone [num]\n");
		exit(1);
	}
	times = atoi(argv[1]);

//	pid = getpid();					// 获取进程ID
	// 打开文件
	if ((fd = open(SEQFILE, O_RDWR, FILE_MODE)) < 0) {
		fprintf(stderr, "open %s error: %s\n",  SEQFILE, strerror(errno));
		exit(1);
	}

	for (i = 0; i < times; i++) {
		my_lock(fd);									// 锁定文件

		lseek(fd, 0L, SEEK_SET);						//定位到文件开始位置
		if ((n = read(fd, line, MAXLINE)) < 0) {		// 读取一行
			fprintf(stderr, "read error: %s\n", strerror(errno));
			exit(1);
		}
		line[n] = '\0';

		n = sscanf(line, "%ld\n", &seqno);				// 将字符转换成数字
//		printf("%s: pid = %d, seq# = %ld\n", argv[0], pid, seqno);

		seqno++;										// 加1

		snprintf(line, sizeof(line), "%ld\n", seqno);	// 将数字写回缓冲区
		lseek(fd, 0L, SEEK_SET);						// 定位到文件开始处
		if (write(fd, line, strlen(line)) < 0) {		// 将缓冲区内容写回文件
			fprintf(stderr, "write error: %s\n", strerror(errno));
			exit(1);
		}

		my_unlock(fd);				// 解锁文件
	}
	exit(0);
}

/*
 * locknone.c
 * P155 图9-1 不上锁的函数
 */
void my_lock(int fd)
{
	return;		// 不做任何事情,直接返回
}

void my_unlock(int fd)
{
	return;		// 不做任何事情,直接返回
}

---------------------------------------------------------------

/*
 * lockmain.c
 * 图9-2 文件上锁例子的main函数
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>

#define FILE_MODE	(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
#define MAXLINE 1024
#define SEQFILE	"seqno"

void my_lock(int fd);
void my_unlock(int fd);

int main(int argc, char *argv[])
{
	int			fd;
	long		i, seqno;
//	pid_t		pid;
	ssize_t		n;
	char		line[MAXLINE + 1];
	int			times;

	if (argc != 2) {
		fprintf(stderr, "loopfcntl [num]\n");
		exit(1);
	}
	times = atoi(argv[1]);

//	pid = getpid();					// 获取进程ID
	// 打开文件
	if ((fd = open(SEQFILE, O_RDWR, FILE_MODE)) < 0) {
		fprintf(stderr, "open %s error: %s\n",  SEQFILE, strerror(errno));
		exit(1);
	}

	for (i = 0; i < times; i++) {
		my_lock(fd);									// 锁定文件

		lseek(fd, 0L, SEEK_SET);						//定位到文件开始位置
		if ((n = read(fd, line, MAXLINE)) < 0) {		// 读取一行
			fprintf(stderr, "read error: %s\n", strerror(errno));
			exit(1);
		}
		line[n] = '\0';

		n = sscanf(line, "%ld\n", &seqno);				// 将字符转换成数字
//		printf("%s: pid = %d, seq# = %ld\n", argv[0], pid, seqno);

		seqno++;										// 加1

		snprintf(line, sizeof(line), "%ld\n", seqno);	// 将数字写回缓冲区
		lseek(fd, 0L, SEEK_SET);						// 定位到文件开始处
		if (write(fd, line, strlen(line)) < 0) {		// 将缓冲区内容写回文件
			fprintf(stderr, "write error: %s\n", strerror(errno));
			exit(1);
		}

		my_unlock(fd);				// 解锁文件
	}
	exit(0);
}

/*
 * lockfcntl.c
 * P155 图9-1 不上锁的函数
 */
void my_lock(int fd)
{
	struct flock	lock;

	lock.l_type = F_WRLCK;		// 写锁
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	// 加锁整个文件
	if (fcntl(fd, F_SETLKW, &lock) < 0) {
		fprintf(stderr, "fcntl error: %s\n", strerror(errno));
	}
	return;		// 不做任何事情,直接返回
}

void my_unlock(int fd)
{
	struct flock	lock;

	lock.l_type = F_UNLCK;		// 解锁
	lock.l_whence = SEEK_SET;
	lock.l_start = 0;
	lock.l_len = 0;

	// 解锁整个文件
	if (fcntl(fd, F_SETLK, &lock) < 0) {
		fprintf(stderr, "fcntl error: %s\n", strerror(errno));
	}
	return;		// 不做任何事情,直接返回
}

----------------------------------------------------------------

运行程序:

$ cat > seqno
1
[^D								Ctrl+D文件结束符]
$ chmod g-x,g+s seqno 			关掉组成员执行位,打开SGID位
$ ls -l seqno 
-rw-rwSr-- 1 user user 2  8月 24 16:45 seqno
$ ./loopfcntl 10000 & ./loopnone 10000 &
[1] 3126
[2] 3127
$ 								等待,然后回车
[1]-  完成                  ./loopfcntl 10000
[2]+  完成                  ./loopnone 10000
$ cat seqno 
13111

当loopnone调用read后,内核切换到loopfcntl,loopfnctl增加文件中的数值,然后切换回loopnone,loopnone将之前读入的值加1后写入文件。此时问题就发生了。

如果多个进程在更新一个文件时,那么所有进程必须使用某种上锁形式协作。只要有一个进程违规就可能引发混乱。


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