7.进程同步:借助 fcntl函数来实现锁机制

版权声明:guojawee https://blog.csdn.net/weixin_36750623/article/details/83473366

问:为什么要使用文件锁?
答:当多个用户共同使用、操作一个文件的情况时,Linux采用的方法就是给文件上锁,来避免共享的资源产生竞争的状态。

问:文件锁有哪些类型?
答:文件锁包括建议性锁和强制性锁。一般情况下,内核和系统都不适用建议性锁,采用强制性锁的影响很大,每次读写操作都必须检查是否有所存在。

问:建议性锁/强制性锁的定义?
建议性锁是指给文件上锁后,只在文件上设置了一个锁的标识。其他进程在对这个文件进程操作时,可以检测到锁的存在,但这个锁并不能阻止它对这个文件进行操作。这就好比红绿灯,当亮红灯时,告诉你不要过马路,但如果你一定要过,也拦不住你。
强制性锁则是当给文件上锁后,当其他进程要对这个文件进程不兼容的操作(如上了读锁,另一个进程要写),则系统内核将阻塞后来的进程直到第一个进程将锁解开

问:怎样实现文件上锁?
答:在Linux中,实现文件上锁的函数有lockf() 和fcntl() ,其中lockf()用于对文件施加建议性锁,而fcntl() 不仅可以施加建议性锁,而且可以施加强制性锁。fcntl()还能对文件的某一记录上锁,也就是记录锁。记录锁又可分读取锁(共享锁)和写入锁(排斥锁),文件的同一部分不能同时建立读取锁和写入锁。

借助 fcntl函数来实现锁机制: 操作文件的进程没有获得锁时,可以打开,但无法执行read、write操作

  int fcntl(int fd, int cmd, ... /* arg */ );
  参2:
     F_SETLK (struct flock *)	用来[非阻塞式]加锁、解锁(trylock)
     F_SETLKW (struct flock *)  用来[阻塞式]加锁、解锁
     F_GETLK (struct flock *)	用来测试锁,注意是测试而不是获取锁
  参3:
     struct flock {
        ...
        short l_type;    	锁的类型:F_RDLCK 、F_WRLCK 、F_UNLCK
        
        short l_whence;  	偏移位置:SEEK_SET、SEEK_CUR、SEEK_END 
        off_t l_start;   		起始偏移:1000
        off_t l_len;     		长度:0表示整个文件加锁
        
        pid_t l_pid;     	持有该锁的进程ID:(F_GETLK only)
        ...
      };

参2:cmd

  • F_GETLK 用来测试锁,注意是测试而不是获取锁;
  • F_SETLK 用来加锁、解锁;非阻塞式;trylock
  • F_SETLKW 功能同F_SETLK,只是操作变成阻塞式的。lock

参3:struct flock

  • l_type在不同的系统中l_type的值是不一样的(需要注意的是不同系统的实现并不一样,宏定义也不一样)
 /* record locking flags (F_GETLK, F_SETLK, F_SETLKW) */
#define    F_RDLCK        1        /* shared or read lock */
#define    F_UNLCK        2        /* unlock */
#define    F_WRLCK        3        /* exclusive or write lock */
而在debian中,/usr/include/bits/fcntl.h
/* For posix fcntl() and  l_type' field of a `struct flock' for lockf().  */
#define F_RDLCK         0       /* Read lock.  */
#define F_WRLCK         1       /* Write lock.  */
#define F_UNLCK         2       /* Remove lock.  */
  • fcntl可以用过l_whence、l_start、l_len来控制文件上锁的区间
  • l_pid当文件被上锁时,使用测试锁进行测试时,l_pid被赋值为当前对文件加锁的进程的pid

示例代码

代码解读
(1) slock先给文件上写锁,然后glock测试读共享锁是否能加上,测试结果是已存在一个写锁(F_WRLCK,debian/centos下定义为1)。
(2) 这里需要注意的是F_GETLK是测试锁是否能加上,并不是真正的加锁

  • 如果可以,则struct flock中的l_type为F_UNLCK(2);
  • 如果不行,则l_type为文件当前锁的类型,而l_pid为上锁的进程pid。
程序执行结果:
./slock
sleep now ...
./glock
lock is 1
exit...

程序修改:如果slock上的锁是F_RDLCK,glock测试的锁也是F_RDLCK—>因为这两个锁是兼容的,所以返回的l_type类型为F_UNLCK(此处非常的特别:l_type没有返回F_RDLCK,而是返回F_UNLCK<==>即你不能通过F_GETLK来判断文件是否上锁,只能测试某个锁是否能加上)

修改后的程序执行结果:
./slock
sleep now ...
./glock
lock is 1
exit...

/* slock.c */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>

int main()
{
    struct flock _lock;

    _lock.l_type =  F_WRLCK;  //程序修改:F_RDLCK
    _lock.l_whence = SEEK_SET;
    _lock.l_start = 0;
    _lock.l_len = 0;

    int fd = open( "/dev/shm/test",O_CREAT|O_RDWR,S_IRWXU|S_IRGRP|S_IWGRP|S_IRWXO );
    if ( fd < 0 )
    {
        puts( "open error" );
        return 0;
    }

    int ret = fcntl( fd,F_SETLK,&_lock ); //先对文件加锁
    if ( ret < 0 )
    {
        puts( "fcntl error" );
        close( fd );
        return 0;
    }

    puts( "sleep now ..." );
    sleep( 100 );
    puts( "exit..." );
    
    _lock.l_type =  F_UNLCK;
    _lock.l_whence = SEEK_SET;
    _lock.l_start = 0;
    _lock.l_len = 0;

    ret = fcntl( fd,F_SETLK,&_lock ); 
    if ( ret < 0 )
    {
        puts( "unlock error" );
    }

    close( fd );
}
/* glock.c */

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>

int main()
{
    struct flock _lock;

    _lock.l_type =  F_RDLCK;
    _lock.l_whence = SEEK_SET;
    _lock.l_start = 0;
    _lock.l_len = 0;

    int fd = open( "/dev/shm/test",O_RDWR );
    if ( fd < 0 )
    {
        perror( "open error" );
        return 0;
    }

    int ret = fcntl( fd,F_GETLK,&_lock ); //F_GETLK测试文件锁
    if ( ret < 0 )
    {
        perror( "fcntl error:" );
        close( fd );
        return 0;
    }

    printf( "lock is %d\n",_lock.l_type );

    close( fd );
}

猜你喜欢

转载自blog.csdn.net/weixin_36750623/article/details/83473366