linux程序只运行一个实例

法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, ... /* struct flock *flockptr */ );
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等异常状态


你可能感兴趣的:(c++,linux,shell)