在文件I/O编程之一的基础上,本文主要讨论函数fcntl的用法及其注意事项。
在Linux中,实现文件上锁的函数有lock和fcntl,其中lock对应的是建议性锁,而fcntl不仅可以施加建议性锁,也可以施加强制锁。同时,fcntl能对文件的某一记录上锁,也就是记录锁。
fcntl函数原型:int fcntl(int fd, int cmd, struct flock *lock)
函数传入值:
fd为文件描述符。
cmd为控制命令,主要有以下若干情况:
F_DUPFD:用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。执行成功则返回新复制的文件描述词。
F_GETFD:取得close-on-exec旗标。若此旗标的FD_CLOEXEC位为0,代表在调用exec()相关函数时文件将不会关闭。
F_SETFD:设置close-on-exec 旗标。该旗标以参数arg 的FD_CLOEXEC位决定。
F_GETFL: 取得文件描述词状态旗标,此旗标为open()的参数flags。
F_SETFL: 设置文件描述词状态旗标,参数arg为新旗标,但只允许O_APPEND、O_NONBLOCK和O_ASYNC位的改变,其他位的改变将不受影响。
F_GETLK: 取得文件锁定的状态。
F_SETLK: 设置文件锁定的状态。此时flcok 结构的l_type 值必须是F_RDLCK、F_WRLCK或F_UNLCK。如果无法建立锁定,则返回-1,错误代码为EACCES 或EAGAIN。
F_SETLKW: F_SETLK 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。
lock为flcok结构体,结构定义如下:
struct flcok
{
short int l_type; /* 锁定的状态*/
short int l_whence;/*决定l_start位置*/
off_t l_start; /*锁定区域的开头位置*/
off_t l_len; /*锁定区域的大小*/
pid_t l_pid; /*锁定动作的进程*/
};
其中l_type有三种状态:
F_RDLCK 建立一个供读取用的锁定
F_WRLCK 建立一个供写入用的锁定
F_UNLCK 删除之前建立的锁定
l_whence有三种状态:
SEEK_SET 以文件开头为锁定的起始位置
SEEK_CUR 以目前文件读写位置为锁定的起始位置
SEEK_END 以文件结尾为锁定的起始位置
fcntl使用实例如下:
void lock_set(int fd, int type) { struct flock lock; lock.l_whence = SEEK_SET; lock.l_start = 0; lock.l_len = 0; for (;;) { lock.l_type = type; //根据不同的type值给文件上锁或解锁 if((fcntl(fd, F_SETLK, &lock)) == 0) { if(lock.l_type == F_RDLCK) printf("read lock set by %d/n", getpid()); else if(lock.l_type == F_WRLCK) printf("write lock set by %d/n", getpid()); else if(lock.l_type == F_UNLCK) printf("release lock by %d/n", getpid()); return; } fcntl(fd, F_GETLK, &lock); //判断文件能否上锁 if(lock.l_type != F_UNLCK) //判断不能上锁原因 { if(lock.l_type == F_RDLCK) //已有读取锁 printf("read lock already set by %d/n", lock.l_pid); else if(lock.l_type == F_WRLCK) //已有写入锁 printf("write lock already set by %d/n", lock.l_pid); getchar(); } } }
在主程序里,我们只需要打开需要读写的文件(已介绍),把锁分为写入锁和读取锁。
写入锁操作:
lock_set(fd, F_WRLCK);
...
lock_set(fd, F_UNLCK);
编译后,在两个终端分别运行该执行文件,有心的朋友自己测试一下吧(其实是我偷懒不截图)
读取锁操作:
lock_set(fd, F_RDLCK);
...
lock_set(fd, F_UNLCK);
同样的操作流程,根据上述两个流程,我们可以自己证明得到:写入锁是互斥锁,读取锁是共享锁。
PS:一个小技巧:为了加锁整个文件,常将l_start设置为0,l_whence设置为SEEK_SET,l_len设置为0。