文件锁实现进程间同步
(APUE学习笔记)
APUE第12章讲述了文件锁(所谓的记录锁),一个很直接的用途就是实现无亲缘关系进程之间的同步.事实上,文件锁的开发先于其他诸多IPC,我要学习的实际上是一个很古老的技术.
下面是一个简单的实现,我会一步步分析.
<lock.c>:
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#define LOCK_FILE ".filelock.file"
#define write_lock(fd, offset, whence, len) /
lock(fd, F_SETLK, F_WRLCK, offset, whence, len)
#define un_lock(fd, offset, whence, len) /
lock(fd, F_SETLK, F_UNLCK, offset, whence, len)
#define is_writelock(fd, offset, whence, len) /
lock_test(fd, F_WRLCK, offset, whence, len)
int lock(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));
}
pid_t 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) < 0)
return -1;
if (lock.l_type == F_UNLCK)
return 0;
else
return lock.l_pid;
}
/*
locktest和lock不是原子的,所以该检测不能保证lock一定成功,提供该函数的原因是,当lock失败时,需要locktest确定哪个进程已经占有了锁
*/
pid_t locktest(int fd)
{
return(is_writelock(fd, 0, SEEK_SET, 0));
}
/*
全文写锁
*/
int lock(int fd)
{
return(write_lock(fd, 0, SEEK_SET, 0));
}
int unlock(int fd)
{
int rtn;
rtn = un_lock(fd, 0, SEEK_SET, 0);
return rtn;
}
int lock_close(int fd)
{
return(close(fd));
}
测试程序A:
int main()
{
static int lock_fd;
int pid;
/*
程序A先运行,所以需要创建文件
*/
lock_fd = open(LOCK_FILE,O_WRONLY | O_CREAT | O_TRUNC, 0777);
whild(1) {
/*get lock*/
if(lock(lock_fd) < 0) {
pid = locktest(lock_fd);
if( pid > 0)
printf("lock owned by process %d/n", pid );
continue;
}
/*critical area*/
printf("A get lock/n");
sleep(2);
/*release lock*/
unlock(lock_fd);
lock_close(lock_fd);
return;
}
}
测试程序B:
int main()
{
static int lock_fd;
int pid;
lock_fd = open(LOCK_FILE,O_WRONLY, 0777);
whild(1) {
/*get lock*/
if(lock(lock_fd) < 0) {
pid = locktest(lock_fd);
if( pid > 0)
printf("lock owned by process %d/n", pid );
continue;
}
/*critical area */
printf("B get lock/n");
/*release lock*/
unlock(lock_fd);
lock_close(lock_fd);
return;
}
}
首先运行程序A,然后运行程序B.可以发现程序B一定是在A之后打印信息.不过这个例子更像是一个临界区.