IPC之记录锁详解
2016-08-17 01:10
176 查看
基本概念:
当两个人同时编辑一个文件时,其后果将如何?在大多数unix系统中,该文件的最后状态取决于写该文件的最后一个进程,
记录锁(record locking)的功能是:当一个进程正在读或者修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一区域。
POSIX.1标准是使用fcntl方法控制记录锁,函数原型如下
对于记录锁,cmd是F_GETLK, F_SETLK 或 F_SETLKW。第三个参数是一个指向flock结构的指针。
对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
锁的互斥性:
对于单进程:
如果一个进程对一个文件区间已经有了一把锁,后来该进程又企图在同一个文件区间再加一把锁,那么新锁将替换已有锁。
对于多进程,如图示:
![](https://img-blog.csdn.net/20160817003158978)
加读锁时。该描述符必须是读打开。加写锁时,该描述符必须是写打开。
说明一下fcntl函数的3种命令:
F_GETLK:判断所描述的锁是否会被另外一把锁排斥,如果不存在这种情况,l_type被设置为F_UNLCK
F_SETLK:设置锁,如果锁被排斥,那么fcntl会立即出错返回,此时errno设置为EACCES或EAGIN
F_SETLKW:这个命令是F_SETLK的阻塞版本,如果需要设置的锁被排斥,那么进程会休眠等待锁成功设置。
应该了解,用F_GETLK测试能否建立一把锁,然后用F_SETLK或F_SETLKW企图建立那把锁,这两者不是一个原子操作。因此不能保证在这两次fcntl调用之间不会有另一个进程插入并建立一把相同的锁。
程序1,设置读锁,,gcc flock_read.c -o readflock
程序2,设置写锁,,gcc flock_write.c -o writeflock
测试1,进程1设置读锁,进程2再设置读锁:
![](https://img-blog.csdn.net/20160817004630199)
测试2,进程1设置读锁,进程2再设置写锁:
![](https://img-blog.csdn.net/20160817004634733)
测试3,进程1设置写锁,进程2再设置读锁:
![](https://img-blog.csdn.net/20160817004638233)
测试4,进程1设置写锁,进程2再设置写锁:
![](https://img-blog.csdn.net/20160817004642075)
锁的隐含继承和释放:
1.进程终止时,它所建立的锁全部释放。
2.无论一个描述符何时关闭,该进程通过这一描述符引用建立的任何一把锁都会释放。
3.由fork产生的子进程不继承父进程所设置的锁。
4.在执行exec后,新程序可以继承原执行程序的锁。因为执行exec前后还是一个进程。我们 只是改变进程执行的程序,并没有创建新的进程。
建议性锁和强制性锁:
建议性锁:建议锁又称协同锁。对于这种类型的锁 ,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为
强制性锁:内核会检查每一个open、read、write,验证调用进程是否违背了正在访问的文件上的某一把锁。
经过测试:unubtu 14是建议性锁
参考:《unix环境高级编程》·第三版
End;
当两个人同时编辑一个文件时,其后果将如何?在大多数unix系统中,该文件的最后状态取决于写该文件的最后一个进程,
记录锁(record locking)的功能是:当一个进程正在读或者修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一区域。
POSIX.1标准是使用fcntl方法控制记录锁,函数原型如下
FCNTL(2) Linux Programmer's Manual FCNTL(2) NAME fcntl - manipulate file descriptor SYNOPSIS #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_SETLK:设置锁,如果锁被排斥,那么fcntl会立即出错返回,此时errno设置为EACCES或EAGIN
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;
相关文章推荐
- 详解DNS的常用记录(下):DNS系列之三
- IPC入侵命令详解
- SQL SERVER 2005中全新的OUTPUT子句添加数据记录详解(实战篇)
- DNS(3)- 详解DNS的资源记录
- 详解DNS的常用记录(下):DNS系列之三
- Android IPC机制 详解
- Android IPC 机制详解
- 详解DNS的常用记录(二)
- MTU & MSS 详解记录
- 详解DNS的常用记录(上):DNS系列之二
- Android IPC机制详解
- DNS 学习笔记之三- 详解DNS的资源记录
- SQL SERVER 2005中全新的OUTPUT子句添加数据记录详解(理论篇)
- IIS4.0事件记录详解
- mx邮件交换记录详解
- Android IPC机制详解
- bind中DNS的记录详解
- 详解DNS的常用记录(上):DNS系列之二
- 让你的Nagios记录系统监控日志(附vmstat详解)