zoukankan      html  css  js  c++  java
  • APUE-数据库函数库

    一、函数库

    1、创建、关闭

    1 DBHANDLE  db_open(const char *pathname, int oflag, .../* int mode */);/*成功返回数据库句柄,出错返回NULL*/
    2 void      db_close(DBHANDLE);

       若db_open成功返回,则建立两个文件:pathname.idx(索引文件)和pathname.dat(数据文件)。第二个参数指定文件的打开模式。若建立新数据库文件,mode将作为第三个参数传递给open。

      若不再使用数据库时,调用db_close来关闭数据库。db_close将关闭索引文件和数据文件,并释放数据库使用过程中分配的所用于内部缓冲的存储空间。

    2、存储

    1 int db_store(DBHANDLE db, const char *key, const char *data, int flah);/*成功返回0,出错返回非0值*/

       key和data是由null结束的字符串。可包含除null外的任何字符,如换行符。

      flag参数取DB_INSERT(添加一条新记录)、DB_REPLACE(替换一条已有记录)或DB_STORE(加一条新记录或替换一条已有的记录,只要合适无论哪一种都可以)。

    3、单条查询

    1 char *db_fetch(DBHANDLE h, const char *key);/*成功返回指向数据的指针,记录未找到返回NULL*/

       通过键key取出一条记录。

      返回键key存放数据的指针。

    1 /*
    2  * Delete the specified record.
    3  */
    4 int db_delete(DBHANDLE h, const char *key);/*成功返回0,失败返回-1*/

    4、遍历查询

    1 void      db_rewind(DBHANDLE db);
    2 char     *db_nextrec(DBHANDLE db, char *key);

      可逐条记录访问数据库。调用db_rewind回滚到数据库第一条记录,然后循环调用db_nexterc,顺序读每条记录。

      若key是非空指针,则db_nextrec将当前记录的键复制到key锁指向的存储区。

      db_nextrec无法保存访问的次序。

    二、实现概述

      下图描述了索引文件和数据文件的结构。

      空闲链表的作用:用于管理那些已删除的记录。因为已经分配了空间,当记录被删除后,空位还在,通过管理空闲链表,后面如果需要插入链表等操作,还可利用此空间。

      下图中第一行0 53 35 0的结构对应于上图中最上面一行的格式。

      0 10Alpha:0:6的解析如下:

    0:下一条索引记录的偏移量(指针)

    10:该索引记录剩余的长度

    Alpha:键值

    ::分隔符

    0:数据记录的偏移量

    6:数据记录长度

      由下图可分析出第一条散列记录中第一条数据为gamma,第二条数据为Alpha。第二个散列表第一条记录为beta,且只有一条记录。

    三、集中式或非集中式

      1、集中式。由一个进程作为数据库管理者,所有数据库访问由此进程完成。其他进程通过IPC与此中心继承进行联系。

      2、非集中式。每个库函数独立申请并发控制(加锁),然后自己调用I/O函数。

      非集中式避免了使用IPC,会比集中式快一点。

      集中式,中心数据库管理进程是唯一对数据文件进行I/O操作的进程。

      集中式的优点,能根据需要对需求对操作模式进行调整,如对于不同的进程赋予不同的优先级。

      集中式的另一个优点,复原更容易。所有状态信息都存储在一处,故恢复时,只需在该处识别出需要解决的未完成事务,然后恢复。

      用户进程是合作进程,通过使用字节范围锁实现并发控制。

    四、并发

      1、粗锁

       最简单的加锁是将两个文件的一个作为整个数据库的锁,并要去调用者在对数据库操作前必须获得这个锁,这种方式称为粗锁。

      2、细锁

    五、构造函数库

      使用下面的命令构造一静态函数库。

    1 gcc -I../include -Wall -c db.c
    2 ar rsv libapue_db.a db.o

      添加libapue.a链接,同事构建动态共享库版本

    1 gcc -I../include -Wall -fPIC -c db.c
    2 gcc -shared -W1, -soname, libapue_db.so.1 -o libapue_db.so.1 -L../lib -lapue -lc db.o

      libapue_db.so.1需放在动态链接/装入程序(dynamic linker/loader)能找到的公用目录。另一个方法:将共享库放置在私有目录,修改LD_LIBRARY_PATH环境变量,即可动态链接/装入程序的搜索路径包含此私有目录。

    六、源代码

     1 /*
     2  * Library's private representation of the database.
     3  */
     4 typedef struct {
     5   int    idxfd;  /* fd for index file */
     6   int    datfd;  /* fd for data file */
     7   char  *idxbuf; /* malloc'ed buffer for index record */
     8   char  *datbuf; /* malloc'ed buffer for data record*/
     9   char  *name;   /* name db was opened under */
    10   
    11   /*索引文件中当前记录的偏移量*/
    12   /*键的位置=idxoff + PTR_SZ + IDXLEN_SZ */
    13   off_t  idxoff; /* offset in index file of index record */
    14                   /* key is at (idxoff + PTR_SZ + IDXLEN_SZ) */
    15                   
    16   /*索引记录长度*/
    17   /*不包括IDXLEN_SZ长度域的长度,包含换行符*/
    18   size_t idxlen; /* length of index record */
    19                   /* excludes IDXLEN_SZ bytes at front of record */
    20                   /* includes newline at end of index record */
    21   /*数据记录在数据文件中的偏移量*/
    22   off_t  datoff; /* offset in data file of data record */
    23   /*数据记录长度,包含末尾换行符*/
    24   size_t datlen; /* length of data record */
    25                   /* includes newline at end */
    26                   
    27   /*包含在散列链中下一条索引项的偏移量*/
    28   off_t  ptrval; /* contents of chain ptr in index record */
    29   
    30   /*指向这个索引记录的链表指针偏移量*/
    31   off_t  ptroff; /* chain ptr offset pointing to this idx record */
    32   
    33   /*索引记录中哈希链表中的位置,及散列表中的链表指针位置*/
    34   /*在散列链中的起始地址*/
    35   off_t  chainoff; /* offset of hash chain for this index record */
    36   
    37   /*索引文件中哈希表的偏移量?*/
    38   off_t  hashoff;  /* offset in index file of hash table */
    39 
    40   /*当前哈希表大小*/
    41   DBHASH nhash;    /* current hash table size */
    42 
    43   /*用于数据操作统计*/
    44   COUNT  cnt_delok;    /* delete OK */
    45   COUNT  cnt_delerr;   /* delete error */
    46   COUNT  cnt_fetchok;  /* fetch OK */
    47   COUNT  cnt_fetcherr; /* fetch error */
    48   COUNT  cnt_nextrec;  /* nextrec */
    49   COUNT  cnt_stor1;    /* store: DB_INSERT, no empty, appended */
    50   COUNT  cnt_stor2;    /* store: DB_INSERT, found empty, reused */
    51   COUNT  cnt_stor3;    /* store: DB_REPLACE, diff len, appended */
    52   COUNT  cnt_stor4;    /* store: DB_REPLACE, same len, overwrote */
    53   COUNT  cnt_storerr;  /* store error */
    54 } DB;
     1 /*
     2  * Open or create a database.  Same arguments as open(2).
     3  */
     4 DBHANDLE
     5 db_open(const char *pathname, int oflag, ...)
     6 {
     7     DB            *db;
     8     int            len, mode;
     9     size_t        i;
    10     char        asciiptr[PTR_SZ + 1],
    11                 hash[(NHASH_DEF + 1) * PTR_SZ + 2];
    12                     /* +2 for newline and null */
    13     struct stat    statbuff;
    14 
    15     /*
    16      * Allocate a DB structure, and the buffers it needs.
    17      */
    18     len = strlen(pathname);
    19     if ((db = _db_alloc(len)) == NULL)/*分配一个db结构,初始化它的缓冲区*/
    20         err_dump("db_open: _db_alloc error for DB");
    21 
    22     db->nhash   = NHASH_DEF;/* hash table size 137 */
    23     db->hashoff = HASH_OFF;    /* offset in index file of hash table 7 */
    24     strcpy(db->name, pathname);
    25     strcat(db->name, ".idx");
    26     if (oflag & O_CREAT) {
    27         /*获取可变参数内容*/
    28         va_list ap;
    29 
    30         va_start(ap, oflag);
    31         mode = va_arg(ap, int);
    32         va_end(ap);
    33 
    34         /*
    35          * Open index file and data file.
    36          */
    37         db->idxfd = open(db->name, oflag, mode);
    38         strcpy(db->name + len, ".dat");
    39         db->datfd = open(db->name, oflag, mode);
    40     } else {
    41         /*
    42          * Open index file and data file.
    43          */
    44         db->idxfd = open(db->name, oflag);
    45         strcpy(db->name + len, ".dat");
    46         db->datfd = open(db->name, oflag);
    47     }
    48 
    49     if (db->idxfd < 0 || db->datfd < 0) {
    50         _db_free(db);
    51         return(NULL);
    52     }
    53     if ((oflag & (O_CREAT | O_TRUNC)) == (O_CREAT | O_TRUNC)) {
    54         /*
    55          * If the database was created, we have to initialize
    56          * it.  Write lock the entire file so that we can stat
    57          * it, check its size, and initialize it, atomically.
    58          */
    59         if (writew_lock(db->idxfd, 0, SEEK_SET, 0) < 0)
    60             err_dump("db_open: writew_lock error");
    61 
    62         if (fstat(db->idxfd, &statbuff) < 0)
    63             err_sys("db_open: fstat error");
    64 
    65         if (statbuff.st_size == 0) {
    66             /*
    67              * We have to build a list of (NHASH_DEF + 1) chain
    68              * ptrs with a value of 0.  The +1 is for the free
    69              * list pointer that precedes the hash table.
    70              */
    71             sprintf(asciiptr, "%*d", PTR_SZ, 0);
    72             hash[0] = 0;
    73             for (i = 0; i < NHASH_DEF + 1; i++)
    74                 strcat(hash, asciiptr);
    75             strcat(hash, "
    ");
    76             i = strlen(hash);
    77             if (write(db->idxfd, hash, i) != i)
    78                 err_dump("db_open: index file init write error");
    79         }
    80         if (un_lock(db->idxfd, 0, SEEK_SET, 0) < 0)
    81             err_dump("db_open: un_lock error");
    82     }
    83     db_rewind(db);
    84     return(db);
    85 }
     1 /*
     2  * Fetch a record.  Return a pointer to the null-terminated data.
     3  */
     4 char *
     5 db_fetch(DBHANDLE h, const char *key)
     6 {
     7     DB      *db = h;
     8     char    *ptr;
     9 
    10     /*查询是否存在,存在,则置db结构中相关数据*/
    11     if (_db_find_and_lock(db, key, 0) < 0) {
    12         ptr = NULL;                /* error, record not found */
    13         db->cnt_fetcherr++;
    14     } else {
    15         /*根据前面查询结果,读取查询到的记录。*/
    16         ptr = _db_readdat(db);    /* return pointer to data */
    17         db->cnt_fetchok++;
    18     }
    19 
    20     /*
    21      * Unlock the hash chain that _db_find_and_lock locked.
    22      */
    23     if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
    24         err_dump("db_fetch: un_lock error");
    25     return(ptr);
    26 }
     1 /*
     2  * Find the specified record.  Called by db_delete, db_fetch,
     3  * and db_store.  Returns with the hash chain locked.
     4  */
     5 static int
     6 _db_find_and_lock(DB *db, const char *key, int writelock)
     7 {
     8     off_t    offset, nextoffset;
     9 
    10     /*
    11      * Calculate the hash value for this key, then calculate the
    12      * byte offset of corresponding chain ptr in hash table.
    13      * This is where our search starts.  First we calculate the
    14      * offset in the hash table for this key.
    15      */
    16      /*计算此键的哈希值,然后计算哈希表中相应链ptr的字节偏移量。
    17      这是我们搜索开始的位置。 首先,我们计算此密钥的哈希表中的偏移量。*/
    18      /*db->chainoff:散列表指针的位置,即第几个链表指针。找到对应key的链表指针*/
    19     db->chainoff = (_db_hash(db, key) * PTR_SZ) + db->hashoff;
    20     db->ptroff = db->chainoff;
    21     
    22     printf("db->chainoff:%p
    ",db->chainoff);
    23 
    24     /*
    25      * We lock the hash chain here.  The caller must unlock it
    26      * when done.  Note we lock and unlock only the first byte.
    27      */
    28      /*锁定哈希链表,只锁定第一个字节,可最大程度增加并发性*/
    29     if (writelock) {
    30         if (writew_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
    31             err_dump("_db_find_and_lock: writew_lock error");
    32     } else {
    33         if (readw_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0)
    34             err_dump("_db_find_and_lock: readw_lock error");
    35     }
    36 
    37     /*
    38      * Get the offset in the index file of first record
    39      * on the hash chain (can be 0).
    40      */
    41      /*取到索引文件中第一条记录的偏移量(指针)*/
    42      /**通过散列表中查到的偏移量(指针),找到索引记录中的第一个元素(链表指针),
    43      及下一条记录的(偏移量)指针,0代表是最后一条记录*/
    44     offset = _db_readptr(db, db->ptroff);
    45     while (offset != 0) {
    46 
    47         /*此函数返回下一条索引记录的指针,同时,如果查询到记录数据内容,
    48         则填充db结构中,数据记录的信息*/
    49         nextoffset = _db_readidx(db, offset);
    50         if (strcmp(db->idxbuf, key) == 0)
    51             break;       /* found a match 找到匹配,退出查询 */
    52         /*准备下一次循环查询工作*/
    53         db->ptroff = offset; /* offset of this (unequal) record */
    54         offset = nextoffset; /* next one to compare */
    55     }
    56     /*
    57      * offset == 0 on error (record not found).
    58      */
    59     return(offset == 0 ? -1 : 0);
    60 }
     1 /*
     2  * Read the next index record.  We start at the specified offset
     3  * in the index file.  We read the index record into db->idxbuf
     4  * and replace the separators with null bytes.  If all is OK we
     5  * set db->datoff and db->datlen to the offset and length of the
     6  * corresponding data record in the data file.
     7  */
     8  /*读下一条索引记录。从索引文件中指定的偏移位置开始。将索引记录内容
     9  读到db->idxbuf中,并用null替换分隔符。如果所有正常,将用对应数据文
    10  件中的数据数据记录的偏移量和长度设置db->datoff和db->datlen。 */
    11  /*单条索引记录结构:链表指针(7)+索引记录长度(4)+键值+:+数据记录偏移+:+数据记录长度*/
    12 static off_t
    13 _db_readidx(DB *db, off_t offset)
    14 {
    15     ssize_t                i;
    16     char            *ptr1, *ptr2;
    17     char            asciiptr[PTR_SZ + 1], asciilen[IDXLEN_SZ + 1];
    18     struct iovec    iov[2];
    19 
    20     /*
    21      * Position index file and record the offset.  db_nextrec
    22      * calls us with offset==0, meaning read from current offset.
    23      * We still need to call lseek to record the current offset.
    24      */
    25      /*索引文件和记录的位置偏移。db_nextrec调用此函数时offset==0,
    26      意味着从当前的位置开始读。我们仍需要调用lseek来记录当前的偏移。*/
    27      /*db->idxoff:索引文件中当前记录偏移量。*/
    28     if ((db->idxoff = lseek(db->idxfd, offset,
    29       offset == 0 ? SEEK_CUR : SEEK_SET)) == -1)
    30         err_dump("_db_readidx: lseek error");
    31 
    32     /*
    33      * Read the ascii chain ptr and the ascii length at
    34      * the front of the index record.  This tells us the
    35      * remaining size of the index record.
    36      */
    37     iov[0].iov_base = asciiptr;
    38     iov[0].iov_len  = PTR_SZ;
    39     iov[1].iov_base = asciilen;
    40     iov[1].iov_len  = IDXLEN_SZ;
    41     if ((i = readv(db->idxfd, &iov[0], 2)) != PTR_SZ + IDXLEN_SZ) {
    42         if (i == 0 && offset == 0)
    43             return(-1);        /* EOF for db_nextrec */
    44         err_dump("_db_readidx: readv error of index record");
    45     }
    46 
    47     /*
    48      * This is our return value; always >= 0.
    49      */
    50      /*当前索引记录的首字段*/
    51     asciiptr[PTR_SZ] = 0;        /* null terminate */
    52     db->ptrval = atol(asciiptr); /* offset of next key in chain */
    53 
    54     /*当前索引记录的长度*/
    55     asciilen[IDXLEN_SZ] = 0;     /* null terminate */
    56     if ((db->idxlen = atoi(asciilen)) < IDXLEN_MIN ||
    57       db->idxlen > IDXLEN_MAX)
    58         err_dump("_db_readidx: invalid length");
    59 
    60     /*
    61      * Now read the actual index record.  We read it into the key
    62      * buffer that we malloced when we opened the database.
    63      */
    64      /*读取索引记录内容,存储到了db->idxbuf中*/
    65     if ((i = read(db->idxfd, db->idxbuf, db->idxlen)) != db->idxlen)
    66         err_dump("_db_readidx: read error of index record");
    67     if (db->idxbuf[db->idxlen-1] != NEWLINE)    /* sanity check */
    68         err_dump("_db_readidx: missing newline");
    69     db->idxbuf[db->idxlen-1] = 0;     /* replace newline with null */
    70 
    71     /*
    72      * Find the separators in the index record.
    73      */
    74      /*ptr1:指针指向键值*/
    75     if ((ptr1 = strchr(db->idxbuf, SEP)) == NULL)
    76         err_dump("_db_readidx: missing first separator");
    77     *ptr1++ = 0;                /* replace SEP with null */
    78     /*指针指向数据记录偏移*/
    79     if ((ptr2 = strchr(ptr1, SEP)) == NULL)
    80         err_dump("_db_readidx: missing second separator");
    81     *ptr2++ = 0;                /* replace SEP with null */
    82 
    83     if (strchr(ptr2, SEP) != NULL)
    84         err_dump("_db_readidx: too many separators");
    85 
    86     /*
    87      * Get the starting offset and length of the data record.
    88      */
    89      /*将解析到的数据记录信息填充到db结构中*/
    90     if ((db->datoff = atol(ptr1)) < 0)
    91         err_dump("_db_readidx: starting offset < 0");
    92     if ((db->datlen = atol(ptr2)) <= 0 || db->datlen > DATLEN_MAX)
    93         err_dump("_db_readidx: invalid length");
    94     return(db->ptrval);        /* return offset of next key in chain */
    95 }
     1 /*
     2  * Delete the current record specified by the DB structure.
     3  * This function is called by db_delete and db_store, after
     4  * the record has been located by _db_find_and_lock.
     5  */
     6  /*删除db结构中的当前记录。
     7  此函数需要记录被_db_find_and_lock函数定位后,被调用*/
     8 static void
     9 _db_dodelete(DB *db)
    10 {
    11     int        i;
    12     char    *ptr;
    13     off_t    freeptr, saveptr;
    14 
    15     /*
    16      * Set data buffer and key to all blanks.
    17      */
    18      /*将数据缓存区置为空格*/
    19     for (ptr = db->datbuf, i = 0; i < db->datlen - 1; i++)
    20         *ptr++ = SPACE;
    21     *ptr = 0;    /* null terminate for _db_writedat */
    22     /*清索引记录*/
    23     ptr = db->idxbuf;
    24     while (*ptr)
    25         *ptr++ = SPACE;
    26 
    27     /*
    28      * We have to lock the free list.
    29      */
    30      /*锁定空闲链表,因为要将删除记录移到空闲链表上*/
    31     if (writew_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
    32         err_dump("_db_dodelete: writew_lock error");
    33 
    34     /*
    35      * Write the data record with all blanks.
    36      */
    37      /*将一个数据记录写为空白*/
    38     _db_writedat(db, db->datbuf, db->datoff, SEEK_SET);
    39 
    40     /*
    41      * Read the free list pointer.  Its value becomes the
    42      * chain ptr field of the deleted index record.  This means
    43      * the deleted record becomes the head of the free list.
    44      */
    45      /*读空闲链表指针*/
    46     freeptr = _db_readptr(db, FREE_OFF);
    47 
    48     /*
    49      * Save the contents of index record chain ptr,
    50      * before it's rewritten by _db_writeidx.
    51      */
    52      /*保存索引记录首项,即下条记录的指针*/
    53     saveptr = db->ptrval;
    54 
    55     /*
    56      * Rewrite the index record.  This also rewrites the length
    57      * of the index record, the data offset, and the data length,
    58      * none of which has changed, but that's OK.
    59      */
    60      /*重写索引记录,重写索引记录长度,数据偏移,数据长度*/
    61      /*idxbuf和idxoff已经被清空*/
    62     _db_writeidx(db, db->idxbuf, db->idxoff, SEEK_SET, freeptr);
    63 
    64     /*
    65      * Write the new free list pointer.
    66      */
    67      /*将索引记录的首项数据写到空闲链表*/
    68     _db_writeptr(db, FREE_OFF, db->idxoff);
    69 
    70     /*
    71      * Rewrite the chain ptr that pointed to this record being
    72      * deleted.  Recall that _db_find_and_lock sets db->ptroff to
    73      * point to this chain ptr.  We set this chain ptr to the
    74      * contents of the deleted record's chain ptr, saveptr.
    75      */
    76      /*重写指向该删除记录的链表指针。重新调用_db_find_and_lock设置
    77      db->ptroff指向该链表指针。设置链表指针指向删除的记录链表指针内容,
    78      下条记录的指针。*/
    79     _db_writeptr(db, db->ptroff, saveptr);
    80     if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0)
    81         err_dump("_db_dodelete: un_lock error");
    82 }

    七、性能

      通过比较在单进程和多进程下,采用不同的加锁方式下,用户时间、系统时间的消耗程度来比较数据库性能。

  • 相关阅读:
    注解的那些事儿(三)| 注解的使用
    注解的那些事儿(二)| 如何自定义注解
    注解的那些事儿(一)| 为什么要使用注解?
    数据库历险记(三) | 缓存框架的连环炮
    书值 | 第 2 期:成为技术管理者,思维上应该如何转变?
    书值 | 第 1 期:如何在1年内完成大学四年的课程?
    网站被篡改详细处理方法
    【代码审计】任意文件读取漏洞实例
    XSS三重URL编码绕过实例
    代码审计之DocCms漏洞分析
  • 原文地址:https://www.cnblogs.com/mofei004/p/10135315.html
Copyright © 2011-2022 走看看