apue自学笔记14.3 记录锁.fcntl记录锁

42 篇文章 0 订阅

函数原型如下:

#include<fcntl.h>

int fcntl(int fd,int cmd,.../* struct flock *flockptr */);
                    //返回值:若成功,依赖于cmd,否则,返回-1

//cmd 取下面三个值之一
#  define F_GETLK	5	/* Get record locking info.  */
#  define F_SETLK	6	/* Set record locking info (non-blocking).  */
#  define F_SETLKW	7	/* Set record locking info (blocking).	*/

//flockptr是下面结构体的指针
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').  */
#ifndef __USE_FILE_OFFSET64
    __off_t l_start;	/* Offset where the lock begins.  */
    __off_t l_len;	/* Size of the locked area; zero means until EOF.  */
#else
    __off64_t l_start;	/* Offset where the lock begins.  */
    __off64_t l_len;	/* Size of the locked area; zero means until EOF.  */
#endif
    __pid_t l_pid;	/* Process holding the lock.  */
  };

//__off_t 是 long int

//l_type 取下面三个值之一
# define F_RDLCK		0	/* Read lock.  */
# define F_WRLCK		1	/* Write lock.	*/
# define F_UNLCK		2	/* Remove lock.	 */

//l_whence 取下面三个值之一
#define SEEK_SET	0	/* Seek from beginning of file.  */
#define SEEK_CUR	1	/* Seek from current position.  */
#define SEEK_END	2	/* Seek from end of file.  */

fcntl函数的3种命令

F_GETLK

判断由flockptr所描述的锁是否会被另外一把锁排斥(阻塞)。如果存在一把锁,它阻止创建由flockptr所描述的锁,则将现有锁的信息将重写在flockptr指向的信息。如果不存在这种情况,则除了将l_type设置为F_UNLCK之外,flockptr所指向的结构中的其他信息保持不变。

写一个程序验证一下

#include <stdio.h>
#include <fcntl.h>
#include <zconf.h>

int main() {
    int fd;
    fd = open("/home/gilbert/test.txt",O_RDONLY);
    if(fd == -1){
        printf("open file failed.\n");
        return 1;
    }
    struct flock f_lock;
    f_lock.l_type = F_RDLCK;
    f_lock.l_whence = SEEK_SET;
    f_lock.l_start = 0;
    f_lock.l_len = 10;

    int ret_fcntl = fcntl(fd,F_GETLK,&f_lock);
    if(ret_fcntl == -1){
        printf("fcntl falied.\n");
    }
    if(f_lock.l_type == F_UNLCK)
        printf("f_lock.l_type changed from F_RDLCK to F_UNLCK.\n");
    if(f_lock.l_whence == SEEK_SET)
        printf("f_lock.l_whence didn't changed.\n");
    if(f_lock.l_start == 0)
        printf("f_lock.l_start didn't changed.\n");
    if(f_lock.l_len == 10)
        printf("f_lock.l_len didn't changed.\n");
    printf("ret_fcntl: %d,pid: %d\n",ret_fcntl,getpid());

    return 0;
}

输出:

f_lock.l_type changed from F_RDLCK to F_UNLCK.
f_lock.l_whence didn't changed.
f_lock.l_start didn't changed.
f_lock.l_len didn't changed.
ret_fcntl: 0,pid: 8500

Process finished with exit code 0

这个程序是将/home/gilbert/test.txt这个文件打开,然后将其从文件开始处起到第十个字节位置使用共享读锁锁起来。因为不存在其他排斥这个锁的锁,所以除了将l_type设置为F_UNLCK之外,flockptr所指向的结构中的其他信息保持不变。

接下来,我们在已有一个共享读锁的情况下再加一个独占性写锁。

#include <stdio.h>
#include <fcntl.h>
#include <zconf.h>

int main() {
    int fd;
    fd = open("/home/gilbert/test.txt",O_RDONLY);
    if(fd == -1){
        printf("open file failed.\n");
        return 1;
    }
    struct flock f_lock;
    f_lock.l_type = F_RDLCK;
    f_lock.l_whence = SEEK_SET;
    f_lock.l_start = 0;
    f_lock.l_len = 10;

    int ret_fcntl = fcntl(fd,F_GETLK,&f_lock);
    if(ret_fcntl == -1){
        printf("fcntl falied.\n");
    }
    struct flock f_lock2;
    f_lock2.l_type = F_WRLCK;
    f_lock2.l_whence = SEEK_SET;
    f_lock2.l_start = 1;
    f_lock2.l_len = 9;
    int ret_fcntl2 = fcntl(fd,F_GETLK,&f_lock2);
    if(ret_fcntl2 == -1){
        printf("fcntl2 falied.\n");
    }
    printf("f_lock2.l_type: %d, f_lock2.l_whence: %d, f_lock2.l_start: %ld,"
           " f_lock2.l_len: %ld\n",f_lock2.l_type,f_lock2.l_whence,f_lock2.l_start,f_lock2.l_len);

    return 0;
}

输出:

f_lock2.l_type: 2, f_lock2.l_whence: 0, f_lock2.l_start: 1, f_lock2.l_len: 9

Process finished with exit code 0

可以看到,除了f_lock2.l_type从F_WRLCK(1)变为F_UNLCK(2)之外,其他的都没有变,说明这个写锁没有被阻塞,这是不是和前面说的

