linux系统命令大全分享 linux原理和方法( 三 )


在这函数,会使用 mch_open 创建一个 backup 文件,名字后面带个 ~,例如 test.txt~,
bfd = mch_open((char *)backup
拿到 backup 文件的句柄,之后拷贝资料(只是一个循环喽), 每 8K 操作一次,从 test.txt 拷贝到 test.txt~,以做备份 。
划重要时机:如果是 test.txt 是超大文件,那这里就慢了哦 。
backup 循环如下:// buf_write
while ((write_info.bw_len = read_eintr(fd, copybuf, WRITEBUFSIZE)) > 0)
{
if (buf_write_bytes(&write_info) == FAIL)
// 如果失败,则终止
// 否则直到文件结束
}
}
我们观看到的,干活的是 buf_write_bytes,这是 write_eintr 的封装函数,其实也只是系统调用 write 的函数,负责写入一个 buffer 的资料到磁盘文件 。
long write_eintr(int fd, void *buf, size_t bufsize) {
longret = 0;
longwlen;
while (ret < (long)bufsize) {
// 封装的系统调用 write
wlen = vim_write(fd, (char *)buf + ret, bufsize - ret);
if (wlen < 0) {
if (errno != EINTR)
break;
} else
ret += wlen;
}
return ret;
}
backup 文件拷贝完成之后,就应该准备动原文件了 。
思考:怎么要先文件备份呢?留条后路呀,搞错了还一些复原,这种才是真正的备份文件 。
改写原文件曾经的第一步,ftruncate 原文件到 0,之后,从 memline (内存 + swp)中拷贝资料,写回原文件 。
划重要时机:这里又是一次文件拷贝,超大文件的时候,这里可能巨慢哦 。
for (lnum = start; lnum <= end; ++lnum)
{
// 从 memline 中获取资料,返回一个内存 buffer( memline 其实只是内存和 swap 文件的一个封装)
ptr = ml_get_buf(buf, lnum, FALSE) - 1;
// 将这种内存 buffer 写到原文件
if (buf_write_bytes(&write_info) == FAIL)
{
end = 0;// write error: break loop
break;
}
// ...
}
划重要时机:vim 并不是调用 pwrite/pread 这样的调用来改写原文件,而是把整个文件清空之后,copy 的方法来更新文件 。涨知识了 。
这样就完成了文件的更新啦,末尾只要删掉 backup 文件就可 。
// Remove the backup unless 'backup' option is set or there was a
// conversion error.
mch_remove(backup);
这种只是我们资料写入的完美流程啦 。是不是没有你想的那么无脑!
无脑小结下:当改写了 test.txt 文件,调用 :w 写入保存资料的时候发生了什么?
人机交互,:w 触发调用 ex_write 回调函数,于 do_write -> buf_write 完成写入 ;
详细操作是:先备份一个 test.txt~ 文件出去(全拷贝);
接着,原文件 test.txt 截断为 0,从 memline( 即 内存最新资料 + .test.txt.swap 的封装)拷贝资料,写入 test.txt (全拷贝) ;
资料团队结构曾经讲的太细节,我们从资料团队的角度来解答下 。vim 针对客户对文件的改写,在原文件之上,封装了两层抽象:memline,memfile。分别对应文件 memline.c,memfile.c。
先说 memline 是啥?
对应到文本文件中的每一行,memline 是基于 memfile 的 。
memline 基于 memfile,那 memfile 又是啥?
这种是一个虚拟内存空间的实现,vim 把整个文本文件映射到内存中,通过自己管理的方法 。这里的单位为 block,memfile 用二叉树的方法管理 block。block 不定长,block 由 page 组成,page 为定长 4k 大小 。
这是一个典型虚拟内存的实现方案,写器的改写都体现为对 memfile 的改写,改写都是改写到 block 之上,这是一个线性空间,每一个 block 对应到文件的要给地点,有 block number 编号,vim 通过策略会把 block 从内存中换出,写入到 swp 文件,从而节省内存 。这只是 swap 文件的名字由来 。
block 区分 3 种类别:
block 0 块:树的根,文件元资料;
pointer block:树的分支,指向下一个 block;
data block:树的叶子节点,存储客户资料;
swap 文件团队:
block 0 是特别块,结构体占用 1024 个字节内存,写到文件是根据 1 个page 对齐的,所以是 4096 个字节 。
如下图:
block 很多的两种类别:pointer 类别:这种是中间的分支节点,指向 block 的;
data 类别:这种是叶子节点;#define DATA_ID(('d' << 8) + 'a') // data block id
#define PTR_ID(('p' << 8) + 't') // pointer block id
这种 ID 等于魔数,在 swp 文件中很简无脑单查看出去,例如在下面的文件中第一个 4k 存储的是 block0,第二个 4k 存储的是 pointer 类别的 block 。
第三,第四个 4k 存储的是一个 data 类别的 block,里面存储了原文件资料 。

推荐阅读