📄 16.htm
字号:
</p>
<p>据库的情况。第一个进程运行到调用fstat,并且在fstat返回后被核心切换。这时
</p>
<p>第二个进程调用db_open,发现索引文件的长度为0,并初始化空闲链表和Hash链表
</p>
<p>。第二个进程继续运行,向数据库中添加了一条记录。这时第二个进程被阻塞,第
</p>
<p>一个进程继续运行,并发现索引文件的大小为0(因为第一个进程是在fstat返回后
</p>
<p>才被切换),所以第一个进程重新初始化空闲链表和Hash链表,第二个进程写入的
</p>
<p>记录就被抹去了。要避免发生这种情况的方法是进行加锁,我们使用12.3节中的r
</p>
<p>eadw_lock,writew_lock和un_lock这三个函数。 </p>
<p>db_open调用程序16.4中定义的函数_db_alloc来为DB结构分配空间,包括一个索引
</p>
<p>缓冲和一个数据缓冲。 </p>
<p>#include "db.h" </p>
<p>/* Allocate & initialize a DB structure, and all the buffers it needs </p>
<p>*/ </p>
<p>DB * </p>
<p>_db_alloc(int namelen) </p>
<p>{ </p>
<p>DB *db; </p>
<p>/* Use calloc, to init structure to zero */ </p>
<p>if ( (db = calloc(1, sizeof(DB))) == NULL) </p>
<p>err_dump("calloc error for DB"); </p>
<p>db->idxfd = db->datfd = -1; /* descriptors */ </p>
<p>/* Allocate room for the name. </p>
<p>+5 for ".idx" or ".dat" plus null at end. */ </p>
<p>if ( (db->name = malloc(namelen + 5)) == NULL) </p>
<p>err_dump("malloc error for name"); </p>
<p>/* Allocate an index buffer and a data buffer. </p>
<p>+2 for newline and null at end. */ </p>
<p>if ( (db->idxbuf = malloc(IDXLEN_MAX + 2)) == NULL) </p>
<p>err_dump("malloc error for index buffer"); </p>
<p>if ( (db->datbuf = malloc(DATLEN_MAX + 2)) == NULL) </p>
<p>err_dump("malloc error for data buffer"); </p>
<p>return(db); </p>
<p>} </p>
<p>程序16.4 _db_alloc函数 </p>
<p>索引缓冲和数据缓冲的大小在db.h中定义。我们的数据库函数库可以通过让这
</p>
<p>些缓冲按需要动态扩张来得到加强。其方法可以是记录这两个缓冲的大小,然后在
</p>
<p>需要更大的缓冲时调用realloc。 </p>
<p>在函数_db_free(程序16.5)中,这些缓冲将被释放,同时打开的文件被关闭
</p>
<p>。db_open在打开索引文件和数据文件时如果遇到错误,则调用_db_free释放资源
</p>
<p>。db_close(程序16.6)也调用_db_free。 </p>
<p>函数db_fetch(程序16.7)根据给定的主键来读取一条记录。它调用_db_fin
</p>
<p>d在数据库中查找一条索引记录,如果找到,再调用_db_readdat来读取对应的数据
</p>
<p>记录。 </p>
<p>#include "db.h" </p>
<p>/* Free up a DB structure, and all the malloc'ed buffers it </p>
<p>* may point to. Also close the file descriptors if still open. */ </p>
<p>int </p>
<p>_db_free(DB *db) </p>
<p>{ </p>
<p>if (db->idxfd >= 0 && close(db->idxfd) < 0) </p>
<p>err_dump("index close error"); </p>
<p>if (db->datfd >= 0 && close(db->datfd) < 0) </p>
<p>err_dump("data close error"); </p>
<p>db->idxfd = db->datfd = -1; </p>
<p>if (db->idxbuf != NULL) </p>
<p>free(db->idxbuf); </p>
<p>if (db->datbuf != NULL) </p>
<p>free(db->datbuf); </p>
<p>if (db->name != NULL) </p>
<p>free(db->name); </p>
<p>free(db); </p>
<p>return(0); </p>
<p>} </p>
<p>程序16.5 _db_free函数 </p>
<p>#include "db.h" </p>
<p>void </p>
<p>db_close(DB *db) </p>
<p>{ </p>
<p>_db_free(db); /* closes fds, free buffers & struct */ </p>
<p>} </p>
<p>程序16.6 db_close函数 </p>
<p>#include "db.h" </p>
<p>/* Fetch a specified record. </p>
<p>* We return a pointer to the null-terminated data. */ </p>
<p>char * </p>
<p>db_fetch(DB *db, const char *key) </p>
<p>{ </p>
<p>char *ptr; </p>
<p>if (_db_find(db, key, 0) < 0) { </p>
<p>ptr = NULL; /* error, record not found */ </p>
<p>db->cnt_fetcherr++; </p>
<p>} else { </p>
<p>ptr = _db_readdat(db); /* return pointer to data */ </p>
<p>db->cnt_fetchok++; </p>
<p>} </p>
<p>/* Unlock the hash chain that _db_find() locked */ </p>
<p>if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0) </p>
<p>err_dump("un_lock error"); </p>
<p>return(ptr); </p>
<p>} </p>
<p>程序16.7 db_fetch函数 </p>
<p>函数_db_find(程序16.8)通过遍历Hash链来查找记录。db_fetch,db_dele </p>
<p>te和db_store这几个需要根据主键找记录的函数都调用它。 </p>
<p>#include "db.h" </p>
<p>/* Find the specified record. </p>
<p>* Called by db_delete(), db_fetch(), and db_store(). */ </p>
<p>int </p>
<p>_db_find(DB *db, const char *key, int writelock) </p>
<p>{ </p>
<p>off_t offset, nextoffset; </p>
<p>/* Calculate hash value for this key, then calculate byte </p>
<p>offset of corresponding chain ptr in hash table. </p>
<p>This is where our search starts. */ </p>
<p>/* calc offset in hash table for this key */ </p>
<p>db->chainoff = (_db_hash(db, key) * PTR_SZ) + db->hashoff; </p>
<p>db->ptroff = db->chainoff; </p>
<p>/* Here's where we lock this hash chain. It's the </p>
<p>caller's responsibility to unlock it when done. </p>
<p>Note we lock and unlock only the first byte. */ </p>
<p>if (writelock) { </p>
<p>if (writew_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0) </p>
<p>err_dump("writew_lock error"); </p>
<p>} else { </p>
<p>if (readw_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0) </p>
<p>err_dump("readw_lock error"); </p>
<p>} </p>
<p>/* Get the offset in the index file of first record </p>
<p>on the hash chain (can be 0) */ </p>
<p>offset = _db_readptr(db, db->ptroff); </p>
<p>while (offset != 0) { </p>
<p>nextoffset = _db_readidx(db, offset); </p>
<p>if (strcmp(db->idxbuf, key) == 0) </p>
<p>break; /* found a match */ </p>
<p>db->ptroff = offset; /* offset of this (unequal) record */ </p>
<p>offset = nextoffset; /* next one to compare */ </p>
<p>} </p>
<p>if (offset == 0) </p>
<p>return(-1); /* error, record not found */ </p>
<p>/* We have a match. We're guaranteed that db->ptroff contains </p>
<p>the offset of the chain ptr that points to this matching </p>
<p>index record. _db_dodelete() uses this fact. (The chain </p>
<p>ptr that points to this matching record could be in an </p>
<p>index record or in the hash table.) */ </p>
<p>return(0); </p>
<p>} </p>
<p>程序16.8 _db_find函数 </p>
<p>_db_find的最后一个参数指明我们需要加什么样的锁,0表示读锁,1表示写锁
</p>
<p>。我们知道,db_fetch需要加读锁,而db_delete和db_store需要加写锁。_db_fi
</p>
<p>nd在获得需要的锁之前将等待。 </p>
<p>_db_find中的while循环遍历Hash链中的索引记录,并比较主键。函数_db_re
</p>
<p>adidx用于读取每条索引记录。 </p>
<p>请注意阅读_db_find中的最后一条注释。当沿着Hash链进行遍历时,必须始终
</p>
<p>跟踪索引前一条索引记录,其中有一个指针指向当前记录。这一点在我们删除一条
</p>
<p>记录时是很有用的,因为我们必须修改当前索引记录的前一条记录的链指针。
</p>
<p>让我们先来看看_db_find调用的一些比较简单的函数。_db_hash(程序16.9)
</p>
<p>根据给定的主键计算Hash值。它将主键中的每一个ASCII字符乘以这个字符在字符
</p>
<p>串中以1开始的索引号,将这些结果加起来,除以Hash表的大小,将余数作为这个
</p>
<p>主键的Hash值。 </p>
<p>#include "db.h" </p>
<p>/* Calculate the hash value for a key. */ </p>
<p>hash_t </p>
<p>_db_hash(DB *db, const char *key) </p>
<p>{ </p>
<p>hash_t hval; </p>
<p>const char *ptr; </p>
<p>char c; </p>
<p>int i; </p>
<p>hval = 0; </p>
<p>for (ptr = key, i = 1; c = *ptr++; i++) </p>
<p>hval += c * i; /* ascii char times its 1-based index */ </p>
<p>return(hval % db->nhash); </p>
<p>} </p>
<p>程序16.9 _db_hash函数 </p>
<p>_db_find调用的下一个函数是_db_readptr(程序16.10)。这个函数能够读取
</p>
<p>以下三种不同的链指针中的任意一种:(1)索引文件最开始的空闲链表指针。(
</p>
<p>2)Hash表中指向Hash链的第一条索引记录的指针。(3)每条索引记录头的指向下
</p>
<p>一条记录的指针(这里的索引记录既可以处于一条Hash链表中,也可以处于空闲链
</p>
<p>表中)。这个函数的调用者应做好必要的加锁,此函数不进行任何加锁。
</p>
<p>#include "db.h" </p>
<p>/* Read a chain ptr field from anywhere in the index file: </p>
<p>* the free list pointer, a hash table chain ptr, or an </p>
<p>* index record chain ptr. */ </p>
<p>off_t </p>
<p>_db_readptr(DB *db, off_t offset) </p>
<p>{ </p>
<p>char asciiptr[PTR_SZ + 1]; </p>
<p>if (lseek(db->idxfd, offset, SEEK_SET) == -1) </p>
<p>err_dump("lseek error to ptr field"); </p>
<p>if (read(db->idxfd, asciiptr, PTR_SZ) != PTR_SZ) </p>
<p>err_dump("read error of ptr field"); </p>
<p>asciiptr[PTR_SZ] = 0; /* null terminate */ </p>
<p>return(atol(asciiptr)); </p>
<p>} </p>
<p>程序16.10 _db_readptr函数 </p>
<p>_db_find中的while循环通过调用_db_readidx来读取各条索引记录。_db_rea </p>
<p>didx(程序16.11)是一个较长的函数,这个函数读取索引记录,并将索引记录的
</p>
<p>信息存储到适当的字段中。 </p>
<p>#include "db.h" </p>
<p>#include <sys/uio.h> /* struct iovec */ </p>
<p>/* Read the next index record. We start at the specified offset in </p>
<p>* the index file. We read the index record into db->idxbuf and </p>
<p>* replace the separators with null bytes. If all is OK we set </p>
<p>* db->datoff and db->datlen to the offset and length of the </p>
<p>* corresponding data record in the data file. */ </p>
<p>off_t </p>
<p>_db_readidx(DB *db, off_t offset) </p>
<p>{ </p>
<p>int i; </p>
<p>char *ptr1, *ptr2; </p>
<p>char asciiptr[PTR_SZ + 1], asciilen[IDXLEN_SZ + 1]; </p>
<p>struct iovec iov[2]; </p>
<p>/* Position index file and record the offset. db_nextrec() </p>
<p>calls us with offset==0, meaning read from current offset. </p>
<p>We still need to call lseek() to record the current offset. */ </p>
<p>if ( (db->idxoff = lseek(db->idxfd, offset, </p>
<p>offset == 0 ? SEEK_CUR : SEEK_SET)) == -1) </p>
<p>err_dump("lseek error"); </p>
<p>/* Read the ascii chain ptr and the ascii length at </p>
<p>the front of the index record. This tells us the </p>
<p>remaining size of the index record. */ </p>
<p>iov[0].iov_base = asciiptr; </p>
<p>iov[0].iov_len = PTR_SZ; </p>
<p>iov[1].iov_base = asciilen; </p>
<p>iov[1].iov_len = IDXLEN_SZ; </p>
<p>if ( (i = readv(db->idxfd, &iov[0], 2)) != PTR_SZ + IDXLEN_SZ) { </p>
<p>if (i == 0 && offset == 0) </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -