【问题标题】:C++ Ubuntu not releasing lock on lock file when terminatedC++ Ubuntu在终止时不释放锁定文件的锁定
【发布时间】:2017-11-03 08:21:28
【问题描述】:

我有一个 C++ 脚本,它检查是否必须执行任何操作,如果是,它会启动正确的处理器 C++ 脚本。但是,由于它每 x 分钟运行一次,它还会使用锁定文件检查处理器是否仍在运行。

我使用以下函数来获取锁:

int LockFile(string FileNameToLock) {
    FileNameToLock += ".lock";
    int fd = open(FileNameToLock.c_str(), O_RDWR | O_CREAT, 0666);
    int rc = flock(fd, LOCK_EX | LOCK_NB);
    if (rc || rc == -1) {
        cout << errno << endl;
        cout << strerror(errno) << endl;
        return -1;
        }
    return fd;
    }

正在执行的代码:

[...]
if (LockFile(ExecuteFileName, Extra) == -1) {
    cout << "Already running!" << endl; //MAIN IS ALREADY RUNNING
    //RETURNS ME Resource temporarily unavailable when processor is running from an earlier run
    exit(EXIT_SUCCESS);
    }
if (StartProcessor) { //PSEUDO
    int LockFileProcessor = LockFile("Processor");
    if (LockFileProcessor != -1) {
        string Command = "nohup setsid ./Processor"; //NOHUP CREATES ZOMBIE?
        Command += IntToString(result->getInt("Klantnummer"));
        Command += " > /dev/null 2>&1 &"; //DISCARD OUTPUT
        system(Command.c_str());
        //STARTS PROCESSOR (AS ZOMBIE?)
        }
    }

第一次运行运行良好,但是当主脚本再次运行时,锁定文件返回 - 1,这意味着发生了错误(仅当处理器仍在运行时)。 errno 为 11,导致错误消息:Resource temporarily unavailable请注意,这只发生在(僵尸)处理器仍在运行时。 (但是,主脚本已经终止,应该关闭文件句柄?)

由于某种原因,僵尸保持打开主脚本的锁文件的文件句柄???

我不知道在哪里寻找这个问题。

已解决: 看我的回答

【问题讨论】:

  • 您可以检查您的进程是否仍在运行或是否是僵尸进程并停止它们。我没有经验如何通过 C++ 程序以优雅的方式完成此操作,因为我主要是在命令行上执行此操作。
  • 我不想杀僵尸。他们必须继续。但是,主处理器可能需要在处理器 A 仍在运行时启动处理器 B,这不起作用,因为我无法获得主处理器的锁。我猜是因为处理器 A 在执行期间将文件句柄保持在主打开的锁文件中,因为当我终止处理器 A 时,我可以获得主锁上的新锁
  • 我认为你混淆了僵尸进程和孤儿进程,见en.wikipedia.org/wiki/Zombie_processen.wikipedia.org/wiki/Orphan_process
  • 很棒的评论,这确实是对我想要创建的过程的更好解释!
  • 顺便说一下,linux.die.net/man/2/flock 有一句有趣的话:“flock() 创建的锁通过 execve(2) 保存。”。老实说,我不知道它的确切含义以及这是否适用于此。

标签: c++ ubuntu system errno flock


【解决方案1】:

不,11 是EAGAIN/EWOULDBLOCK,这仅表示您无法获取锁,因为资源已被锁定(请参阅the documentation)。由于LOCK_NB 标志,您收到了该错误(而不是阻止行为)。

编辑: 经过一番讨论,问题似乎是由于子处理时保留了flock() 锁。为了避免这个问题,我建议不要在整个生命周期内使用flock(),而是使用 touch-and-delete-at-exit 策略:

  1. 如果file.lock存在则退出
  2. 否则创建file.lock并开始处理
  3. 在退出时删除file.lock