如果存在一把锁,它阻止创建由flockptr所描述的锁,则将现有锁的信息将重写在flockptr指向的信息。

 相矛盾呢?

为什么原有的读锁没有阻塞这个新的写锁呢?

这是因为读锁和写锁在同一进程里。后面的锁会覆盖前面的,关于锁的兼容性,指的是不同进程之间的。

现在我们在两个不同的进程里面再加锁两次看看结果如何。

#include <stdio.h>
#include <fcntl.h>
#include <zconf.h>

int main() {
    int pid;
    pid = fork();
    if(pid == -1){
        printf("fork failed.\n");
        return 1;
    }
    else if(pid == 0){
        //child process
        sleep(2);
        printf("sleep finished.\n");
        int fd;
        fd = open("/home/gilbert/test.txt",O_WRONLY);
        if(fd == -1){
            printf("open file failed.\n");
            return 1;
        }
        struct flock f_lock2;
        f_lock2.l_type = F_WRLCK;
        f_lock2.l_whence = SEEK_SET;
        f_lock2.l_start = 1;
        f_lock2.l_len = 9;
        int ret_fcntl2 = fcntl(fd,F_GETLK,&f_lock2);
        if(ret_fcntl2 == -1){
            printf("fcntl2 falied.\n");
        }
        printf("f_lock2.l_type: %d, f_lock2.l_whence: %d, f_lock2.l_start: %ld,"
               " f_lock2.l_len: %ld\n",f_lock2.l_type,f_lock2.l_whence,f_lock2.l_start,f_lock2.l_len);
        _exit(0);

    }
    else{
        //parent process
        int fd;
        fd = open("/home/gilbert/test.txt",O_RDONLY);
        if(fd == -1){
            printf("open file failed.\n");
            return 1;
        }
        struct flock f_lock;
        f_lock.l_type = F_RDLCK;
        f_lock.l_whence = SEEK_SET;
        f_lock.l_start = 0;
        f_lock.l_len = 10;

        int ret_fcntl = fcntl(fd,F_GETLK,&f_lock);
        if(ret_fcntl == -1){
            printf("fcntl falied.\n");
        }
        sleep(5);
    }

    return 0;
}

输出

sleep finished.
f_lock2.l_type: 2, f_lock2.l_whence: 0, f_lock2.l_start: 1, f_lock2.l_len: 9

Process finished with exit code 0

可以看到,除了f_lock2.l_type从F_WRLCK(1)变为F_UNLCK(2)之外,其他的都没有变,说明这个写锁没有被阻塞。这又是为什么呢?

这是因为,fcntl(fd,F_GETLK,&f_lock);这句只是起测试作用,测试这个锁会不会被其他锁阻塞,并没有真的添加一个锁。要添加一个锁,还要在此基础上添加一个锁,而且,若要加读锁,文件需要以只读方式打开,若要加写锁,文件需以只写方式打开。

修改上面的代码

#include <stdio.h>
#include <fcntl.h>
#include <zconf.h>

int main() {
    int pid;
    pid = fork();
    if(pid == -1){
        printf("fork failed.\n");
        return 1;
    }
    else if(pid == 0){
        //child process
        sleep(2);
        printf("sleep finished.\n");
        int fd;
        fd = open("/home/gilbert/test.txt",O_WRONLY);
        if(fd == -1){
            printf("open file failed.\n");
            return 1;
        }
        struct flock f_lock2;
        f_lock2.l_type = F_WRLCK;
        f_lock2.l_whence = SEEK_SET;
        f_lock2.l_start = 1;
        f_lock2.l_len = 9;
        int ret_fcntl2 = fcntl(fd,F_GETLK,&f_lock2);
        if(ret_fcntl2 == -1){
            printf("fcntl2 falied.\n");
        }
        printf("f_lock2.l_type: %d, f_lock2.l_whence: %d, f_lock2.l_start: %ld,"
               " f_lock2.l_len: %ld\n",f_lock2.l_type,f_lock2.l_whence,f_lock2.l_start,f_lock2.l_len);
        _exit(0);

    }
    else{
        //parent process
        int fd;
        fd = open("/home/gilbert/test.txt",O_RDONLY);
        if(fd == -1){
            printf("open file failed.\n");
            return 1;
        }
        struct flock f_lock;
        f_lock.l_type = F_RDLCK;
        f_lock.l_whence = SEEK_SET;
        f_lock.l_start = 0;
        f_lock.l_len = 10;

        int ret_fcntl = fcntl(fd,F_GETLK,&f_lock);
        if(ret_fcntl == -1){
            printf("fcntl falied.\n");
        }
        if(f_lock.l_type == F_UNLCK){
            f_lock.l_type = F_RDLCK;
            ret_fcntl = fcntl(fd,F_SETLK,&f_lock);
            if(ret_fcntl == -1){
                printf("fcntl falied.\n");
            }
            printf("add a read lock.\n");
        }
        sleep(5);
    }

    return 0;
}

输出:

add a read lock.
sleep finished.
f_lock2.l_type: 0, f_lock2.l_whence: 0, f_lock2.l_start: 0, f_lock2.l_len: 10

现在看到,当父进程加了读锁时,子进程再去用写锁测试,写锁会被读锁阻塞,f_lock2结构将被读锁的信息所重写。

如果子进程用读锁去测试呢,得到的结果是,不会阻塞,除了f_lock2.l_type变为F_UNLCK之外,其他都不会变,因为读锁之间是兼容的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值