fcntl()

fcntl系统调用可以用来对已打开的文件描述符进行各种控制操作以改变已打开文件的的各种属性

函数原型:
 
#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);

fcntl函数功能依据cmd的值的不同而不同。参数对应功能如下:

(1)F_DUPFD

与dup函数功能一样,复制由fd指向的文件描述符,调用成功后返回新的文件描述符,与旧的文件描述符共同指向同一个文件。

(2)F_GETFD

读取文件描述符close-on-exec标志

(3)F_SETFD

将文件描述符close-on-exec标志设置为第三个参数arg的最后一位

(4)F_GETFL

获取文件打开方式的标志,标志值含义与open调用一致

(5)F_SETF

设置文件打开方式为arg指定方式

(6)F_SETLK F_GETLK,F_SETLK,F_SETLKW当使用这些命令时,fcntl的第三个参数必须是一个指向flock结构的指针

此时fcntl函数用来设置或释放锁。当short_l_type为F_RDLCK为读锁,F_WDLCK为写锁,F_UNLCK为解锁。

如果锁被其他进程占用,则返回-1;

这种情况设的锁遇到锁被其他进程占用时,会立刻停止进程。

(7)F_SETLKW

此时也是给文件上锁,不同于F_SETLK的是,该上锁是阻塞方式。当希望设置的锁因为其他锁而被阻止设置时,该命令会等待相冲突的锁被释放。

(8)F_GETLK

第3个参数lock指向一个希望设置的锁的属性结构,如果锁能被设置,该命令并不真的设置锁,而是只修改lock的l_type为F_UNLCK,然后返回该结构体。如果存在一个或多个锁与希望设置的锁相互冲突,则fcntl返回其中的一个锁的flock结构。

文件记录锁是fcntl函数的主要功能。

记录锁:实现只锁文件的某个部分,并且可以灵活的选择是阻塞方式还是立刻返回方式

当fcntl用于管理文件记录锁的操作时,第三个参数指向一个struct flock *lock的结构体

struct flock
{
    short l_type;    /*锁的类型 F_RDLCK:为了获得一把共享锁,文件必须以“读”或“读/写”方式打开。F_WRLCK:为了获得一把写(独占)锁,文件也必须以“读”或“读/写”方式打开。F_UNLCK:从它的名字就可以知道,它用于把一个锁定的区域解锁。*/
    short l_whence;  /*偏移量的起始位置:SEEK_SET,SEEK_CUR,SEEK_END*/
    off_t l_start;     /*加锁的起始偏移*/
    off_t l_len;    /*上锁字节*/
    pid_tl_pid;   /*锁的属主进程ID */
}; 
 

short l_type用来指定设置共享锁(F_RDLCK,读锁)还是互斥锁(F_WDLCK,写锁).

当short l_type的值为F_UNLCK时,传入函数中将解锁。

每个进程可以在该字节区域上设置不同的读锁。

但给定的字节上只能设置一把写锁,并且写锁存在就不能再设其他任何锁,且该写锁只能被一个进程单独使用。

这是多个进程的情况。

单个进程时,文件的一个区域上只能有一把锁,若该区域已经存在一个锁,再在该区域设置锁时,新锁会覆盖掉旧的锁,无论是写锁还时读锁。

l_whence,l_start,l_len三个变量来确定给文件上锁的区域。

l_whence确定文件内部的位置指针从哪开始,l_star确定从l_whence开始的位置的偏移量,两个变量一起确定了文件内的位置指针先所指的位置,即开始上锁的位置,然后l_len的字节数就确定了上锁的区域。

特殊的,当l_len的值为0时,则表示锁的区域从起点开始直至最大的可能位置,就是从l_whence和l_start两个变量确定的开始位置开始上锁,将开始以后的所有区域都上锁。

为了锁整个文件,我们会把l_whence,l_start,l_len都设为0。

pid_t l_pid;用来记录参持有锁的进程

例:

源文件名为filelock2.c,用于创建数据文件,并将文件区域加锁,代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

