代码改变世界

linux file lock文件锁

2022-11-21 16:46  youxin  阅读(894)  评论(0编辑  收藏  举报

http://www.kaotop.com/it/37363.html

https://www.codenong.com/cs106758831/

 

 

上锁
文件锁有两种
shared lock 共享锁

exclusive lock 排他锁

当文件被上了共享锁之后,其他进程可以继续为此文件加共享锁,但此文件不能被加排他锁,此文件会有一个共享锁计数,加上一个共享锁计数+1,解锁后-1,只有当共享锁计数为0时,才有可能被上排他锁。
当文件被上了排锁之后,在解锁前,不能上共享锁和排他锁。

 

 

命令flock [options] [command args]

flock [options] -c

flock [options]

options:-s, -share 读锁,共享锁

-x, -e, --exclusive 写锁,独占锁,这是默认值

-u, --unlock 解锁,通常不需要解锁,因为在文件关闭时会自动解锁

-n, --nonblock 无法立刻获取锁定时退出,而不是等待

-w, --timeout 几秒内无法获取锁定则退出

-E, --conflict-exit-code exit code after conflict or timeout

-o, --close close file descriptor before running command

-c, --command 提供一个字符串形式的shell命令来执行

-h, --help display this help and exit

-V, --version output version information and exit

示例[root@localhost ~]# flock -s ~/test.lock -c "sleep 10" &

 

 

# 在flock.lock文件上加排他锁后,执行休眠30s flock -x flock.lock -c 'sleep 30' # command 1

# 另起一个终端 flock -s flock.lock -c 'echo hello' # 会在 # <command 1> 执行后30s 输出'hello'

#加读锁

[1] 1854

[root@localhost ~]# flock -xn~/test.lock -c "echo 1"

Linux中的例行性工作排程crontab会定时执行一些脚本,但脚本的执行时间往往无法控制,当脚本执行时间过长时,可能会导致上一次任务的脚本还没执行完,下一次任务的脚本又开始执行了。
这种情况下可能会出现一些并发问题,严重时会导致出现脏数据/性能瓶颈的恶性循环。

  • 使用flock命令
    (1)使用方式如下,这个命令的好处是等待动作在flock命令中完成,无需另外添加代码。
    ( flock 300 …cmd… flock -u 300 ) > /tmp/file.lock
    (2)但flock有个缺陷是,在打开flock之后fork(),子进程也会拥有锁,如果在flock其间有运行daemon的话,必需确保daemon在启动时已经关闭了所有的文件句柄,不然该文件会因为daemon一直将其置于打开状态而无法解锁。

2.使用flock命令

    • flock 是对于整个文件的建议性锁。
      (1)也就是说,如果一个进程在一个文件(inode)上放了锁,那么其它进程是可以知道的。(建议性锁不强求进程遵守。)
      (2)最棒的一点是,它的第一个参数是文件描述符,在此文件描述符关闭时,锁会自动释放。
      (3)而当进程终止时,所有的文件描述符均会被关闭。
    • shell中实现flock系统调用的命令是flock,其格式为:

#cat /scripts/shell/file_lock.sh

#!/bin/bash

# Description: test for file flock

PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin

export PATH
echo ""

echo "----------------------------------"
echo "start at `date '+%Y-%m-%d %H:%M:%S'` ..."
sleep 140s
echo "finished at `date '+%Y-%m-%d %H:%M:%S'` ..."


=====================创建定时任务:测试排它锁=========================================#crontab -e
* * * * * flock -xn /dev/shm/test.lock -c "sh /scripts/shell/file_lock.sh > /root/stdout.log"
##每分钟执行一次该脚本,并将输出信息写入到stdout.log

// write_1.cc
#include <sys/file.h>
#include <stdio.h>
#include <assert.h>
#include <string.h>

int main(int argc,char* argv[]){
    const char* lock_file_path = "flock.lock";
    if(argc >= 2){lock_file_path = argv[1];}
    int fd = 0;
    assert((fd = open(lock_file_path,"r")) > 0); // 使用文件锁之前须要得到锁文件的file desc
    assert(0 == flock(fd,LOCK_SH)) // 加上共享锁
    char buff[256],str[1024];
    bzero(buff,sizeof(buff));
    bzero(str,sizeof(str));
    size_t rd = 0;
    while((rd = read(fd,buff,sizeof(buff)-1)) > 0) {
        buff[rd] = '\0';
        strcat(str,buff);
    }
    printf("load str from %s is %s\n",lock_file_path,str);
    flock(fd,LOCK_UN) // 解锁
    close(fd); // 如果没有多个 file_description 指向同一个文件,那么可以使用close(fd),隐式解锁,但不建议
    return 0;
}
// read_1.cc
#include <sys/file.h>
#include <stdio.h>
#include <assert.h>
#include <signal.h>
#include <stdlib.h>
#include <unistd.h>

int fd = 0;
void signal_handle(int sig){
    switch(sig){
        case SIGKILL:
        case SIGQUIT:
        case SIGINT:
        case SIGSTOP:
            if(fd){
                flock(fd,LOCK_UN);
                close(fd);
            }
            exit(0);
        default:
            return;
    }
}
int main(int argc,char* argv[]){
    const char* lock_file_path = "flock.lock";
    if(argc >= 2){
        lock_file_path = argv[1];
    }
    assert((fd = open(lock_file_path,"w")) > 0);
    
    signal(SIGKILL,signal_handle);
    signal(SIGQUIT,signal_handle);
    signale(SIGINT,signal_handle);

    while(true){
        assert(0 == flock(fd,LOCK_EX)) // 加上排他锁
        char *str=nullptr;
        size_t str_size = 0;
        getline(&str,&str_size,stdin);
        write(fd,str,str_size);
        assert(0 == flock(fd,LOCK_UN));
        sleep(2);
    }
    return 0;
}
  • 简易包装成guard
// flock_guard.hpp
#include <string>
#include <sys/file.h>
#include <cassert>

class flock_guard{
    int fd;
public:
    flock_guard(const std::string& filepath,
                int lock_type // LOCK_SH 或 LOCK_EX
                ){
        assert((fd = open(filepath.c_str(),"r")) > 0);
        assert(0 == flock(fd,lock_type));
    }
    virtual ~flock_guard(){
        flock(fd,LOCK_UN);
        close(fd);
    }
};

// 包装读写锁
// read flock guard 
class rflock_guard : public flock_guard{ 
public:
    rflock_guard(const std::string& filepath)
        :flock_guard(filepath,LOCK_SH) //使用共享锁 作读锁
    {}
};
// write flock guard
class wflock_guard:public flock_guard{
public:
    wflock_guard(const std::string& filepath)
        :flock_guard(filepath,LOCK_EX) // 使用排他锁 作写锁
    {}
};

 



通过使用flock建立排它锁可以规避这个问题