通过fcntl函数给文件上锁

    技术2025-04-07  32

    当多个进程共同使用,操作一个文件的时候,可以通过采用给文件上锁的的方法,来避免共享的资源产生竞争的状态。

     

    在linux中,实现文件上锁的函数有lock和fcntl。

     

    fcntl可以对文件施加建议性锁,强制锁和记录锁。

     

     

    1.建议锁又称协同锁。对于这种类型的锁,内核只是提供加减锁以及检测是否加锁的操作,但是不提供锁的控制与协调工作。也就是说,如果应用程序对某个文件进行操作时,没有检测是否加锁或者无视加锁而直接向文件写入数据,内核是不会加以阻拦控制的。因此,建议锁,不能阻止进程对文件的操作,而只能依赖于大家自觉的去检测是否加锁然后约束自己的行为。

    2.强制锁,是OS内核的文件锁。每个对文件操作时,例如执行openreadwrite等操作时,OS内部检测该文件是否被加了强制锁,如果加锁导致这些文件操作失败。也就是内核强制应用程序来遵守游戏规则。

    3.记录锁,即对文件的一部分进行加锁操作。记录锁又可分为读取锁和写入锁,其中读取锁又称为共享锁,它能够使多个进程都能在文件的同一部分建立读取锁;而写入锁又称排斥锁,在任何时刻只能有一个进程在文件的某个部分上建上写入锁。当然,在文件的同一部分不能同时建立读取锁和写入锁。

     

    fcntl函数语法:

     #include <fcntl.h>

    int fcntl(int fd,int cmd,struct flock * lock);  

    fcntl()用来操作文件描述符的一些特性。参数fd代表欲设置的文件描述词,参数cmd代表欲操作的指令。  

    有以下几种情况:  

    F_DUPFD用来查找大于或等于参数arg的最小且仍未使用的文件描述词,并且复制参数fd的文件描述词。执行成功则返回新复制的文件描述词。请参考dup2()。

    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 作用相同,但是无法建立锁定时,此调用会一直等到锁定动作成功为止。若在等待锁定的过程中被信号中断时,会立即返回-1,错误代码为EINTR。

     

    参数lock指针为flock 结构指针,定义如下  

    struct flcok  {  

    short int l_type; 

    short int l_whence;  

    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 以文件结尾为锁定的起始位置。

     

    返回值 成功则返回0,若有错误则返回-1,错误原因存于errno.

     

     

     

     

    程序:

     

    #include <stdio.h>

    #include <unistd.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <stdlib.h>

    #include <sys/file.h>

     

    /*

    函数功能:给某个文件上锁

    函数形参:文件描述符fd, 锁类型type

    */

    void Lock_Set(int fd, int type)

    {

    struct flock lock; //锁结构体lock

    lock.l_whence = SEEK_SET; //给lock赋值

    lock.l_start = 0;

    lock.l_len = 0;

     

    while(1)

    {

    lock.l_type = type;

    if( (fcntl(fd, F_SETLK, &lock)) == 0) //根据type类型给文件上锁或解锁

    {

    if(lock.l_type == F_RDLCK) //判断上锁类型

    printf("read lock set by %d", getpid());

    else if(lock.l_type == F_WRLCK)

    printf("write lock set by %d", getpid());

    else if(lock.l_type = F_UNLCK)

    printf("release lock by %d", getpid());

     

    return; //上锁成功后返回

     

    fcntl(fd, F_GETLK, &lock); //获得文件锁定状态,赋值给lock.l_type

     

    if(lock.l_type != F_UNLCK)  //若文件处于上锁状态

    {

    if(lock.l_type == F_RDLCK) //判断文件锁类型

    printf("read lock already set by %d", lock.l_pid);

    else if(lock.l_type == F_WRLCK) 

    printf("write lock already set by %d", lock.l_pid);

     

    getchar(); //等待输入

    }

    }

     

    int main()

    {

    int fd;

     

    struct flock lock; //锁结构体lock

    lock.l_whence = SEEK_SET; //给lock赋值

    lock.l_start = 0;

    lock.l_len = 0;

     

    fd = open("./dat", O_CREAT|O_RDWR, 0777); //打开文件dat

    if(fd == -1)

    {

    perror("open failed/n");

    exit(1);

    }

     

     

     

    Lock_Set(fd, F_WRLCK); //给文件上写入锁

    getchar();

     

    Lock_Set(fd, F_UNLCK); //给文件解锁

    getchar();

     

    close(fd);

     

    return 0;

    }

     

    在linux中用两个终端运行观察结果如下:

    终端1:

     

    [root@localhost IO]# ./fcntl_write

    write lock set by 4621

    release lock by 4621

    终端2: [root@localhost IO]# ./fcntl_write write lock already set by 4621 write lock already set by 4621 write lock set by 4645 release lock by 4645 可见,写入锁为排斥锁,任何时刻只能有一个。 若把main函数中

     

    Lock_Set(fd, F_WRLCK); //给文件上写入锁 改为 Lock_Set(fd, F_RDLCK); //给文件上读取锁 其他不变。 运行结果如下: 终端1: [root@localhost IO]# ./fcntl_read read lock set by 4750 release lock by 4750 终端2: [root@localhost IO]# ./fcntl_read read lock set by 4751 release lock by 4751 可见,进程4750和进程4751同时对同一文件加上了读取锁。

     

    最新回复(0)