int main()
{
    const char *test_file = "test_lock.txt";
    int file_desc = -1;
    int byte_count = 0;
    char *byte_to_write = "A";
    struct flock region_1;
    struct flock region_2;
    int res = 0;

    //打开一个文件描述符    
    file_desc = open(test_file, O_RDWR|O_CREAT, 0666);
    if(!file_desc)
    {
        fprintf(stderr, "Unable to open %s for read/write\n", test_file);
        exit(EXIT_FAILURE);
    }
    //给文件添加100个‘A’字符的数据
    for(byte_count = 0; byte_count < 100; ++byte_count)
    {
        write(file_desc, byte_to_write, 1);
    }
    //在文件的第10~29字节设置读锁(共享锁)
    region_1.l_type = F_RDLCK;
    region_1.l_whence = SEEK_SET;
    region_1.l_start = 10;
    region_1.l_len = 20;
    //在文件的40~49字节设置写锁(独占锁)
    region_2.l_type = F_WRLCK;
    region_2.l_whence = SEEK_SET;
    region_2.l_start = 40;
    region_2.l_len = 10;

    printf("Process %d locking file\n", getpid());
    //锁定文件
    res = fcntl(file_desc, F_SETLK, ®ion_1);
    if(res == -1)
    {
        fprintf(stderr, "Failed to lock region 1\n");
    }
    res = fcntl(file_desc, F_SETLK, ®ion_2);
    if(res == -1)
    {
        fprintf(stderr, "Failed to lock region 2\n");
    }
    //让程序休眠一分钟,用于测试
    sleep(60);
    printf("Process %d closing file\n", getpid());
    close(file_desc);
    exit(EXIT_SUCCESS);
}

filelock3.c用于测试上一个文件设置的锁,测试可否对两个区域都加上一个读锁,代码如下:

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>

int main()
{
    const char *test_file = "test_lock.txt";
    int file_desc = -1;
    int byte_count = 0;
    char *byte_to_write = "A";
    struct flock region_1;
    struct flock region_2;
    int res = 0;
    //打开数据文件
    file_desc = open(test_file, O_RDWR|O_CREAT, 0666);
    if(!file_desc)
    {
        fprintf(stderr, "Unable to open %s for read/write\n", test_file);
        exit(EXIT_FAILURE);
    }
    //设置区域1的锁类型
    struct flock region_test1;
    region_test1.l_type = F_RDLCK;
    region_test1.l_whence = SEEK_SET;
    region_test1.l_start = 10;
    region_test1.l_len = 20;
    region_test1.l_pid = -1;
    //设置区域2的锁类型
    struct flock region_test2;
    region_test2.l_type = F_RDLCK;
    region_test2.l_whence = SEEK_SET;
    region_test2.l_start = 40;
    region_test2.l_len = 10;
    region_test2.l_pid = -1;
    //对区域1的是否可以加一个读锁进行测试
    res = fcntl(file_desc, F_GETLK, ®ion_test1);
    if(res == -1)
    {
        fprintf(stderr, "Failed to get RDLCK\n");
    }
    if(region_test1.l_pid == -1)
    {
        //可以加一个读锁
        printf("test: Possess %d could lock\n", getpid());
    }
    else
    {
        //不允许加一个读锁
        printf("test:Prossess %d  get lock failure\n", getpid());
    }

    //对区域2是否可以加一个读锁进行测试
    res = fcntl(file_desc, F_GETLK, ®ion_test2);
    if(res == -1)
    {
        fprintf(stderr, "Failed to get RDLCK\n");
    }
    if(region_test2.l_pid == -1)
    {
        //可以加一个读锁
        printf("test: Possess %d could lock\n", getpid());
    }
    else
    {
        //不允许加一个读锁
        printf("test:Prossess %d  get lock failure\n", getpid());
    }
    exit(EXIT_SUCCESS);
}
三、解空锁问题
如果我要给在本进程中没有加锁的区域解锁会发生什么事情呢?而如果这个区域中其他的进程有对其进行加锁又会发生什么情况呢?
 
如果一个进程实际并未对一个区域进行锁定,而调用解锁操作也会成功,但是它并不能解其他的进程加在同一区域上的锁。也可以说解锁请求最终的结果取决于这个进程在文件中设置的任何锁,没有加锁,但对其进行解锁得到的还是没有加锁的状态。

猜你喜欢

转载自www.cnblogs.com/tianzeng/p/9403376.html