IPC之记录锁详解
来源:互联网 发布:无地自容 知乎 编辑:程序博客网 时间:2024/05/21 20:15
基本概念:
当两个人同时编辑一个文件时,其后果将如何?在大多数unix系统中,该文件的最后状态取决于写该文件的最后一个进程,
记录锁(record locking)的功能是:当一个进程正在读或者修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一区域。
POSIX.1标准是使用fcntl方法控制记录锁,函数原型如下
FCNTL(2) Linux Programmer's Manual FCNTL(2)NAME fcntl - manipulate file descriptorSYNOPSIS #include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd, ... /* arg */ );
对于记录锁,cmd是F_GETLK, F_SETLK 或 F_SETLKW。第三个参数是一个指向flock结构的指针。
struct flock { short l_type; /* Type of lock: F_RDLCK, F_WRLCK, F_UNLCK */ short l_whence; /* How to interpret l_start: SEEK_SET, SEEK_CUR, SEEK_END */ off_t l_start; /* Starting offset for lock */ off_t l_len; /* Number of bytes to lock */ pid_t l_pid; /* PID of process blocking our lock (F_GETLK only) */};
对flock结构说明如下:
1.所希望的锁类型:F_RDLCK(共享读锁),F_WRLCK(独占写锁),F_UNLCK(解锁一个区域)
2.要加锁或解锁区域的起始字节偏移量(l_start和l_whence)
3.要加锁或解锁区域的字节长度(l_len)
4.进程的ID(l_pid)持有的锁能阻塞当前进程(仅由F_GETLK返回)
关于加锁和解锁区域的说明:
1.指定区域起始偏移量的两个元素与lseek函数中的最后两个参数类似。l_whence可选用值是SEEK_SET, SEEK_CUR, SEEK_END
2.锁可以在当前文件尾端处开始或者越过文件尾端处开始,但是不能在文件起始位置之前开始
3.如若l_len为0,则表示锁的范围可以扩展到最大可能偏移量,这意味着不管向文件中追加写了多少数据,它们都可以处于锁的范围内
4.为了对整个文件加锁,可以设置l_start为0,l_whence为SEEK_SET,l_len为0
锁的互斥性:
对于单进程:
如果一个进程对一个文件区间已经有了一把锁,后来该进程又企图在同一个文件区间再加一把锁,那么新锁将替换已有锁。
对于多进程,如图示:
加读锁时。该描述符必须是读打开。加写锁时,该描述符必须是写打开。
说明一下fcntl函数的3种命令:
F_GETLK:判断所描述的锁是否会被另外一把锁排斥,如果不存在这种情况,l_type被设置为F_UNLCK
F_SETLKW:这个命令是F_SETLK的阻塞版本,如果需要设置的锁被排斥,那么进程会休眠等待锁成功设置。
应该了解,用F_GETLK测试能否建立一把锁,然后用F_SETLK或F_SETLKW企图建立那把锁,这两者不是一个原子操作。因此不能保证在这两次fcntl调用之间不会有另一个进程插入并建立一把相同的锁。
程序1,设置读锁,,gcc flock_read.c -o readflock
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>void Perror(const char *s){ perror(s); exit(EXIT_FAILURE);}/* 检查锁,如果返回0,可以加锁, 否则表示存在写锁或者读锁 */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) Perror("fcntl error"); /* 这里F_UNLCK并不表示文件不存在锁,表示允许加读or写锁 */ if (lock.l_type == F_UNLCK) return 0; return lock.l_pid;}/* 设置锁 or 解锁 */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 main(){ // 创建文件 int fd = open("./test.temp", O_RDONLY | O_CREAT | O_EXCL, 0777); if (fd == -1) { printf("file exeit\n"); fd = open("./test.temp", O_RDONLY, 0777); } else { printf("create file success\n"); } pid_t pid = getpid(); printf("the proc pid:%d\n", pid); // check read pid_t lockpid = lock_test(fd, F_RDLCK, 0, SEEK_SET, 0); if (lockpid == 0) printf("check read lockable, ok\n"); else printf("check read lockable, can't. have write lock, owner pid:%d\n", lockpid); // set read lock if (lock_reg(fd, F_SETLK, F_RDLCK, 0, SEEK_SET, 0) < 0) printf("set read lock failed\n"); else printf("set read lock success\n"); sleep(60); return 0;}
程序2,设置写锁,,gcc flock_write.c -o writeflock
#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <errno.h>void Perror(const char *s){ perror(s); exit(EXIT_FAILURE);}/* 检查锁,如果返回0,可以加锁, 否则表示存在写锁或者读锁 */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) Perror("fcntl error"); /* 这里F_UNLCK并不表示文件不存在锁,表示允许加读or写锁 */ if (lock.l_type == F_UNLCK) return 0; return lock.l_pid;}/* 设置锁 or 解锁 */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 main(){ // 创建文件 int fd = open("./test.temp", O_WRONLY | O_CREAT | O_EXCL, 0777); if (fd == -1) { printf("file exeit\n"); fd = open("./test.temp", O_WRONLY, 0777); } else { printf("create file success\n"); } pid_t pid = getpid(); printf("the proc pid:%d\n", pid); // check write pid_t lockpid = lock_test(fd, F_WRLCK, 0, SEEK_SET, 0); if (lockpid == 0) printf("check write lockable, ok\n"); else printf("check write lockable, can't. have read or write lock, owner pid:%d\n", lockpid); // set write lock if (lock_reg(fd, F_SETLK, F_WRLCK, 0, SEEK_SET, 0) < 0) printf("set write lock failed\n"); else printf("set write lock success\n"); sleep(60); return 0;}
测试1,进程1设置读锁,进程2再设置读锁:
测试2,进程1设置读锁,进程2再设置写锁:
测试3,进程1设置写锁,进程2再设置读锁:
测试4,进程1设置写锁,进程2再设置写锁:
锁的隐含继承和释放:
1.进程终止时,它所建立的锁全部释放。
2.无论一个描述符何时关闭,该进程通过这一描述符引用建立的任何一把锁都会释放。
3.由fork产生的子进程不继承父进程所设置的锁。
4.在执行exec后,新程序可以继承原执行程序的锁。因为执行exec前后还是一个进程。我们 只是改变进程执行的程序,并没有创建新的进程。
建议性锁和强制性锁:
建议性锁:建议锁又称协同锁。对于这种类型的锁 ,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为
强制性锁:内核会检查每一个open、read、write,验证调用进程是否违背了正在访问的文件上的某一把锁。
经过测试:unubtu 14是建议性锁
参考:《unix环境高级编程》·第三版
End;
- IPC之记录锁详解
- IPC之管道详解
- IPC之信号量详解
- IPC之管道详解
- 【IPC】 记录锁
- IPC之消息队列详解
- IPC之Posix信号量详解
- IPC之共享内存详解
- IPC之Posix内存映射文件详解
- IPC之Posix共享内存详解
- 安卓IPC机制之Binder详解
- IPC之FIFO(有名管道)详解
- IPC之Posix消息队列详解
- IPC详解
- IPC笔记之读写锁
- linux IPC---记录上锁
- Android Binder IPC详解-Android学习之旅(96)
- Android IPC通信机制之:Binder 机制详解-2016/07
- <Java小工具>
- 常见的链接关系
- 剑指offer_03 二维数组中查找数字
- 设置textbox样式与easyui扁平化风格相符
- Java设计模式之—静态代理和动态代理
- IPC之记录锁详解
- SASS 基本语法与使用
- windows远程连接访问ubantu上Mysql
- bootstrap treeview实现target功能,iframe中打开页面
- HDU 1050 Moving Tables
- C语言中有没有boo类型
- scala进阶15-依赖注入
- 关于乐观锁和悲观锁
- 38.超文本标记语言HTML详解(上)