2

カーネル 2.6.18-238_xen_AMD64 を搭載した RHEL5.6 で berkeley db 4.3 (/usr/lib64/libdb-4.3.so) を使用して、C でプログラミングしています。

私のテスト (1,000,000 個のキーと値のペアの書き込み) では、db に対する操作の実行中に 1 つのプロセスが異常終了 (ctrl + c、kill、または assert の失敗) した場合、その db に対するその後の操作は開くときにブロックされます。Strace は、プロセスが __db.00x (例: __db.001、__db.002、__db.003) ファイルを開いた後、futex(ptr_to_something, FUTEX_WAIT, 2, NULL) 呼び出しでスタックしたことを示しています。

ロックをクリアする唯一の方法は __db.00x ファイルを削除することであり、次のテストではデータベースが破損していないことが示されました。それは私の要件を満たしていますが、この問題を解決するためのより良い(またはよりエレガントな)方法があるかどうか疑問に思っています。

ここでは、 strace stderr とデータベースを操作するためのコードをリストしました。

strace stderr の一部

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

1 に答える 1

0

__db* ファイルにはロックの名前が含まれていますが、ロック自体は含まれていません。

pthread ロックは、カーネル futex の上に実装されます。強制終了されたプロセスは、強制終了時にアクティブなロックを持っていた可能性があります。

古いロックをクリアするには、「db_recover -h $DBHOME」を実行してみてください。

「古いロック」リムーバーを自動化するために追加できるコールバックもあります。

(余談) コードはほぼ確実に、堅牢な実装のために env->open からの DB_RUNRECOVERY および DB_VERSION_MISMATCH エラー コードを処理する必要があります。DB_RECOVER を設定して再度開くと処理されます。

古い Linux では、再起動することによってのみクリアできる「古い futex」(つまり、終了したプロセスによってロックされた futex) の可能性もあります (フラグを設定する「堅牢なミューテックス」を使用する別の方法があります)。別のプロセスが以前にロックされたミューテックスのロックを解除できるようにしますが、Berkeley DB には実装されていません)。

于 2015-01-09T17:56:43.727 に答える