如何解决使用 fcntl 和 C++ 锁定 Linux 文件
我在网上搜索了两个月,寻找在 C++ 程序中使用的正确文件锁定机制。
我在“C 和 fnctl”上发现了很多我可以证明可以工作的内容。但是所有真正正确的工作锁定机制,我可以证明在 Linux 中工作都仅基于文件描述符。
由于这似乎是一种非常古老的东西,并且在实际的 C++17 风格中使用文件流和 ip-streams 编写 C++ 代码而不使用该机制,所以我只想出了一些可以使用这里介绍的东西:
Not able to ofstream using __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,&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);
}
我征求您对此的意见,或者如何改进。
亚历山大·布伦斯
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。