【问题标题】:Same program/process acquiring lock every time when using LOCK_NB with LOCK_EX flag每次使用带有 LOCK_EX 标志的 LOCK_NB 时,相同的程序/进程都获取锁
【发布时间】:2020-10-12 11:48:41
【问题描述】:

我有一个要求,两个并行运行的独立进程/程序(一个用 Python 编写,一个用 C++ 编写)需要获得独占访问权限,修改一个硬件相关值。

我正在尝试使用 flock 在它们之间实现同步。

相同的代码如下,

Python 代码

#!/usr/bin/python

import fcntl
import time
import datetime
import os

SLICE_SLEEP = 5
LOCK_HOLD_TIME = 20
TOTAL_WAIT_TIME = 300

class LockUtil(object):
   FILE_PATH = "/tmp/sync.lock"
   fd = -1

   @staticmethod
   def acquireLock(totalWaitTime=TOTAL_WAIT_TIME):
      try:
         LockUtil.fd = os.open(LockUtil.FILE_PATH,os.O_WRONLY|os.O_CREAT)
         print('Trying to acquire lock')
         retryTimes = (totalWaitTime/SLICE_SLEEP)
         currentCounter = 0
         while currentCounter < retryTimes:
            try:
                fcntl.flock(LockUtil.fd,fcntl.LOCK_EX|fcntl.LOCK_NB)
                print('Lock acquired successfully')
                return
            except IOError:
                print('Failed to acquire the lock, sleeping for {} secs'.format(SLICE_SLEEP))
                time.sleep(SLICE_SLEEP)
                currentCounter += 1
         print('Tried {} times, now returning'.format(retryTimes))
      except IOError:
         print('Can not access file at path: {}'.format(FILE_PATH))

   @staticmethod
   def releaseLock():
      fcntl.flock(LockUtil.fd,fcntl.LOCK_UN)
      print('Lock released successfully')

class LockHelper(object):
   def __init__(self):
      LockUtil.acquireLock()
   def __del__(self):
      LockUtil.releaseLock()

def createObjAndSleep():
   lock = LockHelper()
   time.sleep(LOCK_HOLD_TIME)

def main():
   while True:
      createObjAndSleep()

if __name__ == '__main__':
   main()

C++ 代码

#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/file.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <ctime>
#include <memory>

int SLICE_SLEEP = 6;
int LOCK_HOLD_TIME = 20;
int TOTAL_WAIT_TIME = 300;
int SUCCESS = 0;

class LockUtil {
   public:
      static std::string path;
      static int fd;
      static int acquireLock(int totalWaitTime=TOTAL_WAIT_TIME);
      static int releaseLock();
};

std::string LockUtil::path = "/tmp/sync.lock";
int LockUtil::fd = -1;

int LockUtil::acquireLock(int totalWaitTime) {
   fd = open(path.c_str(), O_WRONLY|O_CREAT, 0666);
   if(fd != -1)
   {
      auto retryTimes = (totalWaitTime/SLICE_SLEEP);
      auto currentCounter = 0;
      while(currentCounter < retryTimes)
      {
        std::cout << "Trying to acquire lock" << std::endl;
        auto lockStatus = flock(fd,LOCK_EX|LOCK_NB);
        if(lockStatus == SUCCESS)
        {
            std::cout << "Lock acquired successfully" << std::endl;
            return 0;
        } else {
            std::cout << "Failed to acquire the lock, sleeping for " << SLICE_SLEEP << " secs" << std::endl;
            sleep(SLICE_SLEEP);
            currentCounter += 1;
        }
      }
   } else {
      std::cout << "Unable to open the file!" << std::endl;
      std::cout << strerror(errno) << std::endl;
      return -1;
   }
}

int LockUtil::releaseLock() {
   if(fd != -1)
   {
      flock(fd,LOCK_UN);
      std::cout << "Lock released successfully" <<  std::endl;
      return 0;
   } else {
      return -1;
   }
}

class LockHelper {
   public:
      LockHelper() {
         LockUtil::acquireLock();
      }
      ~LockHelper() {
         LockUtil::releaseLock();
      }
};

void createObjAndSleep()
{
   std::unique_ptr<LockHelper> lockObj(new LockHelper());
   sleep(LOCK_HOLD_TIME);
}

int main(void) {
   while (true) {
      createObjAndSleep();
   }
}

但是,当我并行运行这两个程序时,观察到首先获得文件锁的进程一直在获得它,而另一个进程则处于饥饿状态。

但是,如果我将两个程序中的标志更改为仅使用 LOCK_EX 并删除 LOCK_NB,则锁将以循环方式在进程之间共享。

我想了解使用 LOCK_NB 标志时程序中的错误是什么。

操作系统

uname -a

Linux 0000000000002203 4.4.43-hypriotos-v7+ #1 SMP PREEMPT Thu Jan 19 20:54:06 UTC 2017 armv7l GNU/Linux

Python 版本 - 2.7

C++ 版本 - C++11

【问题讨论】:

    标签: python c++ linux flock


    【解决方案1】:

    我不认为这本身是一个错误,但可能是一个意想不到的后果。当您使用阻塞flock 时,您的进程将被放入内部 Linux 内核队列,一旦锁被释放,该队列预计会唤醒进程。

    虽然 Linux 群不能保证公平调度,但看起来事件序列或多或少地按公平调度分配锁定。

    另一方面,使用非阻塞锁,您的进程会不断尝试锁定它。因此,没有锁队列,而是有进程不断地实时竞争锁。为了实现这种锁,当锁可用时,进程需要在 cpu 上运行,而调度程序似乎只是没有给进程在此时存在的机会。

    调度策略非常复杂,所以我不会推测究竟是什么导致了这种调度器行为。

    最后但同样重要的是,当谈到非阻塞锁时,您的最终目标是什么?你为什么想要它们?

    【讨论】:

    • 感谢 SergeyA 的回答。使用非阻塞锁的目的是设置超时或最大重试次数,以便进程可以在一段时间后或重试次数后停止请求锁定。有没有其他方法可以在不使用非阻塞调用的情况下完成上述操作?
    • @PuneetUgru 一种方法是使用警报信号-即在阻塞模式下调用flock之前为所需的超时设置警报(并为sigalarm安装信号处理程序),然后分析返回代码。如果调用因超时而中断,返回码为EINTR
    猜你喜欢
    • 1970-01-01
    • 2011-12-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-08
    • 2018-03-13
    • 1970-01-01
    • 2021-07-24
    • 1970-01-01
    相关资源
    最近更新 更多