0

我正在使用 tdb 来尝试熟悉 Linux 上 C 语言中的数据库管理。根据tdb 的描述

tdb 是一个简单的数据库。

在概念上,它与 GDBM 和 BSD 的 DB 非常相似,只是它允许多个同时写入者并在内部使用锁定来防止写入者相互践踏。tdb 也非常小。界面

该接口与 gdbm 非常相似,除了以下几点:

  • 不同的开放界面。tdb_open 调用更类似于传统的 open()
  • 没有 tdbm_reorganise() 函数
  • 没有 tdbm_sync() 函数。无论如何,库中没有缓存任何操作
  • 增加交易支持

使用 tdb 的一般规则是调用者释放所有返回的 TDB_DATA 结构。只需调用 free(p.dptr) 即可释放名为 p 的 TDB_DATA 返回值。这与 gdbm 相同。

现在我想做一个小测试程序来测试我的数据库的多个写连接。但它失败了。

#include <tdb.h>

int main(int argc, char** argv) {
   struct tdb_context * tdb = tdb_open("test.tdb", 0, 0,
           O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);
   if(!tdb){
       printf("%s\n", tdb_errorstr(tdb));
   } else {
       printf("Database successfully opened!\n");
   }
   
   struct tdb_context * anothertdb = tdb_open("test.tdb", 0, 0,
           O_RDWR| O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP);  //why does this fail?
   if(!anothertdb){
       printf("%s\n", tdb_errorstr(anothertdb));
   } else {
       printf("Another reader successfully opened!\n");
   }
   
   if(tdb_close(anothertdb)){
       printf("Error while closing database!\n");
   } else {
       printf("closing anothertdb-connection\n");
   }
   
   if(tdb_close(tdb)){
       printf("Error while closing database!\n");
   } else {
       printf("closing tdb-connection\n");
   }
return 0;
}

输出是:

Database successfully opened!

RUN FINISHED; Segmentation fault; core dumped; real time: 240ms; user: 0ms; system: 20ms

如果我只打开一个数据库的连接,则没有问题。如何在数据库上打开多个阅读器?

4

1 回答 1

1

您遇到的第一个问题是调用tdb_errorstringaftertdb_open失败:

if(!anothertdb){
       printf("%s\n", tdb_errorstr(anothertdb));
}

这可能不起作用,但有充分的理由怀疑它不起作用,因为anothertdb最肯定是NULLwhentdb_errorstr被调用,并且假设tdb_errorstr' 参数必须是指向 a 的有效指针是合理的struct tdb_context。事实上,这是真的;实际上,要做的第一件事tdb_errorstring是尝试取消引用其参数以提取最后一个错误代码。

简而言之,没有办法从tdb_open失败的结果中提取自定义错误字符串。你所能做的就是你会做些什么来获得NULL回报,例如fopen():打电话perror()希望这errno将被设置为有意义的事情。如果这样做,您将看到一条错误消息而不是崩溃:

$ ./tdb_test 
Database successfully opened!
open 2: Device or resource busy

该库似乎有一种方法可以将tdb_open错误发送到配置的日志记录系统,但默认配置的日志记录系统只是将错误调用扔掉而不做任何事情。我没有阅读文档以查看是否有关于如何配置记录器的描述;你可能想调查一下。

顺便说一句,我并不是在为这个 API 设计辩护。只是报告它。

好的,现在开始你的实际问题:如何打开到 TDB 数据库的多个连接?答案是,你必须从多个流程中做到这一点。单个进程可以打开任意数量的 TDB,但它只能(一次)打开每个 TDB。根据源代码中的评论,这是fcntl()锁定工作方式的结果,这对我来说似乎是一个合理的解释。

因此,如果您想尝试多读取器、多写入器的场景,您将不得不运行多个进程,或者从父进程派生多个子进程。线程将不起作用,因为同一进程中的两个线程也无法打开同一个 TDB。每个进程打开一个。

此外,如果您选择 fork 多个子进程,请记住您tdb_open只需要在 fork 后调用,因为如果您在父进程中打开 TDB,它仍然会在子进程中打开。

最后一点,请养成将警告和错误消息写入 的习惯stderr,这些消息通常不会被缓冲,这样当您输出错误消息时,您就有更好的机会看到错误消息,而不是稍后的某个时刻stdio 库选择刷新 stdout 输出缓冲区。这实际上不是问题,但它可能是,而且你最好使用它来stderr达到这个目的(这是它的预期目的,因此得名)。

于 2021-07-28T01:58:08.107 回答