打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
LevelDB源码剖析之确保系统中只有一个levelDB实例 | 赖明星
userphoto

2014.03.25

关注

如何保证应用程序只有一个实例?以前学MFC的时候就遇到过这个问题,当时只要简单的设置下参数就可以了,没有深究,现在研究levelDB又遇到这个问题,所以,我决定打破沙锅问到底,看看到底是怎么实现的!

如果你月薪一万,别人问你工资多少,你告诉别人你月薪一万,那没有什么特别的,谁也不会在意,如果你月薪一万,却告诉别人你月收入五位数,那,给对方的感觉是完全不一样的,尤其是相亲的时候:-)

我一直好奇如何保证一个应用程序只能有一个实例,当我知道了以后,反而有那么一点点失望,因为它就那么回事,真没有什么高深的东西,你不知道,只是因为你不知道,或者说是因为你知道得太少,而不是这东西太难了。废话真多,下面就来看一下如何保证应用程序只能有一个实例的吧。

直接把目光定位到db_impl.cc文件的下DB::Open函数里的这行代码:

Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists

Recover函数完成levelDB 启动时的一些基本检查,主要包括下面两部分:

  1. 根据传入的db 路径,对LOCK 文件做flock 来判断是否已经有db实例启动,一份数据同时只能有一个db 实例操作。
  2. 根据option 内的create_if_missing / error_if_exists来确定数据目录已经存在的时候要做的处理

下面是Recover函数的部分代码:

  // Ignore error from CreateDir since the creation of the DB is  // committed only when the descriptor is created, and this directory  // may already exist from a previous failed creation attempt.  env_->CreateDir(dbname_);  assert(db_lock_ == NULL);  Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);  if (!s.ok()) {    return s;  }  if (!env_->FileExists(CurrentFileName(dbname_))) {    if (options_.create_if_missing) {      s = NewDB();      if (!s.ok()) {        return s;      }    } else {      return Status::InvalidArgument(          dbname_, "does not exist (create_if_missing is false)");    }  } else {    if (options_.error_if_exists) {      return Status::InvalidArgument(          dbname_, "exists (error_if_exists is true)");    }  }

可以看到,它就做了我们上面说的两件事。我们主要来看看LockFile函数。在看LockFile函数前,先把LockFileName 弄清楚了,它的定义如下:

std::string LockFileName(const std::string& dbname) {  return dbname + "/LOCK";}

假如我们的数据库名子为testdb ,那么,这个函数的功能就是返回一个testdb/LOCK字符串,用于我们定义testdb 目录下的LOCK文件,LOCK文件是一个空文件,它的存在就是帮助实现一个应用程序只存在一个实例。

LockFile 是env_的成员函数,我们知道,env_一个指向PosixEnv的指针,所以,我们直接把注意力转移到PosixEnv 类的LockFile 文件即可。

  virtual Status LockFile(const std::string& fname, FileLock** lock) {    *lock = NULL;    Status result;    int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644);    if (fd < 0) {      result = IOError(fname, errno);    } else if (!locks_.Insert(fname)) {      close(fd);      result = Status::IOError("lock " + fname, "already held by process");    } else if (LockOrUnlock(fd, true) == -1) {      result = IOError("lock " + fname, errno);      close(fd);      locks_.Remove(fname);    } else {      PosixFileLock* my_lock = new PosixFileLock;      my_lock->fd_ = fd;      my_lock->name_ = fname;      *lock = my_lock;    }    return result;  }

LockFile首先打开文件,如果文件不存在,就创建,打开文件以后判断文件是否打开失败,如果打开失败,就返回错误信息,否则,就执行其他语句,我们把注意力集中到下面这条语句。

} else if (LockOrUnlock(fd, true) == -1) {

顾名思意,它要锁住当前文件,怎么做到的呢?我们看看这个函数的定义就知道了。

static int LockOrUnlock(int fd, bool lock) {  errno = 0;  struct flock f;  memset(&f, 0, sizeof(f));  f.l_type = (lock ? F_WRLCK : F_UNLCK);  f.l_whence = SEEK_SET;  f.l_start = 0;  f.l_len = 0;        // Lock/unlock entire file  return fcntl(fd, F_SETLK, &f);}

如果你看过《Unix环境高级编程》,你一定跟我一样,看到这里就笑了,然后心里默念:原来如此!!

我们只看关键的语句,从函数的名字就可以看出来,这个函数要实现对文件的加锁解锁功能,当文件已经被加锁了,我们就解锁,当文件没有锁的时候,我们就加锁。

f.l_type = (lock ? F_WRLCK : F_UNLCK);

然后通过fcntl 系统调用来对文件加锁,当我们对文件加了F_WRLCK(独占锁)锁以后,其他程序打开这个文件的时候,就会出错。

当我们有一个levelDB实例运行的时候,第二个levelDB实例在调用LockFile函数企图打开LOCK文件时,由于第一个levelDB 实例已经将该文件加锁了,第二个levelDB实例打开LOCK文件时就会出错,出错以后就直接返回了。这样就实现了应用程序只有一个实例在运行。

int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644);

有加锁操作,自然也有解锁操作了,在~DBImpl()函数中有这么一条语句:

  if (db_lock_ != NULL) {    env_->UnlockFile(db_lock_);  }

对于以前不知道如何保证应用程序只能有一个实例的你,是不是有点惊讶,怎么这么简单,都是以前学过的知识!

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
Linux中使用flock函数
小何讲Linux: 文件锁及其实例
运行单个实例
FATFS搜索文件函数
批量将工作簿转换为PDF----2
[DB2使用小结
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服