int flock(int fd, int operation); |
其中,参数 fd 表示文件描述符;参数 operation 指定要进行的锁操作,该参数的取值有如下几种:LOCK_SH, LOCK_EX, LOCK_UN 和 LOCK_MAND
- LOCK_SH:表示要创建一个共享锁,在任意时间内,一个文件的共享锁可以被多个进程拥有
- LOCK_EX:表示创建一个排他锁,在任意时间内,一个文件的排他锁只能被一个进程拥有
- LOCK_UN:表示删除该进程创建的锁
- LOCK_MAND: 它主要是用于共享模式强制锁,它可以与 LOCK_READ 或者 LOCK_WRITE 联合起来使用,从而表示是否允许并发的读操作或者并发的写操作(尽管在 flock() 的手册页中没有介绍 LOCK_MAND,但是阅读内核源代码就会发现,这在内核中已经实现了)
通常情况下,如果加锁请求不能被立即满足,那么系统调用 flock() 会阻塞当前进程。比如,进程想要请求一个排他锁,但此时,已经由其他进程获取了这个锁,那么该进程将会被阻塞。如果想要在没有获得这个排他锁的情况下不阻 塞该进程,可以将 LOCK_NB 和 LOCK_SH 或者 LOCK_EX 联合使用,那么系统就不会阻塞该进程。flock() 所加的锁会对整个文件起作用。
int fcntl (int fd, int cmd, struct flock *lock); |
其中,参数 fd 表示文件描述符;参数 cmd 指定要进行的锁操作,由于 fcntl() 函数功能比较多,这里先介绍与文件锁相关的三个取值 F_GETLK、F_SETLK 以及 F_SETLKW。这三个值均与 flock 结构有关。
struct flock
{
short int l_type; /* Type of lock: F_RDLCK, F_WRLCK, or F_UNLCK. */
short int l_whence; /* Where `l_start' is relative to (like `lseek'). */
__off_t l_start; /* Offset where the lock begins. */
__off_t l_len; /* Size of the locked area; zero means until EOF. */
__pid_t l_pid; /* Process holding the lock. */
};
在 flock 结构中,l_type 用来指明创建的是共享锁还是排他锁,其取值有三种:F_RDLCK(共享锁)、F_WRLCK(排他锁)和F_UNLCK(删除之前建立的 锁);l_pid 指明了该锁的拥有者;l_whence、l_start 和l_end 这些字段指明了进程需要对文件的哪个区域进行加锁,这个区域是一个连续的字节集合。因此,进程可以对同一个文件的不同部分加不同的锁。l_whence 必须是 SEEK_SET、SEEK_CUR 或 SEEK_END 这几个值中的一个,它们分别对应着文件头、当前位置和文件尾。l_whence 定义了相对于 l_start 的偏移量,l_start 是从文件开始计算的。
可以执行的操作包括:
- F_GETLK: 进程可以通过它来获取通过 fd 打开的那个文件的加锁信息。执行该操作时,lock 指向的结构中就保存了希望对文件加的锁(或者说要查询的锁)。如果确实存在这样一把锁,它阻止 lock 指向的 flock 结构所给出的锁描述符,则把现存的锁的信息写到 lock 指向的 flock 结构中,并将该锁拥有者的 PID 写入 l_pid 字段中,然后返回;否则,就将 lock 指向的 flock 结构中的 l_type 设置为 F_UNLCK,并保持 flock 结构中其他信息不变返回,而不会对该文件真正加锁。
- F_SETLK:进程用它来对文件的某个区域进行加锁(l_type的值为 F_RDLCK 或 F_WRLCK)或者删除锁(l_type 的值为F_UNLCK),如果有其他锁阻止该锁被建立,那么 fcntl() 就出错返回
- F_SETLKW:与 F_SETLK 类似,唯一不同的是,如果有其他锁阻止该锁被建立,则调用进程进入睡眠状态,等待该锁释放。一旦这个调用开始了等待,就只有在能够进行加锁或者收到信号时才会返回 。
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <string.h> #include <pthread.h> #include <sys/file.h> #ifndef T_DESC #define T_DESC(x, y) (y) #endif #if T_DESC("fcntl", 1) char *flock_name1 = "/tmp/flock1"; char *flock_name2 = "/tmp/flock2"; int flock_fd1; int flock_fd2; int read_lock(int fd) { struct flock lock; lock.l_start = 0; lock.l_len = 0x10; lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; int result = fcntl(fd, F_SETLK, &lock); if(result<0){ perror("lockR:"); } return result; } int read_lock_wait(int fd) { struct flock lock; lock.l_start = 0; lock.l_len = 0x10; lock.l_type = F_RDLCK; lock.l_whence = SEEK_SET; return fcntl(fd, F_SETLKW, &lock); } int write_lock(int fd) { struct flock lock; lock.l_start = 0; lock.l_len = 0x10; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; return fcntl(fd, F_SETLK, &lock); } int write_lock_wait(int fd) { struct flock lock; lock.l_start = 0; lock.l_len = 0x10; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; return fcntl(fd, F_SETLKW, &lock); } int file_unlock(int fd) { struct flock lock; lock.l_start = 0; lock.l_len = 0x10; lock.l_type = F_UNLCK; lock.l_whence = SEEK_SET; return fcntl(fd, F_SETLK, &lock); } #endif #if T_DESC("flock", 1) #endif #if T_DESC("tu", 1) void thread_11(void) { int i; for(i=0; i<100; i++) { write_lock(flock_fd1); printf("This is pthread_111..."); sleep(2); printf("111.\n"); file_unlock(flock_fd1); sleep(1); } pthread_exit(0); } void thread_12(void) { int i; for(i=0; i<100; i++) { write_lock(flock_fd2); printf("This is pthread_222..."); sleep(2); printf("222.\n"); file_unlock(flock_fd1); sleep(1); } pthread_exit(0); } void thread_21(void) { int i; for(i=0; i<100; i++) { flock(flock_fd1, LOCK_EX); printf("This is pthread_111..."); sleep(2); printf("111.\n"); flock(flock_fd1, LOCK_UN); sleep(1); } pthread_exit(0); } void thread_22(void) { int i; for(i=0; i<100; i++) { flock(flock_fd1, LOCK_EX); printf("This is pthread_222..."); sleep(2); printf("222.\n"); flock(flock_fd1, LOCK_UN); sleep(1); } pthread_exit(0); } int tu1_proc(int tu_id) { pthread_t id_1,id_2; int fd,ret; int param; flock_fd1 = open(flock_name1, O_RDWR| O_CREAT, 0600); if (flock_fd1 < 0) return -1; flock_fd2 = open(flock_name2, O_RDWR| O_CREAT, 0600); if (flock_fd2 < 0) return -1; if (tu_id == 1) { ret = pthread_create(&id_1, NULL, (void *)thread_11, NULL); } else { ret = pthread_create(&id_2, NULL, (void *)thread_12, NULL); } if(ret != 0) { printf("Create pthread error!\n"); return -1; } /*等待线程结束*/ if (tu_id == 1) { pthread_join(id_1, NULL); } else { pthread_join(id_2, NULL); } close(flock_fd1); close(flock_fd2); return 0; } #endif #if T_DESC("global", 1) void usage() { printf("\n Usage: <cmd> <tu> <p1> <...>"); printf("\n 1 -- create task 1"); printf("\n 2 -- create task 2"); printf("\n"); } int main(int argc, char **argv) { int ret; int tu_id; if(argc < 2) { usage(); return 0; } tu_id = atoi(argv[1]); if (tu_id < 1 || tu_id > 2) { usage(); return 0; } ret = tu1_proc(tu_id); return ret; } #endif #if T_DESC("readme", 1) /* 1, how to compile gcc -o flock.out flock.c -lpthread */ #endif