【问题标题】:In C++, how to detect that file has been already opened by own process?在 C++ 中,如何检测该文件是否已被自己的进程打开?
【发布时间】:2021-12-07 08:18:12
【问题描述】:

我需要创建一个记录器工具,根据用户提供的内容从不同的代码位置输出到相同或不同的文件。如果未打开,它应该重新创建一个用于记录的文件。但它必须附加到已打开的文件中。

这种幼稚的方式如

std::ofstream f1(“log”);
f1 << "1 from f1\n";
std::ofstream f2(“log”);
f2 << "1 from f2\n";
f1 << "2 from f1\n";

窃取流并重新创建文件。日志包含

1 from f2

使用追加,它将重用文件,但第二个打开窃取来自f1 的流。 日志包含

1 from f1
1 from f2 

尝试猜测将使用哪些文件并在非常乞求时将它们全部打开,但可能会创建很多实际上没有使用的文件。

在每个日志记录调用上打开以追加和关闭将是一个几乎可行的解决方案,但由于大量系统调用和每个日志记录操作的刷新,它似乎是一个缓慢的解决方案。

我要创建一个打开文件的静态表,希望std::filesystem::canonical 将适用于我的所有情况。但据我了解,这样的表应该已经存在于流程中的某个地方。

我读到过,在 Fortran 中人们可以使用 inquire 检查文件是否被打开。

Check whether file has been opened already

但这个答案并没有让我了解如何使用 С/C++ 实现同样的目标。


更新

带有“静态”打开日志表的记录器的划痕可能看起来像

//hpp
class Logger {
  static std::mutex _mutex;
  static std::unordered_map<std::string, std::ofstream> _openFiles;
  std::ostream& _appender;
  std::ostream& _createAppender(const std::filesystem::path& logPath);
public:
  Logger(const std::filesystem::path& logPath):
    _appender(_createAppender(logPath)) {
  }
  template<class... Args>
  void log(const Args&... args) const {
    std::scoped_lock<std::mutex> lock(_mutex);
    (_appender << ... << args);
  }
};
//cpp
#include "Logger.hpp"

std::mutex Logger::_mutex;
std::unordered_map<std::string, std::ofstream> Logger::_openFiles;

std::ostream& Logger::_createAppender(const std::filesystem::path& logPath) {
  if (logPath.empty()) return std::cout;
  const auto truePath{std::filesystem::weakly_canonical(logPath).string()};
  std::scoped_lock<std::mutex> lock(_mutex);
  const auto entry{_openFiles.find(truePath)};
  if (entry != _openFiles.end()) return entry->second;
  _openFiles.emplace(truePath, logPath);
  std::ostream& stream{_openFiles[truePath]};
  stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);
  return stream;
}

也许它会对某人有所帮助。

然而,我仍然想知道是否可以从@yzt 提到的操作系统中获取表映射句柄/描述符,如果有人在程序中解释如何做到这一点,我会接受作为答案。

【问题讨论】:

  • 最好的办法是使用一个记录器库来处理这个问题。或者编写自己的记录器模块或库,提供返回文件句柄的函数。
  • 没有标准的方法可以做到这一点,但您可以自己轻松实现它(如您所写)。操作系统确实有一个表,将句柄/描述符映射到每个进程的其他事物(包括文件),因此标准 C/C++ 库不需要这样的工具。
  • 我不确定“静态”表是否是您想要的(通常首选非静态表),但是,您可以通过自己跟踪所有打开的文件来做到这一点。这一点都不难,但它可能是最便携的方式。
  • 在一个地方打开文件一次。使用简单的类是个好主意。对该类使用单例模式很常见。你的班级可以像你喜欢的那样愚蠢或聪明。对于多个日志,将其外推到一个日志管理器中,该管理器通过某种标识符按需创建它们。最后,根据需要快速、轻松地开始和扩展。如果您在兔子洞中走得太远,请考虑以前有许多其他人已经在那里,并开发了您可能可以使用的出色解决方案。
  • @freakish,我需要在多线程上下文中使用该日志记录。我的意思是“线程之间共享”的静态。因为只共享一个指针就足够了,你是对的,使表格的内容非静态是合理的。

标签: c++ file


【解决方案1】:

所以这里有一个简单的 Linux 特定代码,用于检查当前进程是否打开了指定的目标文件(使用 --std=c++17 列出目录,当然可以使用任何方式)。

#include <string>
#include <iostream>
#include <filesystem>

#include <sys/types.h>
#include <unistd.h>
#include <limits.h>

bool is_open_by_me(const std::string &target)
{
    char readlinkpath[PATH_MAX];

    std::string path = "/proc/" + std::to_string(getpid()) + "/fd";
    for (const auto & entry : std::filesystem::directory_iterator(path)) {
        readlink(entry.path().c_str(), readlinkpath, sizeof(readlinkpath));
        if (target == readlinkpath)
            return true;
    }

    return false;
}

只需通过proc列出当前pid的打开句柄,然后使用readlink函数将其解析为实际文件名。

这是从我所知道的用户空间中做到这一点的最佳方式。进程本身不知道这些信息,内核知道有关进程的信息,因此进程必须使用各种技巧(在这种情况下解析 procfs)来访问它。

如果你想检查一个不同的进程是否持有一个打开的文件句柄,你必须解析所有进程的所有 procfs。这可能并不总是可行的,因为其他进程可能由不同的用户运行。

所有这些 - 在您的特定情况下,当您是一个所有者时,打开和关闭文件 - 维护一个打开的句柄表是一个更清洁的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-06
    • 2011-09-26
    相关资源
    最近更新 更多