c++ - 使用 fcntl 和 C++ 锁定 Linux 文件
问题描述
我在网上搜索了两个月,以寻找在 C++ 程序中使用的适当文件锁定机制。
我在“C and fnctl”上发现了很多我可以证明可以工作的东西。但是,我可以证明在 Linux 中工作的所有真正正确的工作锁定机制都仅基于文件描述符。
由于这似乎是一种非常过时的东西,并且在实际的 C++17 风格中使用文件和 ip 流编写 C++ 代码而不使用该机制,所以我只想出了一些可以使用这里介绍的东西的东西:
无法使用 __gnu_cxx::stdio_filebuf 进行流式传输
我的问题是,这真的是唯一有效的机制吗?连接两个世界?
我查看了所有这些书籍以找到有关 fcntl 和 C++ 的任何信息,但没有成功:
[Der C++ Programmierer Cxx20]
( https://www.hanser-elibrary.com/doi/book/10.3139/9783446465510 )[C++ 编程语言]
( https://www.stroustrup.com/C++.html )[C++ Das Umfassende Handbuch]
( https://www.rheinwerk-verlag.de/c-plusplus-das-umfassende-handbuch/ )[现代 C++ 编程食谱第二版]
( https://www.packtpub.com/product/modern-c-programming-cookbook-second-edition/9781800208988 )
我在这里向 C++ 大师提出的问题是,如果我遗漏了什么,或者下面的代码是,今天,2021 年开始,我们能做的最好的事情。
代码证明的简短说明:
我们有一个 C++ 代码,它将用户名及其 LSF 进程添加到一个 conf 文件中,SSH 服务器读取该文件以允许用户访问该机器。由于此代码的两个或多个运行进程可能导致同时尝试从该文件添加或删除用户,因此我们必须证明适当的文件锁定可以防止这种情况发生。不使用额外的“访问”文件,这也可能是一个解决方案。
这是我测试的一些示例代码:
#include <iostream>
#include <string>
#include <thread>
#include <chrono>
#include <fcntl.h>
#include <unistd.h>
#include <ext/stdio_filebuf.h>
using namespace std::this_thread; // for sleep_for
int main( ) {
// set unbuffered concole output
std::cout.setf(std::ios::unitbuf);
const char* filename {"testfile.txt"};
// get input from input_from_user
std::string input_from_user_string;
std::cout << "Please give input to change in the file: ";
std::cin >> input_from_user_string;
int add_1_del_2 = 0;
std::cout << "Please give 1 if you want to add to the file or 2 if you want to delete from file: ";
std::cin >> add_1_del_2;
int input_from_user_time;
std::cout << "Please give seconds to wait: ";
std::cin >> input_from_user_time;
// opening file
std::cout << "Opening File" << std::endl;
mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; //664
int fd;
fd = open(filename, O_RDWR | O_CREAT, mode);
// printing out information about file descriptor
std::cout << " Dexc:" << fd << std::endl;
// generating C++-streams on filedescriptor
__gnu_cxx::stdio_filebuf<char> sourcebufin(fd, std::ios::in);
__gnu_cxx::stdio_filebuf<char> sourcebufout(fd, std::ios::out);
std::istream myfilein(&sourcebufin);
std::ostream myfileout(&sourcebufout);
// -----------
// check for file Locking or exit
// -----------
// creating structure for file locking
struct flock fl;
fl.l_type = F_RDLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
// set file locking for read
fl.l_type = F_RDLCK;
std::cout << "Checking for Lock on file" << std::endl;
// check for file locking on file for read only once
(void) fcntl(fd, F_GETLK, &fl);
if (fl.l_type != F_UNLCK) {
std::cout << "File is locked for reading by process "
<< fl.l_pid
<< ", in status"
<< ((fl.l_type == F_WRLCK) ? 'W' : 'R')
<< ", start="
<< fl.l_start
<< ", end="
<< fl.l_len
<< std::endl;
}
else {
(void) printf("File is unlocked for reading\n");
}
// set file locking for write
fl.l_type = F_WRLCK;
// check for file locking on file for write in a loop
for (int i = 1; i < 11; i++) {
//printf("Checking for lock %d of 10 times...\n", i);
std::cout << "Checking for lock "
<< i
<< " of 10 times..."
<< std::endl;
(void) fcntl(fd, F_GETLK, &fl);
if (fl.l_type != F_UNLCK) {
//(void) printf("File is locked by process %d, in status %c, start=%8ld, end=%8ld\n", fl.l_pid,
// , fl.l_start, fl.l_len);
std::cout << "File is locked by process "
<< fl.l_pid
<< ", in status"
<< ((fl.l_type == F_WRLCK) ? 'W' : 'R')
<< ", start="
<< fl.l_start
<< ", end="
<< fl.l_len
<< std::endl;
sleep(10);
}
else {
(void) printf("File is unlocked\n");
break;
}
}
// -----------
// apply lock for write on file
// -----------
// locking file
std::cout << "Locking file for write" << std::endl;
// set file locking for write again, as checking on lock resets it
fl.l_type = F_WRLCK;
if (fcntl(fd, F_SETLKW, &fl) == -1) {
perror("fcntl");
abort();
}
// -----------
// wait some time
// -----------
std::cout << "Now waiting for " << input_from_user_time << " seconds, keeping the file locked..." << std::endl;
std::this_thread::sleep_for(std::chrono::seconds(input_from_user_time));
// -----------
// read from file
// -----------
std::cout << "Reading from file... " << std::endl;
myfilein.seekg(0, std::ios::end);
size_t size_before = myfilein.tellg();
myfilein.seekg(0);
std::string filecontent{""};
filecontent.reserve(size_before);
std::cout << "Length of file is: " << size_before << std::endl;
// read full content of file in string "filecontent"
filecontent.assign((std::istreambuf_iterator<char>(myfilein)),
std::istreambuf_iterator<char>());
// -----------
// print output about read data
// -----------
std::cout << "Length of filecontent-string: " << filecontent.size() << std::endl;
std::cout << "Content of File begin" << std::endl;
std::cout << "----------" << std::endl;
std::cout << filecontent << std::endl;
std::cout << "----------" << std::endl;
// -----------
// Apply changes on read in data depending on given input
// -----------
if (add_1_del_2 == 2) {
std::cout << "Runmode: Del" << std::endl;
std::string string_to_delete = input_from_user_string+"\n";
std::string::size_type pos_of_found_substring = filecontent.find(string_to_delete);
if (pos_of_found_substring != std::string::npos) {
filecontent.erase(pos_of_found_substring, string_to_delete.length());
}
else {
}
}
if (add_1_del_2 == 1) {
std::cout << "Runmode: Append" << std::endl;
filecontent.append(input_from_user_string);
}
std::cout << "Content of String after change" << std::endl;
std::cout << "----------" << std::endl;
std::cout << filecontent << std::endl;
std::cout << "----------" << std::endl;
// -----------
// write out to file, truncate before to length of new string
// -----------
std::cout << "Now starting the write out..." << std::endl;
myfilein.seekg(0);
ftruncate(fd,filecontent.length());
myfileout.seekp(0);
myfileout << filecontent;
myfileout.flush();
myfileout.clear();
// -----------
// read from file for a second time and printout content
// -----------
std::cout << "Reading from file again... " << std::endl;
myfilein.seekg(0, std::ios::end);
size_t size_after = myfilein.tellg();
myfilein.seekg(0);
std::string filecontent_after{""};
filecontent_after.reserve(size_after);
std::cout << "Length of file is now: " << size_after << std::endl;
// read full content of file in string "filecontent"
filecontent_after.assign((std::istreambuf_iterator<char>(myfilein)),
std::istreambuf_iterator<char>());
std::cout << "Length of filecontent_after-string: " << filecontent_after.size() << std::endl;
std::cout << "Content of File end" << std::endl;
std::cout << "----------" << std::endl;
std::cout << filecontent_after << std::endl;
std::cout << "----------" << std::endl;
// -----------
// unlocking file and close file
// -----------
printf("Unlocking...\n");
fl.l_type = F_UNLCK;
if (fcntl(fd, F_SETLK, &fl) == -1) {
perror("fcntl");
abort();
}
close(fd);
// -----------
// done
// -----------
std::cout << "done" << std::endl;
exit(0);
}
我征求您对此的意见或如何改进。
亚历山大·布伦斯
解决方案
推荐阅读
- python - 如何从没有“img”标签的网站下载图片?
- postgresql - 如何使用 Pglogical 设置从 AWS RDS PostgreSQL 到 Kafka 的 CDC 复制?
- c++ - 定位 C++ 内存泄漏
- c - 将 N 个原始二进制字节准确写入 C 中的文件
- c# - 使用 Asp.Net Core 连接到本地 SQL 服务器数据库时出现问题
- javascript - 第二大值显示为“未定义”
- javascript - 为什么“window.$ =”分配对于 Vue.js 导入是可选的,但对于 JavaScript 文件中的 jQuery 是强制性的?
- php - Laravel 验证器是否检查文件或扩展名的 MIME 类型?
- javascript - 如何使 classList.toggle 遵循类的 CSS
- python - 如何运行包含打印语句而不打印的python代码?静音模式