在 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 个打开的句柄是一个更简洁的解决方案。
我需要创建一个记录器工具,根据用户提供的内容从不同的代码位置输出到相同或不同的文件。如果未打开,它应该重新创建一个用于记录的文件。但它必须附加到已打开的文件。
这种天真的方式如
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 个打开的句柄是一个更简洁的解决方案。