linux -用fcntl实现单进程启动

前言

用fcntl检测锁文件实现单进程启动比用flock好。
flock在NFS服务器上不好使。
如果是在普通的linux服务器上,用fcntl和flock都行。
TPLI中讲了fcntl的用法,整理一下。封装一下,以后用。

实验

运行效果

Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.48 : show_version()] : ================================================================================
Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.49 : show_version()] : test_file_locker_fcntl
Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.50 : show_version()] : 1.0.0.1
Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.51 : show_version()] : 2018-11-11 14:04
Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.52 : show_version()] : ================================================================================
Jan  5 17:51:18 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.33 : main()] : ok, lock /var/lock/my_prog/my_prog_was_exist, i was the first process
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.48 : show_version()] : ================================================================================
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.49 : show_version()] : test_file_locker_fcntl
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.50 : show_version()] : 1.0.0.1
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.51 : show_version()] : 2018-11-11 14:04
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.52 : show_version()] : ================================================================================
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : ../../component/file_locker/file_locker_by_fcntl.cpp.84 : lock_file()] : lock file '/var/lock/my_prog/my_prog_was_exist' was failed : EAGAIN / Resource temporarily unavailable
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.29 : main()] : can't lock /var/lock/my_prog/my_prog_was_exist, maybe i wasn't first process
Jan  5 17:51:21 debian9 test_file_locker_fcntl: [MY_LOGD : main.cpp.41 : main()] : bye

工程下载点

prj_file_locker_by_fcntl.7z

工程预览

// @file linux_prj\component\file_locker\file_locker_by_fcntl.h

#ifndef __LINUX_PRJ__COMPONENT__FILE_LOCKER__FILE_LOCKER_BY_FCNTL_H__
#define __LINUX_PRJ__COMPONENT__FILE_LOCKER__FILE_LOCKER_BY_FCNTL_H__

class cls_file_locker_ex
{
public:
	cls_file_locker_ex();
	virtual ~cls_file_locker_ex();

	bool lock_file(const char* psz_file_path_name);

private:
	int lockReg(int fd, int cmd, int type, int whence, int start, off_t len);
	int lockRegion(int fd, int type, int whence, int start, int len);
	int lockRegionWait(int fd, int type, int whence, int start, int len);
	pid_t regionIsLocked(int fd, int type, int whence, int start, int len);
		
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_BY_FCNTL_H__
// @file linux_prj\component\file_locker\file_locker_by_fcntl.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_by_fcntl.h"
#include "sys_log.h"
#include "error_proc.h"

// #define FILE_PERMS	(S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
#define FILE_PERMS 0

cls_file_locker_ex::cls_file_locker_ex()
: m_i_fd(-1), m_i_lock_type(LOCK_EX | LOCK_NB), m_b_lock_ok(false)
{
	// m_i_lock_type 为互斥锁 + 非阻塞, 此文件锁只能有一个主人
}

cls_file_locker_ex::~cls_file_locker_ex()
{
	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_ex::lock_file(const char* psz_file_path_name)
{
	bool b_rc = false;
	char sz_buf[0x1000] = {'\0'};
	std::string str_error_desc = "";
	std::string str_errno_name = "";
	
	do {
		if (NULL == psz_file_path_name) {
			break;
		}

		// O_EXCL 是独占
		// 第一次打开文件,假设文件不存在
		m_i_fd = open(psz_file_path_name, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC, FILE_PERMS);
		if (-1 == m_i_fd) {
			if (EEXIST != errno) {
				// 如果第一次打开,不是由于文件已经存在引起的打开错误,那就是真的错了
				str_errno_name = errno_name(errno, str_error_desc);
				MYLOG_D("'%s' open failed : %s / %s",
					psz_file_path_name, 
					str_errno_name.c_str(), 
					str_error_desc.c_str());
			
				break;
			}
			
			// 如果文件已经存在, 只用独占和读写去打开试试
			m_i_fd = open(psz_file_path_name, O_RDWR | O_EXCL | O_CLOEXEC, FILE_PERMS);
			if (-1 == m_i_fd) {
				str_errno_name = errno_name(errno, str_error_desc);
				MYLOG_D("'%s' open failed : %s / %s",
					psz_file_path_name, 
					str_errno_name.c_str(), 
					str_error_desc.c_str());

				break;
			}
		}

		// lock file
		if (-1 == lockRegion(m_i_fd, F_WRLCK, SEEK_SET, 0, 0)) {
			str_errno_name = errno_name(errno, str_error_desc);
			MYLOG_D("lock file '%s' was failed : %s / %s", 
				psz_file_path_name, 
				str_errno_name.c_str(), 
				str_error_desc.c_str());
		
			break;
		}

		// set file length to 0
		if (-1 == ftruncate(m_i_fd, 0)) {
			MYLOG_D("Could not truncate PID file '%s', %s / %s", 
				psz_file_path_name, 
				str_errno_name.c_str(), 
				str_error_desc.c_str());
			
			break;
		}

		// write pid to lock file
		snprintf(sz_buf, sizeof(sz_buf) - 1, "%ld\n", (long) getpid());
		if (write(m_i_fd, sz_buf, strlen(sz_buf)) != (int)strlen(sz_buf)) {
			MYLOG_D("Writing to PID file '%s', %s / %s",
				psz_file_path_name, 
				str_errno_name.c_str(), 
				str_error_desc.c_str());
		}

		b_rc = true;
	} while (0);
	
	return b_rc;
}

int cls_file_locker_ex::lockReg(int fd, int cmd, int type, int whence, int start, off_t len)
{
    struct flock fl;

    fl.l_type = type;
    fl.l_whence = whence;
    fl.l_start = start;
    fl.l_len = len;

    return fcntl(fd, cmd, &fl);
}

int cls_file_locker_ex::lockRegion(int fd, int type, int whence, int start, int len)
{
    return lockReg(fd, F_SETLK, type, whence, start, len);
}

int cls_file_locker_ex::lockRegionWait(int fd, int type, int whence, int start, int len)
{
    return lockReg(fd, F_SETLKW, type, whence, start, len);
}

pid_t cls_file_locker_ex::regionIsLocked(int fd, int type, int whence, int start, int len)
{
    struct flock fl;

    fl.l_type = type;
    fl.l_whence = whence;
    fl.l_start = start;
    fl.l_len = len;

    if (fcntl(fd, F_GETLK, &fl) == -1)
        return -1;

    return (fl.l_type == F_UNLCK) ? 0 : fl.l_pid;
}



// @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_by_fcntl.h" // for cls_file_locker

void show_version();

int main(int argc, char** argv)
{
	int i_rc = EXIT_FAILURE;
	cls_file_locker_ex locker;
	char sz_buf[0x1000] = {'\0'};

	do {
		show_version();

		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;
}

void show_version()
{
	MYLOG_D(TITLE_LINE80);
	MYLOG_D(PROG_NAME);
	MYLOG_D(PROG_VER);
	MYLOG_D(RELEASE_TIME);
	MYLOG_D(TITLE_LINE80);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值