Berkeley DB:由于先前在 c api 调用期间异常退出而卡在 futex_wait

Posted

技术标签:

【中文标题】Berkeley DB:由于先前在 c api 调用期间异常退出而卡在 futex_wait【英文标题】:Berkeley DB: stuck at futex_wait because of previous abnormal quit during c api call 【发布时间】:2012-09-06 05:03:09 【问题描述】:

我正在使用 C 语言进行编程,在内核 2.6.18-238_xen_AMD64 的 RHEL5.6 上使用 berkeley db 4.3 (/usr/lib64/libdb-4.3.so)。

在我的测试中(写入 1,000,000 个键/值对),如果一个进程异常退出(ctrl + c、kill 或 assert 失败),而对 db 的操作正在进行中,那么稍后对该 db 的操作将被阻止开幕。 Strace 显示进程在打开 __db.00x(e.g __db.001, __db.002, __db.003) 文件后卡在了 futex(ptr_to_something, FUTEX_WAIT, 2, NULL) 调用。

我知道清除锁定的唯一方法是删除 __db.00x 文件,并且以下测试表明数据库没有损坏。它符合我的要求,但我只是想知道是否有更好(或更优雅)的方法来解决这个问题。

这里我列出了一些 strace stderr 和操作数据库的代码,可能会有所帮助。

一些 strace 标准错误

...  
open("__db.001", O_RDWR)                = 3  
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0  
fstat(3, st_mode=S_IFREG|0640, st_size=24576, ...) = 0  
close(3)                                = 0  
open("__db.001", O_RDWR)                = 3  
fcntl(3, F_SETFD, FD_CLOEXEC)           = 0  
mmap(NULL, 24576, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x2afcc4149000  
close(3)                                = 0
futex(0x2afcc4149000, FUTEX_WAIT, 2, NULL **[[stuck here]]**  

操作数据库的代码

typedef DB* db_handle;
db_handle bdb_open(const char *filename, u_int32_t cache_size_mb)

    int ret;
    DB_ENV *env;
    db_handle dbp;
    u_int32_t flags = DB_CREATE | DB_THREAD | DB_INIT_LOCK | DB_INIT_MPOOL | DB_INIT_LOCK ;
    u_int32_t gb = cache_size_mb / 1024, mb = cache_size_mb % 1024;

    if (ret = db_env_create(&env, 0)) 
        fprintf(stderr, "db_env_create:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if (ret = env->set_timeout(env, 3 * 1000000, DB_SET_LOCK_TIMEOUT)) 
        fprintf(stderr, "env->set_timeout:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if (ret = env->set_lk_detect(env, DB_LOCK_DEFAULT))  /* this seems to be of no use in my case */
        fprintf(stderr, "env->set_lk_detect:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if (ret = env->set_cachesize(env, gb, mb * 1024 * 1024, 0)) 
        fprintf(stderr, "env->set_cachesize:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if ((ret = env->open(env, NULL, flags, 0)) != 0) 
        fprintf(stderr, "db_env_open:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if (ret = db_create(&dbp, env, 0)) 
        fprintf(stderr, "db_create:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    if (ret = dbp->open(dbp, NULL, filename, NULL, DB_BTREE, flags, 0664)) 
        fprintf(stderr, "dbp->open:%d, %s\n", ret, db_strerror(ret));
        exit(EXIT_FAILURE);
    

    return dbp;


int bdb_put(db_handle db, void* key, u_int32_t keylen, void* val, u_int32_t vallen)

    DBT dkey, dval;
    bzero(&dkey, sizeof(dkey));
    bzero(&dval, sizeof(dval));
    dkey.data = key, dkey.size = keylen;
    dval.data = val, dval.size = vallen;
    return db->put(db, NULL, &dkey, &dval, 0);


int bdb_get(db_handle db, void* key, const u_int32_t keylen,
                          void* buf, u_int32_t buflen, u_int32_t* nwrite)

    DBT dkey, dval;
    bzero(&dkey, sizeof(dkey));
    bzero(&dval, sizeof(dval));
    dkey.data = key, dkey.size = keylen;
    dval.data = buf, dval.ulen = buflen, dval.flags = DB_DBT_USERMEM;
    int ret = db->get(db, NULL, &dkey, &dval, 0);
    if (ret == 0 && nwrite != NULL)
        *nwrite = dval.size;
    return ret;

【问题讨论】:

【参考方案1】:

__db* 文件包含锁的名称,但不包含锁本身。

pthread 锁是在内核 futex 之上实现的。过程 被杀死的对象在被杀死时可能具有活动锁。

尝试运行“db_recover -h $DBHOME”来清除过时的锁。

还可以添加一个回调来自动执行“过时锁定” 卸妆。

(旁白) 您的代码几乎肯定需要处理 DB_RUNRECOVERY 和 DB_VERSION_MISMATCH 来自 env->open 的错误代码以实现健壮的实现。使用 DB_RECOVER 重新打开 set 将处理。

在较旧的 linux 上,也有可能出现“陈旧的 futexes”(即 futex 被死掉的进程锁定)只能被清除 重新启动(还有另一种方法,使用“强大的互斥锁”,设置 允许另一个进程解锁先前锁定的互斥锁的标志, 但这在 Berkeley DB 中没有实现)。

【讨论】:

以上是关于Berkeley DB:由于先前在 c api 调用期间异常退出而卡在 futex_wait的主要内容,如果未能解决你的问题,请参考以下文章

用 Java 编写旧版本的 Berkeley DB

检查 Berkeley DB C++ API 中是不是存在密钥 [关闭]

我可以使用 oracle berkeley db java edition 的 c 实现(python bsddb)创建的 bdb(berkeley db)文件吗?

无法从 C 中的 Berkeley DB 检索值

在 Berkeley DB Core 和 Berkeley DB JE 之间进行选择

Oracle - Berkeley DB XML Java API - XML 查询以获取多个级别的属性值