本文给出了进程只能单开的方法。
安装依赖
rust
cargo add fslock
使用方法
rust
use fslock::LockFile;
// 打开pid文件,没有则自动创建
let mut pid_lock =
LockFile::open(&pid_path.clone().into_os_string()).unwrap();
// 非阻塞的锁文件
if !pid_lock.try_lock_with_pid().unwrap() {
// 如果文件已经被锁,则退出进程
// ...
}
// 文件加锁成功,则执行业务逻辑
// ...
库支持的系统
此库支持 windows
、linux
和 mac
,通过如下代码实现
rust
#[cfg(unix)]
mod unix;
#[cfg(unix)]
use crate::unix as sys;
mod string;
mod fmt;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use crate::windows as sys;
原理简介
1. 锁操作
linux
调用 libc.so的接口实现,目的是不使用 std
库。
rust
pub fn try_lock(fd: FileDesc) -> Result<bool, Error> {
let res = unsafe { libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB) };
if res >= 0 {
Ok(true)
} else {
let err = errno();
if err == libc::EWOULDBLOCK || err == libc::EINTR {
Ok(false)
} else {
Err(Error::from_raw_os_error(err as i32))
}
}
}
windows
rust
pub fn try_lock(handle: FileDesc) -> Result<bool, Error> {
let mut overlapped = make_overlapped()?;
let drop_handle = DropHandle { handle: overlapped.hEvent };
let res = unsafe {
LockFileEx(
handle,
LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY,
0,
1,
1,
&mut overlapped as LPOVERLAPPED,
)
};
let ret = if res == TRUE {
let res = unsafe { WaitForSingleObject(overlapped.hEvent, 0) };
if res != WAIT_FAILED {
Ok(true)
} else {
Err(Error::last_os_error())
}
} else {
let err = unsafe { GetLastError() };
if err == ERROR_LOCK_VIOLATION {
Ok(false)
} else {
Err(Error::from_raw_os_error(err as i32))
}
};
drop(drop_handle);
ret
}
2. 进程退出解锁清空文件内容
rust
impl Drop for LockFile {
fn drop(&mut self) {
if self.locked {
let _ = self.unlock();
}
sys::close(self.desc);
}
}
pub fn unlock(&mut self) -> Result<(), sys::Error> {
if !self.locked {
panic!("Attempted to unlock already unlocked lockfile");
}
self.locked = false;
sys::unlock(self.desc)?;
// 解锁后清空文件内容
sys::truncate(self.desc)?;
Ok(())
}