在Linux上植入C ++ ifstream(GCC 4.6)

时间:2011-12-29 11:58:20

标签: c++ linux g++ flock

上下文

我正在慢慢地用C ++编写一个专门的Web服务器应用程序(使用C onion http server libraryJSONCPP library进行JSON序列化,如果这很重要的话)。对于带有GCC 4.6编译器的Linux系统(我不关心非Linux系统的可移植性,也不关心4.5之前的GCC或3.0之前的Clang)。

我决定保留用户“数据库”(用户很少,可能只有一两个,因此性能不是问题,O(n)访问时间是可以接受的)JSON格式,可能是一个小的像

这样的JSON对象数组
 { "_user" : "basile" ;
   "_crypasswd" : "XYZABC123" ; 
   "_email" : "basile@starynkevitch.net" ;
   "firstname" : "Basile" ;
   "lastname" : "Starynkevitch" ;
   "privileges" : "all" ;
 }

使用约定(àla.htpasswd_crypasswd字段是用户密码的crypt(3)“加密”,由_user名称加密;

我想用Json对象描述用户的原因是我的应用程序可能在描述用户的这些Json对象中添加(而不是替换)一些JSON字段(例如上面的privileges)。我正在使用 JsonCpp 作为C ++的Json解析库。该库需要解析ifstream

所以我正在用

读取我的密码文件
extern char* iaca_passwd_path; // the path of the password file
std::ifstream jsinpass(iaca_passwd_path);
Json::Value jpassarr;
Json::Reader reader;
reader.parse(jsinpass,jpassarr,true);
jsinpass.close();
assert (jpassarr.isArray());
for (int ix=0; ix<nbu; ix++) {
  const Json::Value&jcuruser= jpassarr[ix];
  assert(jcuruser.isObject());
  if (jcuruser["_user"].compare(user) == 0) {
    std::string crypasswd = jcuruser["_crypasswd"].asString();
    if (crypasswd.compare(crypted_password(user,password)) == 0) {
         // good user
    }
  }
}

问题

显然,我想要flocklockf密码文件,以确保只有一个进程正在读取或写入它。要调用这些函数,我需要获取ifstream jsinpass的文件描述符(用Unix术语)。但谷歌给了我大部分Kreckel's fileno(我发现它完整,但有点疯狂)来获取std::ifstream的文件描述符,我不确定构造函数是否会预读它的一些。因此我的问题

如何锁定C ++ ifstream(Linux,GCC 4.6)?

(或者您是否找到了解决该问题的其他方法?)

由于

4 个答案:

答案 0 :(得分:1)

您可能希望使用单独的锁文件,而不是尝试从ifstream获取描述符。它实现起来要容易得多,你可以将ifstream包装在一个自动化的类中。

如果您想确保原子打开/锁定,您可能希望使用this SO answer中建议的方法,openflock

来构建流

答案 1 :(得分:1)

使用filestream API的一个缺点是您不能(至少不easily)访问fstream的文件描述符(例如,请参阅herehere)。这是因为没有要求fstream以FILE *或文件描述符的形式实现(尽管在实践中它总是如此)。 将管道用作C ++流也是必需的。

因此,“规范”答案(如对问题的评论中暗示的)是:

创建一个stream buffer(派生自std :: basic_streambuf),它使用Posix和C stdio I / O函数(即打开等),从而可以访问文件描述符。

使用基于stdio的流缓冲区而不是std :: streambuf创建自己的'LockableFileStream'(从std :: basic_iostream派生)。

您现在可以拥有类似fstream的类,您可以从中获取对文件描述符的访问权限,因此可以根据需要使用fcntl(或lockf)。

有一些库可以提供开箱即用的功能。

我原本以为这已经解决了,因为我们已经达到了C ++ 17但是我找不到链接所以我一定是梦想它。

答案 2 :(得分:1)

我对这个问题的解决方案是从以下答案中得出的:https://stackoverflow.com/a/19749019/5899976

我只在GCC 4.8.5上进行过测试。

#include <cstring>  // for strerror()
#include <iostream> // for std::cerr
#include <fstream>
#include <ext/stdio_filebuf.h>

extern "C" {
#include <errno.h>
#include <sys/file.h>  // for flock()
}

    // Atomically increments a persistent counter, stored in /tmp/counter.txt
int increment_counter()
{
    std::fstream file( "/tmp/counter.txt" );
    if (!file) file.open( "/tmp/counter.txt", std::fstream::out );

    int fd = static_cast< __gnu_cxx::stdio_filebuf< char > * const >( file.rdbuf() )->fd();
    if (flock( fd, LOCK_EX ))
    {
        std::cerr << "Failed to lock file: " << strerror( errno ) << "\n";
    }

    int value = 0;
    file >> value;
    file.clear();   // clear eof bit.
    file.seekp( 0 );
    file << ++value;

    return value;

    // When 'file' goes out of scope, it's closed.  Moreover, since flock() is
    //  tied to the file descriptor, it gets released when the file is closed.
}

答案 3 :(得分:0)

传统的unix-y解决方案依赖于重命名()的原子性是不可接受的吗?

我的意思是,除非您的JSON序列化格式支持就地更新(使用事务日志或其他),否则更新密码数据库需要重写整个文件,不是吗?所以你不妨将它写入临时文件,然后将其重命名为真实名称,从而确保读者阅读一致的条目? (当然,为了使其工作,每个读者必须在每次想要访问数据库条目时打开()文件,保持文件打开不会削减它)