在 C++ 中,如何检测该文件已被自己的进程打开?

In C++, how to detect that file has been already opened by own process?

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

这种天真的方式如

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 

尝试猜测将使用哪些文件并在一开始就将它们全部打开是可行的,但可能会创建很多实际未使用的文件。

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

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

我听说在 Fortran 中人们可以使用 inquire.

检查文件是否被打开

但是那个答案并没有让我对如何使用 С/C++ 实现同样的目标有任何见解。


更新

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

//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 提到的 OS 得到 table 映射 handles/descriptors,如果有人解释如何做,我会接受作为答案在程序里面。

所以这是一个简单的 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。这可能并不总是可行,因为其他进程可能由不同的用户 运行。

综上所述 - 在您的特定情况下,当您是唯一所有者时,打开和关闭文件 - 维护 table 个打开的句柄是一个更简洁的解决方案。