# 先決測驗 ###### tags: `2021 Summer - Linux kernel` :::warning 依據指定格式,標注 GitHub 帳號名稱 :notes: jserv ::: Github account: [demonsome](https://github.com/demonsome/linux2021_projects.git) [先決測驗題目連結](https://hackmd.io/@sysprog/linux2021-summer/https%3A%2F%2Fhackmd.io%2F%40sysprog%2Flinux2021-summer-quiz) ## 測驗 $\alpha$ 延伸問題: **1. 舉出 Linux 核心原始程式碼裡頭 bit rotation 的案例並說明:** **ANS:** 在Linux核心之中的[bitops.h](https://github.com/torvalds/linux/blob/master/tools/include/linux/bitops.h)之中,我們可以找到bit rotation的程式碼如下: ```clike= /** * rol32 - rotate a 32-bit value left * @word: value to rotate * @shift: bits to roll */ static inline __u32 rol32(__u32 word, unsigned int shift) { return (word << shift) | (word >> ((-shift) & 31)); } ``` 此函式為巨集定義,故`__u32`會在編譯後被取代為32bits的資料型態,變數**word**為32bits。這是實作32bits的向左位元旋轉,位移量為變數**shift**。舉例來說,當**word** = 0xABCDEF12, **shift** = 4 時,此函式應回傳 0xBCDEF12A 。 因此,實作上只要將word向左位移**shift**個bits,以此例來說是0xABCDEF12向左位移4個bits成為0xBCDEF120,並和word向右位移(32-**shift**)個bits,即0x0000000A,兩者再進行AND bitwise操作即可得到結果。 但為確保(32-**shift**)在大於31以及小於0時,仍可以正確運作。故寫成((-**shift**) & 31),此操作類似mod 32,使數值可以落在0到31之間。 **2. x86_64 指令集具備 rotr 和 rotl 指令,下述(測驗 $\alpha$ ) C 程式碼經過編譯器最佳化 (例如使用 gcc) 後,能否運用到這二個指令呢?** ```c= #include <stdint.h> #define __DECLARE_ROTATE(bits, type) \ static inline type rotl##bits(const type v, int c) \ { \ const int mask = (bits) - (1); \ c &= mask; \ \ return (v << c) | (LLL); \ } \ \ static inline type rotr##bits(const type v, int c) \ { \ const int mask = (bits) - (1); \ c &= mask; \ \ return (v >> c) | (RRR); \ } #define DECLARE_ROTATE(bits) __DECLARE_ROTATE(bits, uint##bits##_t) ``` **ANS:** 一般來說,若gcc可認得pattern: `(x << n) | (x >> (bits - n))`,則在最佳化時可直接將上述程式碼編譯成rotr 和 rotl指令,以減少指令數,增加處理器執行效能。 但是前提需滿足`0<n<bits`,否則編譯時,編譯器對於這些未定義的行為,會直接轉成 negl, shrl, sall, orl...等多個指令,而非rotr 和 rotl指令。詳細資訊可參考: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57157 ## 測驗 $\beta$ 延伸問題: **1. 說明下述程式碼的運作原理** ```c= #include <stdint.h> static inline uintptr_t align_up(uintptr_t sz, size_t alignment) { uintptr_t mask = alignment - 1; if ((alignment & mask) == 0) { /* power of two? */ return MMM; } return (((sz + mask) / alignment) * alignment); } ``` 此函式是將參數**sz**向上進位至參數**alignment**的倍數後回傳,若**alignment**是2的冪次方,則套用一個比較快速的方式(bitwise operation)回傳,否則使用除法的方式向上進位。 由於數位邏輯的基本運算中,邏輯運算如: AND, OR, NOT, shift 等bitwise operation操作是遠比除法快很多的。故將2的冪次方的情況獨立出來處理。 **2. 在 Linux 核心原始程式碼找出類似 align_up 的程式碼,並舉例說明其用法** 在linux的header file [align.h](https://github.com/torvalds/linux/blob/master/include/linux/align.h)中定義了向上對齊至2的冪次方的巨集,如下: ```c= /* SPDX-License-Identifier: GPL-2.0 */ #ifndef _LINUX_ALIGN_H #define _LINUX_ALIGN_H #include <linux/const.h> /* @a is a power of 2 value */ #define ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define ALIGN_DOWN(x, a) __ALIGN_KERNEL((x) - ((a) - 1), (a)) #define __ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask)) #define PTR_ALIGN(p, a) ((typeof(p))ALIGN((unsigned long)(p), (a))) #define PTR_ALIGN_DOWN(p, a) ((typeof(p))ALIGN_DOWN((unsigned long)(p), (a))) #define IS_ALIGNED(x, a) (((x) & ((typeof(x))(a) - 1)) == 0) #endif /* _LINUX_ALIGN_H */ ``` 而我們又可以在`/include/linux/netfilter/x_tables.h`中發現`__ALIGN_KERNEL`的蹤跡,巨集如下: ```c= /* this is a dummy structure to find out the alignment requirement for a struct * containing all the fundamental data types that are used in ipt_entry, * ip6t_entry and arpt_entry. This sucks, and it is a hack. It will be my * personal pleasure to remove it -HW */ struct _xt_align { __u8 u8; __u16 u16; __u32 u32; __u64 u64; }; #define COMPAT_XT_ALIGN(s) __ALIGN_KERNEL((s), __alignof__(struct _compat_xt_align)) ``` 由於在gcc及XL C/C++中,`__alignof__`讓你可以了解一個物件是如何被對齊的,在此應用中,假設結構`_compat_xt_align`需對齊到a個bytes的邊界,`COMPAT_XT_ALIGN(s)`將回傳一個讓參數s可以向上對齊的一個a的倍數,且a是2的冪次方。如此,可以確保物件在記憶體位置中被正確的擺放,而沒有被錯誤覆寫的狀況發生。