当然这里有一个竞争条件。为了确保安全,您需要另一个带有flock 的文件:

  1. flock(common.flock)
  2. 如果file.lock存在则退出
  3. 否则创建file.lock
  4. 解锁flock(common.flock)
  5. 开始处理
  6. 在退出时删除file.lock

但这仅在您希望同时呼叫main 时才重要。如果你不这样做(你说 cron 每 10 分钟启动一次进程,这里没有比赛),那么坚持第一个版本。

旁注:这是这种(非同步)文件锁的简单实现:

#include <string>
#include <fstream>
#include <stdexcept>
#include <cstdio>

// for sleep only
#include <chrono>
#include <thread>

class FileLock {
    public:
        FileLock(const std::string& path) : path { path } {
            if (std::ifstream{ path }) {
                // You may want to use a custom exception here
                throw std::runtime_error("Already locked.");
            }
            std::ofstream file{ path };
        };

        ~FileLock() {
            std::remove(path.c_str());
        };

    private:
        std::string path;
};

int main() {
    // This will throw std::runtime_error if test.xxx exists
    FileLock fl { "./test.xxx" };
    std::this_thread::sleep_for(std::chrono::seconds { 5 });
    // RAII: no need to delete anything here
    return 0;
};

需要 C++11。请注意,此实现不是竞争条件安全的,即您通常需要 flock() 构造函数,但在这种情况下它可能没问题(即当您不并行启动 main 时)。

【讨论】:

  • @TVAvanHesteren 不,我建议一切都很好。没有僵尸。还有另一个进程保持锁定(第一个触发)。或者你是说没有其他进程在运行但你不能锁定它?
  • @TVAvanHesteren 我想我需要更多解释。进程 A 锁定一个文件并进行一些处理(锁定在 A 的整个生命周期内保持)。进程 B 尝试锁定文件,但失败(因为 A 拥有锁定)并退出。这就是您的代码应该(而且看起来确实如此)的工作方式。你期望什么行为?
  • @TVAvanHesteren 我想我现在明白了。文件描述符(以及 flock 调用)在 execvefork (可能还有 system 系统调用)之间保留。尝试在 system 系统调用之前手动解锁 flock
  • @TVAvanHesteren 足够公平,所以另一个想法是不使用flock,而是在退出时触摸并删除(并检查是否存在)一个文件,并且只使用flock进行触摸-如果不存在同步。您可能希望将 pid 存储在该文件中(以了解在发生故障时要查找的进程)。我认为这是通常的策略。当然,你需要将这个锁包裹在 RAII 中。
  • @TVAvanHesteren 您可以通过信号处理程序处理一些异常终止。但是,您永远不会处理 kill -9 案例。但是,如果您的解决方案适合您,那就太好了。
【解决方案2】:

我解决了这个问题,因为 system 和 fork 命令似乎传递了羊群,通过将命令保存在向量中执行。在循环命令向量之前解锁锁定文件并执行每个命令。这在未锁定的情况下留下了非常小的运行间隙,但现在它似乎工作得很好。这也支持不优雅的终止。

[...]
if (LockFile(ExecuteFileName, Extra) == -1) {
    cout << "Already running!" << endl; //MAIN IS ALREADY RUNNING
    //RETURNS ME Resource temporarily unavailable when processor is running from an earlier run
    exit(EXIT_SUCCESS);
    }
vector<string> Commands;
if (StartProcessor) { //PSEUDO
    int LockFileProcessor = LockFile("Processor");
    if (LockFileProcessor != -1) {
        string Command = "nohup setsid ./Processor"; //NOHUP CREATES ZOMBIE
        Command += IntToString(result->getInt("Klantnummer"));
        Command += " > /dev/null 2>&1 &"; //DISCARD OUTPUT
        Commands.push_back(Command);
        }
    }
//UNLOCK MAIN
if (UnlockFile(LockFileMain)) {
    for(auto const& Command: Commands) {
        system(Command.c_str());
        }
    }

【讨论】:

    猜你喜欢
    • 2021-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-23
    • 1970-01-01
    • 2018-12-19
    相关资源
    最近更新 更多