概述
fcntl函数文件锁有几个比较容易忽视的地方:
1.文件锁是真的进程之间而言的,调用进程绝对不会被自己创建的锁锁住,因为F_SETLK和F_SETLKW命令总是替换调用进程现有的锁(若已存在),所以调用进程决不会阻塞在自己持有的锁上,于是,F_GETLK命令决不会报告调用进程自己持有的锁。
2.struct flock结构指针中的l_type成员3个short值分别是:
常量 | 值 |
F_RDLCK | 0 |
F_WRLCK | 1 |
F_UNLCK | 2 |
3.如果两个锁之间的文件区域有交集,就会阻塞,无论交集的大小。
如图中section 1持有1到10字节区间的读锁,倘若此时需要创建section 2写锁,那么需要等待section 1区域释放才行。
示例代码:
进程A
#include <error.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> extern int *__errno_location(void); #define errno (*__errno_location()) void report_err(int re); struct flock section_1 = { F_RDLCK, SEEK_SET, 0, 10 }; struct flock section_1_1 = { F_RDLCK, SEEK_SET, 0, 10 }; int main(void) { int re; int file = open("/documents/test/test_2", O_RDWR); printf("open file fd: %d ", file); //struct flock section_1_1 = section_1; re = fcntl(file, F_GETLK, §ion_1); printf("section_1 l_type: %d ", (§ion_1)->l_type); re = fcntl(file, F_SETLK, §ion_1_1); report_err(re); printf("section_1_1 l_type: %d ", (§ion_1_1)->l_type); sleep(10); return 0; } void report_err(int re) { if(re == -1){ perror("file error"); } }
进程B
#include <error.h> #include <unistd.h> #include <stdio.h> #include <fcntl.h> extern int *__errno_location(void); #define errno (*__errno_location()) void report_err(int re); struct flock section_2 = { F_WRLCK, SEEK_SET, 5, 10 }; struct flock section_2_1 = { F_WRLCK, SEEK_SET, 5, 10 }; int main(void) { int re; int file = open("/documents/test/test_2", O_RDWR); printf("open file fd: %d ", file); re = fcntl(file, F_GETLK, §ion_2); report_err(re); printf("section_2 l_type: %d ", (§ion_2)->l_type); re = fcntl(file, F_SETLKW, §ion_2_1); report_err(re); printf("section_2_1 l_type: %d ", (§ion_2_1)->l_type); return 0; } void report_err(int re) { if(re == -1){ perror("file error"); } }
进程A在创建section 1后阻塞10秒,期间启动进程B创建section 2,此时进程B阻塞等待进程A释放section 1。
4.锁与进程和文件关联。这里有两重含义:第一重很明显,当一个进程终止时,它所有建立的锁全部释放;第二重则不太明显,无论一个描述符何时关闭,该进程通过这一描述符引用的文件上的任何一把锁都会释放(这些锁都是该进程设置的),详情参见《Unix高级环境编程》396页。