📄 16.htm
字号:
<p>return(-1); /* EOF for db_nextrec() */ </p>
<p>err_dump("readv error of index record"); </p>
<p>} </p>
<p>asciiptr[PTR_SZ] = 0; /* null terminate */ </p>
<p>db->ptrval = atol(asciiptr); /* offset of next key in chain */ </p>
<p>/* this is our return value; always >= 0 */ </p>
<p>asciilen[IDXLEN_SZ] = 0; /* null terminate */ </p>
<p>if ( (db->idxlen = atoi(asciilen)) < IDXLEN_MIN || </p>
<p>db->idxlen > IDXLEN_MAX) </p>
<p>err_dump("invalid length"); </p>
<p>/* Now read the actual index record. We read it into the key </p>
<p>buffer that we malloced when we opened the database. */ </p>
<p>if ( (i = read(db->idxfd, db->idxbuf, db->idxlen)) != db->idxlen) </p>
<p>err_dump("read error of indexc record"); </p>
<p>if (db->idxbuf[db->idxlen-1] != '\n') </p>
<p>err_dump("missing newline"); /* sanity checks */ </p>
<p>db->idxbuf[db->idxlen-1] = 0; /* replace newline with null */ </p>
<p>/* Find the separators in the index record */ </p>
<p>if ( (ptr1 = strchr(db->idxbuf, SEP)) == NULL) </p>
<p>err_dump("missing first separator"); </p>
<p>*ptr1++ = 0; /* replace SEP with null */ </p>
<p>if ( (ptr2 = strchr(ptr1, SEP)) == NULL) </p>
<p>err_dump("missing second separator"); </p>
<p>*ptr2++ = 0; /* replace SEP with null */ </p>
<p>if (strchr(ptr2, SEP) != NULL) </p>
<p>asciiptr[PTR_SZ] = 0; /* null terminate */ </p>
<p>db->ptrval = atol(asciiptr); /* offset of next key in chain */ </p>
<p>/* this is our return value; always >= 0 */ </p>
<p>asciilen[IDXLEN_SZ] = 0; /* null terminate */ </p>
<p>if ( (db->idxlen = atoi(asciilen)) < IDXLEN_MIN || </p>
<p>db->idxlen > IDXLEN_MAX) </p>
<p>err_dump("invalid length"); </p>
<p>/* Now read the actual index record. We read it into the key </p>
<p>buffer that we malloced when we opened the database. */ </p>
<p>if ( (i = read(db->idxfd, db->idxbuf, db->idxlen)) != db->idxlen) </p>
<p>err_dump("read error of indexc record"); </p>
<p>if (db->idxbuf[db->idxlen-1] != '\n') </p>
<p>err_dump("missing newline"); /* sanity checks */ </p>
<p>db->idxbuf[db->idxlen-1] = 0; /* replace newline with null */ </p>
<p>/* Find the separators in the index record */ </p>
<p>if ( (ptr1 = strchr(db->idxbuf, SEP)) == NULL) </p>
<p>err_dump("missing first separator"); </p>
<p>*ptr1++ = 0; /* replace SEP with null */ </p>
<p>if ( (ptr2 = strchr(ptr1, SEP)) == NULL) </p>
<p>err_dump("missing second separator"); </p>
<p>*ptr2++ = 0; /* replace SEP with null */ </p>
<p>if (strchr(ptr2, SEP) != NULL) </p>
<p>err_dump("too many separators"); </p>
<p>/* Get the starting offset and length of the data record */ </p>
<p>if ( (db->datoff = atol(ptr1)) < 0) </p>
<p>err_dump("starting offset < 0"); </p>
<p>if ( (db->datlen = atol(ptr2)) <= 0 || db->datlen > DATLEN_MAX) </p>
<p>err_dump("invalid length"); </p>
<p>return(db->ptrval); /* return offset of next key in chain */ </p>
<p>} </p>
<p>程序16.11 _db_readidx函数 </p>
<p>我们调用readv来读取索引记录开始处的两个固定长度的字段:指向下一条索
</p>
<p>引记录的链指针和索引记录剩下的不定长部分的长度。然后,索引记录的剩下的部
</p>
<p>分被读入:主键、数据记录的偏移量和数据记录的长度。我们并不读数据记录,这
</p>
<p>由调用者自己完成。例如,在db_fetch中,在_db_find按主键找到索引记录前是不
</p>
<p>去读取数据记录的。 </p>
<p>下面我们回到db_fetch。如果_db_find找到了索引记录,我们调用_db_readd
</p>
<p>at来读取对应的数据记录。这是一个很简单的函数(见程序16.12)。
</p>
<p>#include "db.h" </p>
<p>/* Read the current data record into the data buffer. </p>
<p>* Return a pointer to the null-terminated data buffer. */ </p>
<p>char * </p>
<p>_db_readdat(DB *db) </p>
<p>{ </p>
<p>if (lseek(db->datfd, db->datoff, SEEK_SET) == -1) </p>
<p>err_dump("lseek error"); </p>
<p>if (read(db->datfd, db->datbuf, db->datlen) != db->datlen) </p>
<p>err_dump("read error"); </p>
<p>if (db->datbuf[db->datlen - 1] != '\n') /* sanity check */ </p>
<p>err_dump("missing newline"); </p>
<p>db->datbuf[db->datlen - 1] = 0; /* replace newline with null */ </p>
<p>return(db->datbuf); /* return pointer to data record */ </p>
<p>} </p>
<p>程序16.12 _de_readdat函数 </p>
<p>我们从db_fetch开始,现在已经读取了索引记录和对应的数据记录。注意到,
</p>
<p>我们只在_db_find中加了一个读锁。由于我们对这条Hash链加了读锁,所以其他进
</p>
<p>程在这段时间内不可能对这条Hash链进行修改。 </p>
<p>下面我们来看函数db_delete(程序16.13)。它开始时和db_fetch一样,调用
</p>
<p>_db_find来查找记录,只是这里传递给_db_find的最后一个参数为1,表示我们要
</p>
<p>对这一条Hash链加写锁。 </p>
<p>db_delete调用_db_dodelete(程序16.14)来完成剩下的工作(在后面我们将
</p>
<p>看到db_store也调用_db_dodelete)。_db_dodelete的大部分工作是修正空闲链以
</p>
<p>及与主键对应的Hash链。 </p>
<p>当一条记录被删除后,我们将其主键和数据记录设为空。在本章后面我们将提
</p>
<p>到的函数db_nextrec要用到这一点。 </p>
<p>_db_dodelete对空闲链表加写锁,这样能防止两个进程同时删除不同链表上的
</p>
<p>记录时产生相互影响,因为我们将被删除的记录移到空闲链表上,这将改变空闲链
</p>
<p>表,而一次只能有一个进程能这样做。 </p>
<p>#include "db.h" </p>
<p>/* Delete the specified record */ </p>
<p>int </p>
<p>db_delete(DB *db, const char *key) </p>
<p>{ </p>
<p>int rc; </p>
<p>if (_db_find(db, key, 1) == 0) { </p>
<p>rc = _db_dodelete(db); /* record found */ </p>
<p>db->cnt_delok++; </p>
<p>} else { </p>
<p>rc = -1; /* not found */ </p>
<p>db->cnt_delerr++; </p>
<p>} </p>
<p>if (un_lock(db->idxfd, db->chainoff, SEEK_SET, 1) < 0) </p>
<p>err_dump("un_lock error"); </p>
<p>return(rc); </p>
<p>} </p>
<p>程序16.13 db_delete函数 </p>
<p>#include "db.h" </p>
<p>/* Delete the current record specified by the DB structure. </p>
<p>* This function is called by db_delete() and db_store(), </p>
<p>* after the record has been located by _db_find(). */ </p>
<p>int </p>
<p>_db_dodelete(DB *db) </p>
<p>{ </p>
<p>int i; </p>
<p>char *ptr; </p>
<p>off_t freeptr, saveptr; </p>
<p>/* Set data buffer to all blanks */ </p>
<p>for (ptr = db->datbuf, i = 0; i < db->datlen - 1; i++) </p>
<p>*ptr++ = ' '; </p>
<p>*ptr = 0; /* null terminate for _db_writedat() */ </p>
<p>/* Set key to blanks */ </p>
<p>ptr = db->idxbuf; </p>
<p>while (*ptr) </p>
<p>*ptr++ = ' '; </p>
<p>/* We have to lock the free list */ </p>
<p>if (writew_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0) </p>
<p>err_dump("writew_lock error"); </p>
<p>/* Write the data record with all blanks */ </p>
<p>_db_writedat(db, db->datbuf, db->datoff, SEEK_SET); </p>
<p>/* Read the free list pointer. Its value becomes the </p>
<p>chain ptr field of the deleted index record. This means </p>
<p>the deleted record becomes the head of the free list. */ </p>
<p>freeptr = _db_readptr(db, FREE_OFF); </p>
<p>/* Save the contents of index record chain ptr, </p>
<p>before it's rewritten by _db_writeidx(). */ </p>
<p>saveptr = db->ptrval; </p>
<p>/* Rewrite the index record. This also rewrites the length </p>
<p>of the index record, the data offset, and the data length, </p>
<p>none of which has changed, but that's OK. */ </p>
<p>_db_writeidx(db, db->idxbuf, db->idxoff, SEEK_SET, freeptr); </p>
<p>/* Write the new free list pointer */ </p>
<p>_db_writeptr(db, FREE_OFF, db->idxoff); </p>
<p>/* Rewrite the chain ptr that pointed to this record </p>
<p>being deleted. Recall that _db_find() sets db->ptroff </p>
<p>to point to this chain ptr. We set this chain ptr </p>
<p>to the contents of the deleted record's chain ptr, </p>
<p>saveptr, which can be either zero or nonzero. */ </p>
<p>_db_writeptr(db, db->ptroff, saveptr); </p>
<p>if (un_lock(db->idxfd, FREE_OFF, SEEK_SET, 1) < 0) </p>
<p>err_dump("un_lock error"); </p>
<p>return(0); </p>
<p>} </p>
<p>程序16.14 _db_dodelete函数 </p>
<p>_db_dodelete通过调用函数_db_writedat(程序16.15)清空数据记录。这时 </p>
<p>_db_writedat并不对数据文件加写锁,这是因为db_delete对这条记录索引的Hash
</p>
<p>链加了写锁,这已经保证不会有其它进程能够读写这条记录。本章后面讲述db_st
</p>
<p>ore时,我们将看到在另一种情况下,_db_writedat将数据添加到文件的末尾,并
</p>
<p>将加锁。 </p>
<p>_db_writedat调用writev来写数据记录及回车符。我们不能够假设调用者传递
</p>
<p>进来的字符缓冲有空间让我们在其后面再添加一个回车符。回忆12。7节,在那里
</p>
<p>我们曾讨论过一个writev要比两个write快。 </p>
<p>接着_db_dodelete修改索引记录。让这条记录的链指针指向空闲链表的第一条
</p>
<p>记录(如果空闲链表为空,则这个链指针置为0),空闲链表指针也被修改,指向
</p>
<p>当前删除的这条记录,这样就将这条删除的记录移到了空闲链表首。我们看到空闲
</p>
<p>链表实际上很象一个先进先出的堆栈。 </p>
<p>#include "db.h" </p>
<p>#include <sys/uio.h> /* struct iovec */ </p>
<p>/* Write a data record. Called by _db_dodelete() (to write </p>
<p>the record with blanks) and db_store(). */ </p>
<p>void </p>
<p>_db_writedat(DB *db, const char *data, off_t offset, int whence) </p>
<p>{ </p>
<p>struct iovec iov[2]; </p>
<p>static char newline = '\n'; </p>
<p>/* If we're appending, we have to lock before doing the lseek() </p>
<p>and write() to make the two an atomic operation. If we're </p>
<p>overwriting an existing record, we don't have to lock. */ </p>
<p>if (whence == SEEK_END) /* we're appending, lock entire file */ </p>
<p>if (writew_lock(db->datfd, 0, SEEK_SET, 0) < 0) </p>
<p>err_dump("writew_lock error"); </p>
<p>if ( (db->datoff = lseek(db->datfd, offset, whence)) == -1) </p>
<p>err_dump("lseek error"); </p>
<p>db->datlen = strlen(data) + 1; /* datlen includes newline */ </p>
<p>iov[0].iov_base = (char *) data; </p>
<p>iov[0].iov_len = db->datlen - 1; </p>
<p>iov[1].iov_base = &newline; </p>
<p>iov[1].iov_len = 1; </p>
<p>if (writev(db->datfd, &iov[0], 2) != db->datlen) </p>
<p>err_dump("writev error of data record"); </p>
<p>if (whence == SEEK_END) </p>
<p>if (un_lock(db->datfd, 0, SEEK_SET, 0) < 0) </p>
<p>err_dump("un_lock error"); </p>
<p>} </p>
<p>程序16.15 _db_writedat函数 </p>
<p>我们并没有为索引文件和数据文件的空闲记录设置各自的空闲链表。这是因为
</p>
<p>当一条记录被删除时,对应的索引记录被加入了空闲链表,而这条索引记录中有指
</p>
<p>向数据文件中数据记录的指针。有其它很多更好的处理记录删除的方法,只是它们
</p>
<p>会更复杂一些。 </p>
<p>程序16.16列出了函数_db_writeidx,_db_dodelete调用此函数来写一条索引 </p>
<p>记录。和_db_writedat一样,这一函数也只有在添加新索引记录时才需要加锁。_
</p>
<p>db_dodelete调用这条函数是为了重写一条索引记录,我们知道在这种情况下调用
</p>
<p>者已经在Hash链上加了写锁,所以不再需要加另外的锁了。 </p>
<p>#include "db.h" </p>
<p>#include <sys/uio.h> /* struct iovec */ </p>
<p>/* Write an index record. </p>
<p>* _db_writedat() is called before this function, to set the fields </p>
<p>* datoff and datlen in the DB structure, which we need to write </p>
<p>* the index record. */ </p>
<p>void </p>
<p>_db_writeidx(DB *db, const char *key, </p>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -