法1.使用文件的排他锁,排他锁(Exclusive Locks,简称X锁),又称为写锁、独占锁,是一种基本的锁类型。
若事务T对数据对象A加上X锁,则只允许T读取和修改A,其他任何事务都不能再对A加任何类型的锁,直到T释放A上的锁。这就保证了其他事务在T释放A上的锁之前不能再读取和修改A。
int flock(int fd, int operation);
其中,参数 fd 表示文件描述符;参数 operation 指定要进行的锁操作,该参数的取值有如下几种:LOCK_SH,LOCK_EX, LOCK_UN 和 LOCK_MAND
- LOCK_SH:表示要创建一个共享锁,在任意时间内,一个文件的共享锁可以被多个进程拥有
- LOCK_EX:表示创建一个排他锁,在任意时间内,一个文件的排他锁只能被一个进程拥有
- LOCK_UN:表示删除该进程创建的锁
- LOCK_MAND: 它主要是用于共享模式强制锁,它可以与 LOCK_READ 或者 LOCK_WRITE联合起来使用,从而表示是否允许并发的读操作或者并发的写操作(尽管在 flock() 的手册页中没有介绍LOCK_MAND,但是阅读内核源代码就会发现,这在内核中已经实现了)
通常情况下,如果加锁请求不能被立即满足,那 么系统调用 flock()会阻塞当前进程。比如,进程想要请求一个排他锁,但此时,已经由其他进程获取了这个锁,那么该进程将会被阻塞。如果想要在没有获得这个排他锁的情况下不阻塞该进程,可以将 LOCK_NB 和 LOCK_SH 或者 LOCK_EX 联合使用,那么系统就不会阻塞该进程。flock()所加的锁会对整个文件起作用。
flock是对同一个文件进行加锁,才会对不同进程进行排斥,所以,如果一个进程加锁后,是可以独享这个文件的操作权,但是,如果另一个进程不去尝试加锁,就直接往该文件写东西,那还是会写进去的。这里的防止多进程排斥,其实是要求每个进程必须先去尝试加锁,这就是flock的真正含义加锁。
文件在释放前,要释放锁即调用flock,使用LOCK_UN,进程退出时,锁自动释放,关闭文件时,锁自动释放,fork产生的子进程不继承锁,子进程需要调用fcntl才能获得它自己的锁。执行exec后,新程序可以继承原先程序的锁,执行exec后,其实是用当前进程的进程实体替换原进程的进程实体。
原进程被杀掉(但保留了 进程ID即 PID)
#include
int checkexit(char* pfile)//文件名
{
if (pfile == NULL)
{
return -1;
}
int lockfd = open(pfile,O_RDWR);//打开文件
if (lockfd == -1)
{
return -2;
}
int iret = flock(lockfd,LOCK_EX|LOCK_NB);//对打开的文件加排他锁,非阻塞,
if (iret == -1)
{
return -3;
}
return 0;
}
法2.记录锁当我们有多个进程要访问同一个文件的时候,为了防止多进程访问导致的不一致,我们就要考虑进程间的同步问题了。fcntl是一个
非常强大的函数,在这里我们可以使用它来给文件的某一个部分上锁。
int fcntl(int filedes, int cmd, ... );
filedes自然是要操作的文件描述符,对与记录锁相关的操作,cmd只能是F_GETLK, F_SETLK, 或者 F_SETLKW,而第三个参数则必须是一
个指向flock结构体的指针
struct flock {
short l_type;/*F_RDLCK, F_WRLCK, or F_UNLCK */
off_t l_start;/*offset in bytes, relative to l_whence */
short l_whence;/*SEEK_SET, SEEK_CUR, or SEEK_END */
off_t l_len;/*length, in bytes; 0 means lock to EOF */
pid_t l_pid;/*returned with F_GETLK */
};
F_RDLCK 建立一个供读取用的锁定 F_WRLCK 建立一个供写入用的锁定 F_UNLCK 删除之前建立的锁定
第一个成员是加锁的类型:只读锁,写锁,或是解锁。l_start和l_whence用来指明加锁部分的开始位置,l_len是加锁的长度,l_pid是加
锁进程的进程id。比如说,我们现在需要把一个文件的前三个字节加读锁,则该结构体的l_type=F_RDLCK, l_start=0, l_whence=SEEK_SET,
l_len=3,l_pid不需要指定,然后调用fcntl函数时,cmd参数使用F_SETLK.
只有对文件有相应的读写权限才能施加对应的文件锁
bool SingleInstance::LockFile(int fd)
{
/* 只有对文件有相应的读写权限才能施加对应的文件锁 */
struct flock fl;
fl.l_type = F_WRLCK; // 读写锁,即不允许其他进程再对其加任何类型的锁,但读锁(共享锁)允许
fl.l_start = 0; //从文件开头开始锁定
fl.l_whence = SEEK_SET;
fl.l_len = 0; // 文件全部内容锁住
return (0 == fcntl(fd, F_SETLK, &fl));//锁成功
}
int SingleInstance::AlreadyRunning()
{
int fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE);
if (fd < 0)
return -1;
if (!LockFile(fd))
{
if (errno == EACCES || errno == EAGAIN)
{
close(fd);
return 1;
}
return -1;
}
return 0;
}
法3.system调用脚本,脚本去判断当前有几个进程,包括当前的程序也算,脚本返回进程数,如果system返回1则表示只有当前进程,返回2则
表示已经有进程在运行
checkProcess.sh
#!/bin/bash
ret=`ps -ef|grep $1|grep -v grep|wc -l`
exit $ret
注意system函数有时会返回256,512等异常状态