Linux下文件锁操作主要是通过以下两个API接口来完成的。
#include <sys/file.h> int flock(int fd, int operation);
或者
#include <unistd.h> #include <fcntl.h> int fcntl(int fd, int cmd); int fcntl(int fd, int cmd, long arg); int fcntl(int fd, int cmd, struct flock *lock);注:前者主要用于对整个文件的锁操作,后者可以对文件的部分内容进行锁操作。
Linux应用程序编程时应该注意以下几点:
1)文件锁是针对整个文件还是文件的部分内容。
2)进程级文件句柄关闭将会导致文件锁释放。
3)文件内容修改需要注意到glibc的缓冲机制,及时同步数据。
4)flock锁inode,fcntl锁文件描述符,因此flock不支持NFS,兼容性需要注意。
这里将给出进程级和线程级文件锁demo code供参考。
进程级文件锁demo:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #define TEST_FOPEN int main(int argc, char *argv[]) { /* l_type l_whence l_start l_len l_pid */ struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 }; int fd; #ifdef TEST_FOPEN FILE *file = NULL; #endif /* TEST_FOPEN */ fl.l_pid = getpid(); if (argc > 1) fl.l_type = F_RDLCK; while(1) { #ifdef TEST_FOPEN if ((file = fopen("lockdemo.c", "rw+")) == NULL) { perror("fopen"); exit(1); } #else if ((fd = open("lockdemo.c", O_RDWR)) == -1) { perror("open"); exit(1); } #endif /* TEST_FOPEN */ printf("Press <RETURN> to try to get lock: "); getchar(); printf("Trying to get lock..."); #ifdef TEST_FOPEN fd = fileno(file); #endif /* TEST_FOPEN */ fl.l_type = F_WRLCK; /* set to lock same region */ if (fcntl(fd, F_SETLKW, &fl) == -1) { perror("fcntl"); exit(1); } printf("got lock\n"); printf("Press <RETURN> to release lock: "); getchar(); fl.l_type = F_UNLCK; /* set to unlock same region */ if (fcntl(fd, F_SETLK, &fl) == -1) { perror("fcntl"); exit(1); } printf("Unlocked.\n"); #ifdef TEST_FOPEN fclose(file); #else close(fd); #endif /* TEST_FOPEN */ } return 0; }运行结果:
线程级文件锁demo:
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <pthread.h> #define TEST_FOPEN #define TEST_FLOCK void* thread_flock(void* ptr) { /* l_type l_whence l_start l_len l_pid */ struct flock fl = {F_WRLCK, SEEK_SET, 0, 0, 0 }; int fd; int ith = *((int *)ptr); #ifdef TEST_FOPEN FILE *file = NULL; #endif /* TEST_FOPEN */ fl.l_pid = getpid(); while(1) { #ifdef TEST_FOPEN if ((file = fopen("lockdemo.c", "rw+")) == NULL) { perror("fopen"); exit(1); } #else if ((fd = open("lockdemo.c", O_RDWR)) == -1) { perror("open"); exit(1); } #endif /* TEST_FOPEN */ #ifdef TEST_FOPEN fd = fileno(file); #endif /* TEST_FOPEN */ #ifdef TEST_FLOCK flock(fd, LOCK_EX); #else fl.l_type = F_WRLCK; /* set to lock same region */ if (fcntl(fd, F_SETLKW, &fl) == -1) { perror("fcntl"); exit(1); } #endif /* TEST_FLOCK */ printf("[%d] %d --> got lock\n", ith, fd); sleep(ith); #ifdef TEST_FLOCK flock(fd, LOCK_UN); #else fl.l_type = F_UNLCK; /* set to unlock same region */ if (fcntl(fd, F_SETLKW, &fl) == -1) { perror("fcntl"); exit(1); } #endif /* TEST_FLOCK */ printf("[%d] %d--> Unlocked.\n", ith, fd); #ifdef TEST_FOPEN fclose(file); #else close(fd); #endif /* TEST_FOPEN */ sleep(2); } } int main(int argc, char *argv[]) { int time1, time2; pthread_t pid1,pid2; time1 = 1; pthread_create(&pid1, NULL, &thread_flock, &time1); time2 = 3; pthread_create(&pid2, NULL, &thread_flock, &time2); while(1) sleep(10); return 0; }运行结果:
参考资料:
【1】Linux man pages
【2】fcntl() for thread or process synchronization?
【3】How to lock files using fcntl() and to work between threads of the same process
【4】fcntl+pthread_rwlock制作的支持多进程多线程混合的互斥锁
【5】flock不支持NFS
【6】Re: Linux, fcntl vs flock and pthreads