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,里面存儲了原文件資料 。

推薦閱讀