contributed by < kylekylehaha
>
linux2020
先研究 xs 的結構:
字串長度 15 時,被歸為「短字串」
* 「短字串」存於 stack
中
* 字串長度 = 15 - space_left
,其中space_left
為字串右邊存在多少 0
字串 15
* is_ptr
設為 1 ,用來辨識此 xs 為 「長字串」
* 字串儲存於 heap
* 字串起點為 ptr
* capacity
為儲存字串的大小,為 2 的次方。
思考上述的 15
是如何被定義?是否能夠反映出硬體特性?
stack alginment matters
文章中有提到
The compiler is maintaining a 16-byte alignment of the stack pointer when a function is called, adding padding to the stack as necessary.
,因此我們理應選 16 bytes 來避免需要 padding
,但因為字串需要結束字元 \0
,故我們必須 -1。
這題出現在 xs_new
function 上。
一開始,我們可以從命名來猜,猜測這是一個具有 create 的 function。
我們已知道當字串長度小於 15
時,會直接把字串存於 data
中,而當字串大於 15
時,會將字串存於 heap
中。
而 len = strlen(p) + 1
中的 +1
,是為了加上 \0
,故可以得知 len
為包含 \0
後的長度,因此 AAA
= 16
BBB
& CCC
位於 xs_trim
function 中。
由 output 可以得知,此 function 用於刪除頭尾特定的字元,特定字元藉由 *trimset
傳入 function。
BBB
set_bit
的功能是將目標字元設為 1,其操作原理有點 hash function 的味道。256/8 = 32
即為 mask
所需的大小,故 BBB
= 32
mask[88/8]
會被設為 mask[88/8] |= 1 << (88%8)
,也就是 mask[88/8] |= 1
,等於 mask[88/8] = xxxxxxx1
CCC
check_bit
: 將 *dataptr
內的字元在 mask
上設為 1 ,並與之前已經 set_bit
的 mask
做比較。由於是做比較的效果,因此我認為此題 CCC
= &
set_bit
,故 X 的 mask
值為 xxxxxxx1
。這時將 X 丟入 check_bit
時,會發現兩者 &
後會得到 1,代表兩者為相同的字元,故要移除。flag1
來記錄此為共享記憶體,當有寫入更改時,會額外配置新的記憶體,並將 flag1
清除。dest->data[i]
dest->ptr
指向 src->ptr
測試一下
預期結果
印出兩者的位置,來確認是否為 copy
輸出結果
int *refcnt
,並修改一下之前的 xs_cpy
,來符合我們的 reference count*refcnt
指標,指向共同的記憶體,也就是 src->refcnt
。接著來修改 xs_concat
這邊我將問題切成兩種:串上後字元 16 & 串上後字元 16
xs_grow
內),故只須注意是否還有其他人仍然使用著共享記憶體,故不能隨便 free
掉。最後,來修改 xs_trim
free
掉。測試
輸出結果
測試一下
結果
strncpy
來當作這次實驗的對照組。Valgind
接著利用 valgrind 來檢查 cache miss Using valgrind to measure cache misses
一開始,我們先用 ori_cpy
得到以下結果
緊接著,我們用 xs_cpy
來實驗。
可以發現 D1 misses
兩者有明顯的差距。
Perf
接著,我們利用另一種工具 perf 來觀察 cache misses
以下分別為 ori_cpy
以及 xs_cpy
ori_cpy
xs_cpy
在 linux 中,以下為 process 進行 fork 的流程圖。參考What is a process
其中在作業系統,為了避免程式 fork 完後立刻 exec,因此採用 CoW 的機制。利用 parrent & children 共用未寫入的記憶體,來增加 children 的建立速度。
此外,若 children 第一時間就 exec 的話,也可以因為 CoW 的機制不會浪費記憶體空間以及時間了。
因此,我們研究 linux/fork.c,並參考 where is the source for fork() call in linux
do_fork
do_fork
出現在 linux/fork.cdo_fork
會先檢查是哪個 system callcopy_process
,which sets up the process descriptor and any other kernel data structure required for child 's executioncopy_prcoess
中,又去呼叫 dup_task
struct`, which creates new kernel stack, thread_info and task_struct structures for the new process,也就是真正創建 children process 的位置。current
為目前運行程式的 pointercopy_process
完成後,會去喚醒 childenvfork
的話,必須等 children 呼叫 exec
或離開而 CoW 實現在 copy_process
內的 copy_mm
。
copy_mm
中, vfork
會直接進入條件式,因為呼叫 vfork 時 args.flags 會被設成 CLONE_VFORK | CLONE_VM
,故直接共用一個mm_struct
。fork
則會直接呼叫 dup_mm
來複製 parent 的 mm_struct
。