最后我们来看一下事务共享区域。

首先仍然是每个进程都有的一个数据结构__db_txnmgr{}:

/*
* The transaction manager encapsulates the transaction system.  It contains
* references to the log and lock managers as well as the state that keeps
* track of the shared memory region.
*/
struct __db_txnmgr {
/* These fields need to be protected for multi-threaded support. */
    db_mutex_t    *mutexp;    /* Synchronization. */
                    /* list of active transactions */
    TAILQ_HEAD(_chain, __db_txn)    txn_chain;

/* These fields are not protected. */
    REGINFO        reginfo;    /* Region information. */
    DB_ENV        *dbenv;        /* Environment. */
    int (*recover)            /* Recovery dispatch routine */
        __P((DB_LOG *, DBT *, DB_LSN *, int, void *));
    u_int32_t     flags;        /* DB_TXN_NOSYNC, DB_THREAD */
    DB_TXNREGION    *region;    /* address of shared memory region */
    void        *mem;        /* address of the shalloc space */
};

同样拥有一个指向描述共享区域的reginfo字段和指向共享区域的指针mem。

/*
* Layout of the shared memory region.
* The region consists of a DB_TXNREGION structure followed by a large
* pool of shalloc'd memory which is used to hold TXN_DETAIL structures
* and thread mutexes (which are dynamically allocated).
*/
struct __db_txnregion {
    RLAYOUT        hdr;        /* Shared memory region header. */
    u_int32_t    magic;        /* transaction magic number */
    u_int32_t    version;    /* version number */
    u_int32_t    maxtxns;    /* maximum number of active txns */
    u_int32_t    last_txnid;    /* last transaction id given out */
    DB_LSN        pending_ckp;    /* last checkpoint did not finish */
    DB_LSN        last_ckp;    /* lsn of the last checkpoint */
    time_t        time_ckp;    /* time of last checkpoint */
    u_int32_t    logtype;    /* type of logging */
    u_int32_t    locktype;    /* lock type */
    u_int32_t    naborts;    /* number of aborted transactions */
    u_int32_t    ncommits;    /* number of committed transactions */
    u_int32_t    nbegins;    /* number of begun transactions */
    SH_TAILQ_HEAD(_active) active_txn;    /* active transaction list */
};

共享区域除了有个公用的头部hdr字段以外,最重要的是有一个所有活动事务的列表active_txn字段。

现在让我们看一下这些数据结构和共享区域的创建或打开以及初始化操作。

在db_appinit()函数中调用txn_open()函数打开或连接一个事务共享区域:

if (LF_ISSET(DB_INIT_TXN) && (ret = txn_open(NULL,
    LF_ISSET(DB_CREATE | DB_THREAD | DB_TXN_NOSYNC),
    mode, dbenv, &dbenv->tx_info)) != 0)
    goto err;

其中txn_open定义如下:

int
txn_open(path, flags, mode, dbenv, mgrpp)
    const char *path;
    u_int32_t flags;
    int mode;
    DB_ENV *dbenv;
    DB_TXNMGR **mgrpp;
{
    DB_TXNMGR *tmgrp;
    u_int32_t maxtxns;
    int ret;

    /* Validate arguments. */
    首先仍然是验证参数

    然后用malloc创建每个进程都有的事务管理器结构
    /* Now, create the transaction manager structure and set its fields. */
    if ((ret = __os_calloc(1, sizeof(DB_TXNMGR), &tmgrp)) != 0)
        return (ret);
    并初始化事务管理器结构
    /* Initialize the transaction manager structure. */
    tmgrp->mutexp = NULL;
    tmgrp->dbenv = dbenv;
    tmgrp->recover =
        dbenv->tx_recover == NULL ? __db_dispatch : dbenv->tx_recover;
    tmgrp->flags = LF_ISSET(DB_TXN_NOSYNC | DB_THREAD);
    TAILQ_INIT(&tmgrp->txn_chain);

    现在开始创建或连接到一个事务共享区域
    /* Join/create the txn region. */
    首先还是一样,填充区域描述信息REGINFO{},然后调用__db_rattach()函数创建或打开一个共享区域
    tmgrp->reginfo.dbenv = dbenv;
    tmgrp->reginfo.appname = DB_APP_NONE;
    if (path == NULL)
        tmgrp->reginfo.path = NULL;
    else
        if ((ret = __os_strdup(path, &tmgrp->reginfo.path)) != 0)
            goto err;
    tmgrp->reginfo.file = DEFAULT_TXN_FILE;
    tmgrp->reginfo.mode = mode;
    tmgrp->reginfo.size = TXN_REGION_SIZE(maxtxns);
    tmgrp->reginfo.dbflags = flags;
    tmgrp->reginfo.addr = NULL;
    tmgrp->reginfo.fd = -1;
    tmgrp->reginfo.flags = dbenv->tx_max == 0 ? REGION_SIZEDEF : 0;
    if ((ret = __db_rattach(&tmgrp->reginfo)) != 0)
        goto err;

    /* Fill in region-related fields. */
    tmgrp->region = tmgrp->reginfo.addr;
    tmgrp->mem = &tmgrp->region[1];

    if (F_ISSET(&tmgrp->reginfo, REGION_CREATED)) {
        tmgrp->region->maxtxns = maxtxns;
        if ((ret = __txn_init(tmgrp->region)) != 0)
            goto err;

    } else if (tmgrp->region->magic != DB_TXNMAGIC) {
        /* Check if valid region. */
        __db_err(dbenv, "txn_open: Bad magic number");
        ret = EINVAL;
        goto err;
    }

    // 如果这是一个新的事务共享区域,初始化之。这里又和MPOOL一样,
    // 使用__shmalloc函数管理共享区域内存
    if (LF_ISSET(DB_THREAD)) {
        if ((ret = __db_shalloc(tmgrp->mem, sizeof(db_mutex_t),
            MUTEX_ALIGNMENT, &tmgrp->mutexp)) == 0)
            /*
            * Since we only get here if threading is turned on, we
            * know that we have spinlocks, so the offset is going
            * to be ignored.  We put 0 here as a valid placeholder.
            */
            __db_mutex_init(tmgrp->mutexp, 0);
        if (ret != 0)
            goto err;
    }

    UNLOCK_TXNREGION(tmgrp);
    *mgrpp = tmgrp;
    return (0);

err:    if (tmgrp->reginfo.addr != NULL) {
        if (tmgrp->mutexp != NULL)
            __db_shalloc_free(tmgrp->mem, tmgrp->mutexp);

        UNLOCK_TXNREGION(tmgrp);
        (void)__db_rdetach(&tmgrp->reginfo);
        if (F_ISSET(&tmgrp->reginfo, REGION_CREATED))
            (void)txn_unlink(path, 1, dbenv);
    }

    if (tmgrp->reginfo.path != NULL)
        __os_freestr(tmgrp->reginfo.path);
    __os_free(tmgrp, sizeof(*tmgrp));
    return (ret);
}

