转载:http://blog.csdn.net/pbymw8iwm/article/details/7974789
查找系统头文件的路径 locate types.h |grep "sys"
在man fcntl 的手册中可以看到结构体 struct flock
fcntl函数可以对已打开的文件描述符进行各种控制操作以改变文件的各种属性。
可以用fcntl 函数改变一个已打开的文件的属性,可以重新设置读、写、追加、非阻塞等标志(这些标志称为File StatusFlag),而不必重新open 文件。
#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);
int fd, //文件描述符
int cmd , //不同的命令
struct flock *lock //设置记录锁的具体状态
cmd取值:
1、F_DUPFD 复制文件描述符,此时fcntl的功能跟dup一样。
2、F_GETFD 获得fd的close-on-exec标志
3、F_SETFD 设置close-on-exec标志为第三个参数arg的最后一位
4、F_GETFL 获得文件打开的方式。
5、F_SETFL 设置文件打开的方式为第三个参数arg指定的方式。(O_APPEND O_ASYNC O_NONBLOCK)
6、F_SETLK
7、F_SETLKW
8、F_GETLK
9、F_GETOWN 取得当前正在接收SIGIO或者SIGURG信号的进程id或进程组id,进程组id返回的是负值(arg被忽略)
10、F_SETOWN 设置将接收SIGIO和SIGURG信号的进程id或进程组id,进程组id通过提供负值的arg来说明(arg绝对值的一个进程组ID),否则arg将被认为是进程id
11、F_GETSIG 此时,可以在输入输出时,获得发送的信号
12、F_SETSIG 此时,设置在输入输出时发送的信号
6、7、8的情况:
6、F_SETLK 设置锁, 要设置的锁的种类 要跟 打开文件的方式保持一致。(读还是写)
7、F_SETLKW 希望设置的锁因为其他锁存在而被阻止时,改命令就会等待相冲突的锁释放。
8、F_GETLK 测试返回当前锁的状态
当有多个进程同时对某一个文件进行操作时,就有可能发生数据的不同步,从而引发错误。该文件的最后状态取决于写该文件的最后一个程序。
对于有些应用程序。例如数据库,进程需要确保它在单独写一个文件。为了向进程提供这种功能,Linux系统提供了记录锁机制。
Linux的文件记录锁能提供非常详尽的控制。它能对文件的某一区域进行文件记录锁的控制。
当fcntl用于管理文件记录锁的操作时,第三个参数指向struct flock *lock的结构。
这里的lock结构体如下:
struct flock {
short l_type; /*F_RDLCK(读取锁),F_WRLCK(写入锁),F_UNLCK(解锁)*/
off_t l_start; /*相对偏移量(字节)*/
short l_whence; /*SEEK_SET ,SEEK_CUR ,SEEK_END */
off_t l_len; /*加锁区域长度*/
pid_t l_pid; /*锁的属主进程ID*/
}
--------------------------------------------
文件锁包括了 建议性锁 和 强制性锁。
建议性锁要求每个上锁文件的进程都要检查是否有锁存,并且尊重已有的锁。在一般情况下,内核和系统都不使用建议性锁。
强制性锁是由内 核执行的锁,当一个文件被上锁进行写入操作的时候,内核将阻止其他任何文件对其进行读写操作。
采用强制性锁对性能的影响很大,每次读写操作都必须检查是否有锁存在。
在Linux中实现上锁的函数有flock()和fcntl()。
flock()用于对文件施加建议性锁
fcntl()用于对文件施加建议性锁和强制性锁都行。同时还可以对文件某一条纪录进行上锁,也就是记录锁。
记录锁分为 读取锁(共享锁,它能够使多个进程都能在文件的同一部分建立读取锁) 和
写入锁(排斥锁,在任何时刻只能有一个进程在文件的某部分建立写入锁。)。
提示:如果加锁整个文件通常的方法是将l_start设置为0,l_whence设置为SEEK_SET, l_len设置为0。也就是说不管向文件中
添加多少数据,都处于锁的范围内。
----------------------------------------------------------------------------------------
多个进程在一个给定的字节上可以有一把共享的锁,但是在一个给定字节上的写锁则只能由一个进程单独使用。
多个进程在给定字节上如果已经有一把或多把读锁,则不能在该给定字节上再加写锁。
如果在给定字节上已经有一把独占性的写锁,则不能再对他加任何读锁。
一个进程只能设置某一个文件区域上的一种锁,如果已经存在文件记录锁了,则在设置新的锁在该区域的话,旧的锁
将会被新的锁取代。
锁的不兼容性是针对多个进程之间的。
测试代码:
(Linux的文件记录锁默认是建议性锁,而不是强制性锁)
进程1 进程2 同时在执行。
进程1 设置了读锁, 进程2 可以设置读锁。但是进程1 进程2 不能再设置写锁
进程1独立执行。
进程1 设置了读锁,后面可以设置写锁。单个进程在同一字节上只能设置一种锁,新的锁会取代旧的锁。
进程1 进程2 同时在执行
进程1 设置了写锁 进程2 不能设置读锁 也不能设置写锁
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> //测试锁,只有当测试发现参数lock指定的锁能被设置时,返回0 int lock_test(int fd, struct flock *lock) { if (0 == fcntl(fd, F_GETLK, lock)) { if (F_UNLCK == lock->l_type)//测试发现能按参数lock要求设置锁 { printf("lock can be set in fd\n"); return 0; } else//有不兼容的锁存在,打印出设置该锁的进程ID { if (F_RDLCK == lock->l_type) { printf("can not set lock, read lock has been set by :%d\n", lock->l_pid); } else if (F_WRLCK == lock->l_type) { printf("can not set lock, write lock has been set by :%d\n", lock->l_pid); } return -2; } } else { printf("get incompatible locks fail\n"); return -1; } } //锁的设置或释放函数 int lock_set(int fd, struct flock* lock) { if (0 == fcntl(fd, F_SETLK, lock)) { if (F_RDLCK == lock->l_type) { //测试是0???使用 getpid( ) ??? //printf("set read lock, pid:%d\n", lock->l_pid); } else if (F_WRLCK == lock->l_type) { //测试是0??? //printf("set write lock, pid:%d\n", lock->l_pid); } else if (F_UNLCK == lock->l_type) { //printf("release lock, pid:%d\n", lock->l_pid); } } else { printf("lock operation fail error:%s\n", strerror(errno)); return -1; } return 0; } int main(int argc, char* argv[]) { printf("my pid:%d\n", getpid()); int fd; int ret; struct flock lock; char r_buf[20]; char w_buf[] = "test lock\n"; char w_buf2[] = "test write lock"; //打开文件 fd = open("example", O_RDWR|O_APPEND|O_CREAT, 700); if (-1 == fd) { printf("open file error:%s\n", strerror(errno)); } write(fd, w_buf, strlen(w_buf)); //初始化锁 bzero(&lock, sizeof(struct flock)); lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0;
//!!??
lock.l_pid = getpid();
lock.l_type = F_RDLCK; if (0 == lock_test(fd, &lock))//测试可以设置文件锁 { lock.l_type = F_RDLCK; lock_set(fd, &lock); } //read data from file lseek(fd, 0, SEEK_SET); if ( (ret = read(fd, r_buf, 10)) < 0) { printf("read error\n"); } r_buf[ret] = '\0'; printf("read data from the file:%s\n", r_buf); //wait input getchar(); lock.l_type = F_WRLCK; if (0 == lock_test(fd, &lock))//测试可以设置写锁 { //设置写锁 lock.l_type = F_WRLCK; lock_set(fd, &lock); } //write data write(fd, w_buf2, strlen(w_buf2)); //release lock lock.l_type = F_UNLCK; lock_set(fd, &lock); close( fd ); return(0); }