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

Ask*_*nto 7 c++ file

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

\n

这种天真的方式例如

\n
std::ofstream f1(\xe2\x80\x9clog\xe2\x80\x9d);\nf1 << "1 from f1\\n";\nstd::ofstream f2(\xe2\x80\x9clog\xe2\x80\x9d);\nf2 << "1 from f2\\n";\nf1 << "2 from f1\\n";\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n
1 from f2\n
Run Code Online (Sandbox Code Playgroud)\n

使用追加,它将重用文件,但第二个打开会从f1.\nLog中窃取流

\n
1 from f1\n1 from f2 \n
Run Code Online (Sandbox Code Playgroud)\n

尝试猜测将使用哪些文件并在最开始时将它们全部打开会起作用,但可能会创建许多实际未使用的文件。

\n

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

\n

我\xe2\x80\x99m 将创建一个打开文件的静态表,希望std::filesystem::canonical\n 能在我的所有情况下工作。但据我了解,这样的表应该已经存在于过程中的某个地方。

\n

我读过,在 Fortran 中,人们可以检查文件是否使用inquire.

\n

检查文件是否已经打开

\n

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

\n
\n

更新

\n

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

\n
//hpp\nclass Logger {\n  static std::mutex _mutex;\n  static std::unordered_map<std::string, std::ofstream> _openFiles;\n  std::ostream& _appender;\n  std::ostream& _createAppender(const std::filesystem::path& logPath);\npublic:\n  Logger(const std::filesystem::path& logPath):\n    _appender(_createAppender(logPath)) {\n  }\n  template<class... Args>\n  void log(const Args&... args) const {\n    std::scoped_lock<std::mutex> lock(_mutex);\n    (_appender << ... << args);\n  }\n};\n//cpp\n#include "Logger.hpp"\n\nstd::mutex Logger::_mutex;\nstd::unordered_map<std::string, std::ofstream> Logger::_openFiles;\n\nstd::ostream& Logger::_createAppender(const std::filesystem::path& logPath) {\n  if (logPath.empty()) return std::cout;\n  const auto truePath{std::filesystem::weakly_canonical(logPath).string()};\n  std::scoped_lock<std::mutex> lock(_mutex);\n  const auto entry{_openFiles.find(truePath)};\n  if (entry != _openFiles.end()) return entry->second;\n  _openFiles.emplace(truePath, logPath);\n  std::ostream& stream{_openFiles[truePath]};\n  stream.exceptions(std::ifstream::failbit|std::ifstream::badbit);\n  return stream;\n}\n
Run Code Online (Sandbox Code Playgroud)\n

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

\n

然而,我仍然想知道是否有可能从 @yzt 提到的操作系统获取表映射句柄/描述符,并且如果有人解释如何在程序内执行此操作,我将接受作为答案。

\n

Yur*_*man 3

因此,这里是一个简单的 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;
}
Run Code Online (Sandbox Code Playgroud)

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

这是我所知道的从用户空间执行此操作的最佳方法。进程本身不知道此信息,内核知道有关该进程的信息,因此进程必须使用各种技巧(在本例中解析 procfs)来访问它。

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

综上所述 - 在您的具体情况下,当您是唯一所有者时,打开和关闭文件 - 维护一张打开句柄表是一种更干净的解决方案。