📄 pager.c
字号:
{ if(0 != pager_open_journal(pager)) return NULL; } /* 如果此页已在jf里做备份(pg->page_state != PAGE_CLEAN) */ if(pager_page_is_in_journal(pg)) { assert(pg->page_num <= pager_get_total_pages_count(pager)); /* 将页置成dirty,并加入dirty list */ if(!pager_page_is_dirty(pg)) pager_make_page_dirty(pager, pg, PAGE_EVT_WRITE_BUF_BK); return buf_write; } /* 如果(pg->page_state == PAGE_CLEAN)将调用以下代码 */ assert(!pager_page_is_dirty(pg)); assert(pager->orig_pages_count != (unsigned int)-1); if(pg->page_num > pager->orig_pages_count) { /* * 页在修改之前本身是不存在的,所以也不用真正地写入,做个标记即可 * 这里应处理当有多个页的超出页文件大小的情况,因为超出文件大小情况下,只需要同步 * jf头就可以了,这种情况只需要同步一次,即之后再发生这种情况,因为不存在写入的问题 * jf是不用步的,因为已经同步过了 */ pager_set_total_pages_count(pager, ((unsigned int)pg->page_num)); pager_make_page_dirty(pager, pg, PAGE_EVT_WRITE_BUF_BK); /* 如果已经做过同步,则需要修改状态 */ if(pager->bsync_journal_has_done) pager_change_page_state(pg, PAGE_EVT_SYNC_JOURNAL); } else { unsigned int cksum = pager_cal_page_cksum(pg->pg_buf_start, pager->page_size, pager->cur_cksum_init); /* * 如果没有则需要写入jfd, <页缓存内容,先页号,最后写入校验和>,并且给该页缓存做上injournal标识,并在着色表相应的位置置标识 */ uint_to_big_endian((unsigned int)pg->page_num, (pg->pg_buf_start + pager->page_size), sizeof(unsigned int)); uint_to_big_endian(cksum, pg->pg_buf_start + pager->page_size + sizeof(unsigned int), sizeof(unsigned int)); if(0 != OsFileWrite(pager->hjf, pg->pg_buf_start, pager_get_journal_rec_len(pager), NULL)) return NULL; pager->in_journal_pages_count ++; pager->journal_end += pager_get_journal_rec_len(pager); /* 将页置成dirty,并加入dirty list */ pager_make_page_dirty(pager, pg, PAGE_EVT_WRITE_BUF_BK); assert(!pager_page_is_sync_journal(pg)); } pager_mark_page_in_journal(pager, pg); if(!pager_page_is_sync_journal(pg)) pager->need_sync_journal = 1; return buf_write;}/** * @brief 从jf里回滚 */static __INLINE__ int pager_file_truncate(pager_t * pager, unsigned int page_count){ assert(pager && pager->hf); if(0 != OsFileTruncate(pager->hf, page_count * pager->page_size)) return -1; return 0;}/** * @brief 从jf里回滚 */static __INLINE__ int pager_roll_back_one_page(pager_t * pager, unsigned int orig_page_count, unsigned int cksum_init, unsigned char * data_buf, const unsigned int data_buf_len){ int ret = 0; unsigned int cksum = 0; unsigned int pg_no = 0; pghd_t * pg = NULL; assert(pager && pager->hjf && pager->page_size); assert(pager_get_journal_rec_len(pager) == data_buf_len && data_buf); assert(pager_get_journal_rec_len(pager) >= pager->page_size); if(0 != OsFileRead(pager->hjf, data_buf, data_buf_len, NULL)) return RHAPSODY_FAIL; pager->journal_end += data_buf_len; /* 计算校验和,判断此页是否是正确的 */ array_to_uint_as_big_endian(data_buf + pager->page_size + sizeof(unsigned int), sizeof(unsigned int), cksum); if(cksum != pager_cal_page_cksum(data_buf, pager->page_size, cksum_init)) return RHAPSODY_DONE; /* 在页缓存哈希表里查找 */ array_to_uint_as_big_endian(data_buf + pager->page_size, sizeof(unsigned int), pg_no); pg = pager_hash_lookup(pager, pg_no); if(NULL == pg || pager_page_is_written_before_page_sync(pg)) { /* * 找不到,需要写回文件 * 找到,则判断是否在提交之前写回过文件,如果有也要写回文件 */ if(0 != pager_write_page_data(pager, data_buf, pager->page_size, pg_no)) return RHAPSODY_FAIL; } /* 重刷内存 */ if(pg) { assert(PAGE_CLEAN != pg->pg_state); memcpy(pg->pg_buf_start, data_buf, pager->page_size); if(pager_page_is_dirty(pg)) { assert(pager_page_is_in_dirty_list(pg)); pager_page_out_of_dirty_list(pager, pg); } pager_change_page_state(pg, PAGE_EVT_ROLL_BACK); /* 回调用户的reload回调 */ if(pager->page_reload_cb) pager->page_reload_cb(pg); } return RHAPSODY_OK;}/** * @brief 从jf里回滚 */static __INLINE__ int pager_roll_back_from_journal(pager_t * pager){ int64 file_size = 0; unsigned int injournal_count = 0; unsigned int orig_page_count = 0; unsigned int cksum_init = 0; int ret = 0; int bfirst = 1; int i = 0; unsigned char * data_buf = NULL; unsigned int data_buf_len = 0; assert(pager && pager->hjf); /* 分配处理回滚过程中需要的内存 */ data_buf_len = pager_get_journal_rec_len(pager); data_buf = MyMemPoolMalloc(pager->hm, data_buf_len); if(NULL == data_buf) return -1; if(0 != OsFileSize(pager->hjf, &file_size)) { ret = -1; goto pager_roll_back_from_journal_end_; } if(0 != OsFileSeek(pager->hjf, 0)) { ret = -1; goto pager_roll_back_from_journal_end_; } pager->journal_end = 0; while(1) { /* 先读取jf头 */ ret = pager_read_jf_head(pager, file_size, &injournal_count, &orig_page_count, &cksum_init); if(RHAPSODY_OK != ret) break; if(bfirst) { /* 如果读出的第一个jf头,则要将页文件栽减成原来的大小 */ if(0 != pager_file_truncate(pager, orig_page_count)) { ret = -1; goto pager_roll_back_from_journal_end_; } pager->total_page_count = orig_page_count; bfirst = 0; } /* 如果是最后一个jf头,并且记录数显示为零,则有可能jf还未同步 */ if(0 == injournal_count && pager->journal_end == (pager->jouranl_prev_hdr + pager->sector_size)) { /* 说明此时有可能jf本身还同步,剩余的脏页可以直接于页文件里取,因为没有写回 */ assert(pager->journal_end + pager->in_journal_pages_count * pager_get_journal_rec_len(pager) == file_size || 0 == pager->ref_count); assert((pager->journal_end < file_size && pager->need_sync_journal && pager->pg_dirty) || 0 == pager->ref_count || NULL == pager->pg_dirty); /* * 此时外存对的页文件的页是干净的,但此时仍然从jf里取 * 代价是额外的计算校验和的开销, * 如果改成直接从外页页文件中,则看外设的特点,以点os对文件的组织形式. * 如果外设是flash,此种方法效率较低. * 如果外设是机械磁盘,则要看页文件的大小,以及os对文件的组织.也许从jf里读,可以减少磁盘io时的寻道时间. */ injournal_count = pager->in_journal_pages_count; } /* 根据jf头,循环读出每一条备份记录 */ for(i = injournal_count; i > 0; i --) { ret = pager_roll_back_one_page(pager, orig_page_count, cksum_init, data_buf, data_buf_len); if(RHAPSODY_OK != ret) break; } } { /* * 还需要还原内存中那些超过页文件大小的页缓存, * 将它们的数据刷回0 * todo:优先此段代码 */ pghd_t * pg = pager->pg_all; for(; pg; pg = pg->pg_all_next) { assert(!pager_page_is_dirty(pg)); if(orig_page_count <= pg->page_num) memset(pg->pg_buf_start, 0, pager->page_size); if(pager_page_is_sync_journal(pg) && 0 == pg->ref_count) { assert(pager_page_is_in_free_list(pg)); assert(pager_page_is_in_syncjf_list(pg)); pager_out_syncjf_list(pager, pg); } assert(!pager_page_is_in_syncjf_list(pg)); if(pager_page_is_dirty(pg)) { assert(pager_page_is_in_dirty_list(pg)); pager_page_out_of_dirty_list(pager, pg); } if(!pager_page_is_clean_not_in_jf(pg)) pager_change_page_state(pg, PAGE_EVT_DEL_JOURNAL); assert(!pager_page_is_sync_journal(pg)); } assert(NULL == pager->pg_syncjf_first && NULL == pager->pg_syncjf_last); } /* 如果ret != RHAPSODY_FAIL 删除备份日志文件 */ if(RHAPSODY_FAIL != ret) { assert(NULL == pager->pg_dirty); pager->page_file_is_integrity = 1; if(0 != pager_close_and_del_journal(pager)) { ret = -1; goto pager_roll_back_from_journal_end_; } ret = 0; }pager_roll_back_from_journal_end_: MyMemPoolFree(pager->hm, data_buf); return ret;}/** * @brief 第一页获取页缓存引用,判断是否存在jf文件,存在则执行回滚操作 */static __INLINE__ int pager_check_and_roll_back(pager_t * pager){ assert(pager && pager->hf); /* 判断jf文件是否存在,不存在,返回成功 */ if(!OsFileExists((char *)MyBufferGet(pager->hb_jf_name, NULL))) return 0; /* * 存在,需要打开它进行回滚 * 以只读方式打开jf文件 */ if(0 != pager_open_journal_readonly(pager)) return -1; /* 回滚 */ if(0 != pager_roll_back_from_journal(pager)) return -1; return 0;}/** * @brief 重新加载页缓存中的脏页 */static __INLINE__ int pager_reload_cache(pager_t * pager){ unsigned char * buf = NULL; pghd_t * pg = NULL; assert(pager && pager->hf); assert(pager->pg_dirty); assert(pager->page_size); assert(pager->page_file_is_integrity); assert(NULL == pager->pg_syncjf_first && NULL == pager->pg_syncjf_last); buf = MyMemPoolMalloc(pager->hm, pager->page_size); if(NULL == buf) return -1; for(pg = pager->pg_dirty; pg; pg = pager->pg_dirty) { assert(pager_page_is_dirty(pg)); assert(!pager_page_is_in_syncjf_list(pg)); assert(!pager_page_is_sync_journal(pg)); /* 从外存文件读取相应的页 */ if(pg->page_num <= pager->orig_pages_count) { if(0 != pager_read_page_data(pager, pg->page_num, buf, pager->page_size)) return -1; /* 拷贝至页缓存 */ memcpy(pg->pg_buf_start, buf, pager->page_size); } else memset(pg->pg_buf_start, 0, pager->page_size); /* 改变页的状态 */ pager_page_out_of_dirty_list(pager, pg); pager_change_page_state(pg, PAGE_EVT_ROLL_BACK); assert(PAGE_CLEAN == pg->pg_state); if(pager->page_reload_cb) pager->page_reload_cb(pg); } assert(buf); MyMemPoolFree(pager->hm, buf); assert(NULL == pager->pg_dirty); /* 删除备份的jf文件 */ if(0 != pager_close_and_del_journal(pager)) return -1; return 0;}/** * @brief 取消所有的页的更改 */static __INLINE__ int pager_rollback(pager_t * pager){ int ret = -1; assert(pager); /* * 在PagerSyn之前,RollBack是有效的,PagerSyn之后,RollBack没有意义了. * RollBack判断页文件是否已经同步过jfd,并且向fd里写了数据.此时应从jfd里回滚所有页 * 如果没有,则重新加载fd里相应的页至脏页缓存即可, * 删除份的jfd文件. * jfd着色表以及页的injournal标识均置成0,因为数据恢复成完整状态. */ if(!pager->page_file_is_integrity)/* 如果在提交之前,有部分页缓存有写回至页文件,从jf里还原 */ ret = pager_roll_back_from_journal(pager); else if(NULL == pager->pg_dirty)/* 如果没有脏的缓存页,把jf删除即可 */ ret = pager_close_and_del_journal(pager); else/* 如果在提交之前,没有页缓存写回页文件,重新将脏缓存加载一次即可 */ ret = pager_reload_cache(pager); /* 完成回滚之后,jf应删除 */ return ret;}/** * @brief 按页号递增进行排序的比较回调函数 * > 0 表示 key1 大于 key2 * == 0 表示 key1 等于 key2 * < 0 表示 key1 小于 key2 * * @param context:用户自定义的上下文数据 */static int pager_sort_dirty_compare(const void * data1, const void * data2, const void * context){ pghd_t * pg1 = *((pghd_t **)data1); pghd_t * pg2 = *((pghd_t **)data2); assert(pg1 && pg2); assert(pg2->page_num && pg1->page_num); /*return pg1->page_num - pg2->page_num */ if(pg1->page_num >= pg2->page_num) return pg1->page_num - pg2->page_num; else return -1;}/** * @brief 同步所有脏缓存页 */static __INLINE__ int pager_sort_dirty_list(pager_t * pager){ /* * sqlite有用的是归并排序 * 对脏页进行排序,保证从最小的页开始写回外存 * 在将脏页写入外存页文件之前,按页号进行排序是很重要的 * * 分配一块内存数组 * 将所有的页依次填写这个内存数组 * 以页号为比较关键字对这个数组进行快速排序 * 将排序好的数组按次序添加至dirty list */ pghd_t * pg = NULL; pghd_t ** pg_array = NULL; unsigned int dirty_pages_count = 0; unsigned int i = 0; assert(pager && pager->pg_dirty); for(pg = pager->pg_dirty; pg; pg = pg->pg_dirty_next) dirty_pages_count ++; if(dirty_pages_count <= 1) return 0; pg_array = MyMemPoolMalloc(pager->hm, sizeof(pg_array[0]) * dirty_pages_count); if(NULL == pg_array) return -1; for(i = 0, pg = pager->pg_dirty; pg; pg = pg->pg_dirty_next, i ++) { assert(pager_page_is_dirty(pg)); assert(pg->page_num); assert(i < dirty_pages_count); pg_array[i] = pg; } pg = NULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -