前言
在TLPI上看到,用flock可以检测建议锁。可以用来检测单进程启动。写个demo整理一下。
运行效果
Jan 1 19:08:12 debian9 test_file_locker: [MY_LOGD : main.cpp.31 : main()] : ok, lock /var/lock/my_prog/my_prog_was_exist, i was the first process
Jan 1 19:08:21 debian9 test_file_locker: [MY_LOGD : ../../component/file_locker/file_locker.cpp.73 : lock_file()] : flock failed : Resource temporarily unavailable
Jan 1 19:08:21 debian9 test_file_locker: [MY_LOGD : main.cpp.27 : main()] : can't lock /var/lock/my_prog/my_prog_was_exist, maybe i wasn't first process
Jan 1 19:08:21 debian9 test_file_locker: [MY_LOGD : main.cpp.39 : main()] : bye
demo下载点
实验
// @file main.cpp
// @brief 测试后来的进程是否能打开文件锁, 如果打不开就对了
// @note
// 实验环境:
// debian9.6
#include <stdlib.h> // for EXIT_SUCCESS
#include <stdio.h> // for snprintf
#include "const_define.h" // for FILE_VAR_LOCK
#include "sys_log.h" // for MYLOG_D
#include "file_locker.h" // for cls_file_locker
int main(int argc, char** argv)
{
int i_rc = EXIT_FAILURE;
cls_file_locker locker;
char sz_buf[0x1000] = {'\0'};
do {
snprintf(sz_buf, sizeof(sz_buf), "mkdir -p %s", DIR_VAR_LOCK);
system(sz_buf);
if (!locker.lock_file(FILE_VAR_LOCK)) {
MYLOG_D("can't lock %s, maybe i wasn't first process", FILE_VAR_LOCK);
break;
}
MYLOG_D("ok, lock %s, i was the first process", FILE_VAR_LOCK);
printf("runing..., press any key to quit");
getchar();
i_rc = EXIT_SUCCESS;
} while (0);
MYLOG_D("bye");
return i_rc;
}
// @file linux_prj\component\file_locker\file_locker.h
#ifndef __LINUX_PRJ__COMPONENT__FILE_LOCKER__FILE_LOCKER_H__
#define __LINUX_PRJ__COMPONENT__FILE_LOCKER__FILE_LOCKER_H__
class cls_file_locker
{
public:
cls_file_locker();
virtual ~cls_file_locker();
bool lock_file(const char* psz_file_path_name);
private:
int m_i_fd;
const int m_i_lock_type;
bool m_b_lock_ok;
};
#endif // #ifndef __LINUX_PRJ__COMPONENT__FILE_LOCKER__FILE_LOCKER_H__
// @file linux_prj\component\file_locker\file_locker.cpp
// /usr/include/asm-generic/errno.h
// /usr/include/asm-generic/errno-base.h
#include <stdio.h> // for printf
#include <unistd.h> // for close
#include <sys/file.h> // for flock()
#include <fcntl.h> // for O_RDWR, struct flock
#include <errno.h> // for error
#include <string.h> // for strerror
#include "file_locker.h"
#include "sys_log.h"
// #define FILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define FILE_PERMS 0
cls_file_locker::cls_file_locker()
: m_i_fd(-1), m_i_lock_type(LOCK_EX | LOCK_NB), m_b_lock_ok(false)
{
// m_i_lock_type 为互斥锁 + 非阻塞, 此文件锁只能有一个主人
}
cls_file_locker::~cls_file_locker()
{
if (-1 != m_i_fd) {
if (m_b_lock_ok) {
flock(m_i_fd, LOCK_UN); // 解锁
}
close(m_i_fd);
m_i_fd = -1;
}
}
bool cls_file_locker::lock_file(const char* psz_file_path_name)
{
bool b_rc = false;
int i_rc = -1;
do {
if (NULL == psz_file_path_name) {
break;
}
// O_EXCL 是独占
// 第一次打开文件,假设文件不存在
m_i_fd = open(psz_file_path_name, O_RDWR | O_CREAT | O_EXCL, FILE_PERMS);
if (-1 == m_i_fd) {
if (EEXIST != errno) {
// 如果第一次打开,不是由于文件已经存在引起的打开错误,那就是真的错了
MYLOG_D("open failed : %s", strerror(errno));
break;
}
// 如果文件已经存在, 只用独占和读写去打开试试
m_i_fd = open(psz_file_path_name, O_RDWR | O_EXCL, FILE_PERMS);
if (-1 == m_i_fd) {
MYLOG_D("open failed : %s", strerror(errno));
break;
}
}
// flock在NFS(分布式网络文件服务器)上无效
// flock是建议锁, 要操作文件的程序必须要先上锁,得到锁后,再操作文件
// 建议锁并没有将锁和文件关联在一起
// 如果m_i_lock_type 不带 LOCK_NB 选项, 拿不到锁会阻塞
// 如果m_i_lock_type 带 LOCK_NB 选项, 拿不到锁会返回错误码,这种场景用来检测单进程启动不错
i_rc = flock(m_i_fd, m_i_lock_type); // 用共享方式打开文件锁
m_b_lock_ok = (-1 != i_rc);
if (-1 == i_rc) {
MYLOG_D("flock failed : %s", strerror(errno));
break;
}
b_rc = true;
} while (0);
return b_rc;
}