这个函数就是txn_open()调用来初始化事务共享区域的函数:

/*
* This file contains the top level routines of the transaction library.
* It assumes that a lock manager and log manager that conform to the db_log(3)
* and db_lock(3) interfaces exist.
*
* Initialize a transaction region in shared memory.
* Return 0 on success, errno on failure.
*/
static int
__txn_init(txn_region)
    DB_TXNREGION *txn_region;
{
    time_t now;

    (void)time(&now);

    /* maxtxns is already initialized. */
    txn_region->magic = DB_TXNMAGIC;
    txn_region->version = DB_TXNVERSION;
    txn_region->last_txnid = TXN_MINIMUM;
    /*
    * XXX
    * If we ever do more types of locking and logging, this changes.
    */
    txn_region->logtype = 0;
    txn_region->locktype = 0;
    txn_region->time_ckp = now;
    ZERO_LSN(txn_region->last_ckp);
    ZERO_LSN(txn_region->pending_ckp);
    SH_TAILQ_INIT(&txn_region->active_txn);
    __db_shalloc_init((void *)&txn_region[1],
        TXN_REGION_SIZE(txn_region->maxtxns) - sizeof(DB_TXNREGION));

    return (0);
}

其中DB_TXN{}是每个事务的描述符(定义在db_int.h文件中):

/* The structure allocated for every transaction. */
struct __db_txn {
    DB_TXNMGR    *mgrp;        /* Pointer to transaction manager. */
    DB_TXN        *parent;    /* Pointer to transaction's parent. */
    DB_LSN        last_lsn;    /* Lsn of last log write. */
    u_int32_t    txnid;        /* Unique transaction id. */
    size_t        off;        /* Detail structure within region. */
    TAILQ_ENTRY(__db_txn) links;    /* Links transactions off manager. */
    TAILQ_HEAD(__kids, __db_txn) kids; /* Child transactions. */
    TAILQ_ENTRY(__db_txn) klinks;    /* Links child transactions. */

#define    TXN_MALLOC    0x01        /* Structure allocated by TXN system. */
    u_int32_t    flags;
};


typedef struct __txn_detail {
    u_int32_t txnid;        /* current transaction id
                    used to link free list also */
    DB_LSN    last_lsn;        /* last lsn written for this txn */
    DB_LSN    begin_lsn;        /* lsn of begin record */
    size_t    last_lock;        /* offset in lock region of last lock
                    for this transaction. */
    size_t    parent;            /* Offset of transaction's parent. */
#define    TXN_UNALLOC    0
#define    TXN_RUNNING    1
#define    TXN_ABORTED    2
#define    TXN_PREPARED    3
#define    TXN_COMMITTED    4
    u_int32_t status;        /* status of the transaction */
    SH_TAILQ_ENTRY    links;        /* free/active list */

#define    TXN_XA_ABORTED        1
#define    TXN_XA_DEADLOCKED    2
#define    TXN_XA_ENDED        3
#define    TXN_XA_PREPARED        4
#define    TXN_XA_STARTED        5
#define    TXN_XA_SUSPENDED    6
    u_int32_t xa_status;        /* XA status */

    /*
    * XID (xid_t) structure: because these fields are logged, the
    * sizes have to be explicit.
    */
    DB_XID xid;            /* XA global transaction id */
    u_int32_t bqual;        /* bqual_length from XID */
    u_int32_t gtrid;        /* gtrid_length from XID */
    int32_t format;            /* XA format */
} TXN_DETAIL;