同步——记录锁

记录锁是读写锁的一种扩展类型,它可用于有亲缘关系或无亲缘关系的进程之间共享某个文件的读与写。被锁住的文件通过其描述符访问,执行上锁操作的函数为fcntl。该种类型的锁通常在内核中维护,其属主是由属主的进程ID标识,可用于不同进程间的上锁。

Posix记录锁定义了一个特殊的字节范围以指定整个文件,它的起始偏移为0,长度也为0;粒度用于标记能被锁住的对象的大小,对于Posix记录锁来说,粒度就是1byte。

#include <fcntl.h>
struct flock{
       short l_type; /* F_RDLCK, F_WRLCK, F_UNLCK */
       short l_whence; /* SEEK_SET, SEEK_CUR, SEEK_END */
       off_t l_start; 
       off_t l_end; /* #bytes, 0 means until e-o-f */
       pid_t l_pid; /* PID returned by F_GETLK */
};
int fcntl(int fd, int cmd, .../*struct flock *arg */); // 成功返回值取决于cmd,出错-1
       
cmd参数共有三个值:F_SETLK, F_GETLK, F_SETLKW(wait)

F_SETLK: 获取type或释放锁

F_SETLKW:与F_SETLK类似,会阻塞到该锁能够授权为止

F_GETLK:检查由arg指向的锁以确定是否有某个已存在的锁会妨碍将新锁授予调用进程。若不存在,由arg指向的flock结构的type成员就被置为F_UNLCK;否则返回已存在锁的信息,其中包括持有锁的进程ID

调用进程已持有的针对同一字节范围的锁不会妨碍它获取新锁,同一进程内后执行的获取锁命令覆盖先执行的命令。

fcntl记录上锁既可用于读也可用于写,对于一个文件的任意字节,最多只能存在一种类型的锁。而且,一个给定字节可以有多个读出锁,但只能有一个写入锁。自然,当一个描述符不是打开来读时,对它请求一个读出锁,错误发生;同样当一个描述符不是打开来写时,请求写入锁也会发生错误。对于一个打开着某个文件的给定进程来说,当它关闭该文件的所有描述符或它本身终止时,与该文件关联的所有锁都被删除。锁不能通过fork由子进程继承。

#include	"unpipc.h"

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;				/* write lock entire file */

	Fcntl(fd, F_SETLKW, &lock);
}

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;				/* unlock entire file */

	Fcntl(fd, F_SETLK, &lock);
}
Posix记录上锁称为劝告性上锁,内核维护着已有各个进程上锁的所有文件的正确信息,但是它不能防止拥有足够权限的其他进程乱来。Posix记录上锁对于协作进程足够了

记录上锁的一个常见用途是确保某个程序在任何时刻只有一个副本在运行,常用于守护进程

#include	"unpipc.h"

#define	PATH_PIDFILE	"pidfile"

int
main(int argc, char **argv)
{
	int		pidfd;
	char	line[MAXLINE];

		/* 4open the PID file, create if nonexistent */
	pidfd = Open(PATH_PIDFILE, O_RDWR | O_CREAT, FILE_MODE);

		/* 4try to write lock the entire file */
	if (write_lock(pidfd, 0, SEEK_SET, 0) < 0) {
		if (errno == EACCES || errno == EAGAIN)
			err_quit("unable to lock %s, is %s already running?",
					 PATH_PIDFILE, argv[0]);
		else
			err_sys("unable to lock %s", PATH_PIDFILE);
	}

		/* 4write my PID, leave file open to hold the write lock */
	snprintf(line, sizeof(line), "%ld\n", (long) getpid());
	Ftruncate(pidfd, 0);
    Write(pidfd, line, strlen(line));

	/* then do whatever the daemon does ... */

	pause();
}


你可能感兴趣的:(同步,Linux编程,unix网络编程)