Linux Berkeley DB:由于之前在c api调用过程中异常退出,所以一直停留在futex_等待

Linux Berkeley DB:由于之前在c api调用过程中异常退出,所以一直停留在futex_等待,linux,berkeley-db,futex,c-api,Linux,Berkeley Db,Futex,C Api,我用C语言编程,在RHEL5.6上使用berkeley db 4.3(/usr/lib64/libdb-4.3.so),内核为2.6.18-238_xen_AMD64 在我的测试(写入1000000个键/值对)中,如果一个进程在数据库上的操作正在进行时异常退出(ctrl+c、kill或assert失败),则该数据库上的后续操作将在打开时被阻止。Strace显示,在打开uuu db.00x(例如uu db.001、uu db.002、uu db.003)文件后,进程在futex(ptr_to_us

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

在我的测试(写入1000000个键/值对)中,如果一个进程在数据库上的操作正在进行时异常退出(ctrl+c、kill或assert失败),则该数据库上的后续操作将在打开时被阻止。Strace显示,在打开uuu db.00x(例如uu db.001、uu db.002、uu db.003)文件后,进程在futex(ptr_to_usomething、futex_WAIT、2、NULL)调用中卡住

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

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

一些strace stderr

操作数据库的代码


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

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

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

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

(旁白) 您的代码几乎肯定需要处理DB\u RUNRECOVERY和DB\u VERSION\u不匹配 来自env->open的错误代码用于健壮的实现。使用DB_RECOVER重新打开 我会处理的

在较旧的linux上,还可能出现“过时的futex”(即futex) 它被一个进程锁定(该进程已死亡),该进程只能通过 重新启动(还有另一种方法,使用“健壮互斥体”,设置 允许另一进程解锁先前锁定的互斥锁的标志,
但是这在Berkeley DB中没有实现。

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

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

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

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

(旁白) 您的代码几乎肯定需要处理DB\u RUNRECOVERY和DB\u VERSION\u不匹配 来自env->open的错误代码用于健壮的实现。使用DB_RECOVER重新打开 我会处理的

在较旧的linux上,还可能出现“过时的futex”(即futex) 它被一个进程锁定(该进程已死亡),该进程只能通过 重新启动(还有另一种方法,使用“健壮互斥体”,设置 允许另一进程解锁先前锁定的互斥锁的标志, 但这并没有在Berkeley DB中实现

...  
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;
}