简体   繁体   English

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

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

I need to create a logger facility that outputs from different places of code to the same or different files depending on what the user provides.我需要创建一个记录器工具,根据用户提供的内容从不同的代码位置输出到相同或不同的文件。 It should recreate a file for logging if it is not opened.如果未打开,它应该重新创建一个用于记录的文件。 But it must append to an already opened file.但它必须 append 到一个已经打开的文件。

This naive way such as这种幼稚的方式如

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

steals stream and recreates the file.窃取 stream 并重新创建文件。 Log contains日志包含

1 from f2

With append, it will reuse file, but the second open steals stream from f1 .使用 append ,它将重用文件,但第二次打开从f1窃取 stream 。 Log contains日志包含

1 from f1
1 from f2 

Try to guess which files will be used and open them all in the very begging will work but may create a lot of files that are not actually used.尝试猜测将使用哪些文件并在非常乞求时将它们全部打开,但可能会创建很多实际上没有使用的文件。

Open for append and closing on each logging call would be an almost working solution, but it seems to be a slow solution due to a lot of system calls and flushing on each logging action.打开 append 并关闭每个日志记录调用将是一个几乎可行的解决方案,但由于大量系统调用和每个日志记录操作的刷新,它似乎是一个缓慢的解决方案。

I'm going to create a static table of opened files, hoping that std::filesystem::canonical will work in all of my cases.我将创建一个 static 已打开文件表,希望std::filesystem::canonical在我的所有情况下都能正常工作。 But as far as I understand such a table should already exist somewhere in the process.但据我了解,这样的表应该已经存在于过程中的某个地方。

I've read that in Fortran people can check if a file was opened using inquire .我在 Fortran 中读到过,人们可以检查文件是否已使用inquire打开。

Check whether file has been opened already 检查文件是否已经打开

But that answer did not give me any insight on how to achieve the same with С/C++.但是这个答案并没有让我了解如何使用С/C++ 实现相同的目标。


Update更新

A scratch of the logger with a "static" table of open logs can look like带有打开日志的“静态”表的记录器的划痕看起来像

//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;
}

maybe it will help someone.也许它会帮助某人。

Yet, I still wonder if it is possible to get table mapping handles/descriptors from OS mentioned by @yzt, and will accept as an answer if someone explains how to do that inside the program.然而,我仍然想知道是否可以从@yzt 提到的操作系统中获取表映射句柄/描述符,如果有人在程序中解释如何做到这一点,我会接受作为答案。

So here is a simple Linux specific code that checks whether a specified target file is open by the current process (using --std=c++17 for dir listing but any way can be used of course).所以这里有一个简单的 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;
}

Simply list the current pid's open handles via proc, then use readlink function to resolve it to the actual file name.只需通过 proc 列出当前 pid 的打开句柄,然后使用 readlink function 将其解析为实际文件名。

That is the best way to do it from the userspace I know.这是从我知道的用户空间做到这一点的最佳方式。 This information is not known by the process itself, it is known by the kernel about the process, hence the process has to use various tricks, in this case parsing procfs, to access it.进程本身不知道此信息,kernel 知道有关该进程的信息,因此该进程必须使用各种技巧(在这种情况下解析 procfs)来访问它。

If you want to check whether a different process hold an open handle to a file, you will have to parse all the procfs for all processes.如果要检查其他进程是否持有文件的打开句柄,则必须解析所有进程的所有 procfs。 That may not be always possible since other processes may be run by different users.这可能并不总是可能的,因为其他进程可能由不同的用户运行。

All that said - in your specific case, when you are the one owner, opening and closing the files - maintaining a table of open handles is a much cleaner solution.所有这一切 - 在您的特定情况下,当您是一个所有者时,打开和关闭文件 - 维护一个打开的句柄表是一个更清洁的解决方案。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM