Try   HackMD

2022q1 Homework3 (quiz3)

contributed by < laneser >

作業要求

2022q1 第 3 週測驗題

測驗 1

已知我們使用的微處理器架構為 64 位元,且 unsigned long 為 8 位元組寬度 (符合 LP64 資料模型),> 以下是可能的 GENMASK 巨集的定義:

#define GENMASK(h, l) \
    (((~0UL) >> (LEFT)) & ((~0UL) >> (l) << (RIGHT)))

想法 & 思考

先拆解題目的結果, 會是兩個值做 bitwise AND:

  • ((~0UL) >> (LEFT)) : 這個值是 ~0 (all 1) 往低位移 bits, 所以一定是高位 0, 低位 1 的形式.
  • ((~0UL) >> (l) << (RIGHT)) : 先把 ~0 往低位移, 再往高位移, 所以會是高位跟低位為 0, 中間為 1 的形式.

所以這樣就可以得知, 如果我們需要 h 表示高位的 1 起點, l 表示低位的 1 終點, 那麼

  • ((~0UL) >> (LEFT)) : 這個值是 ~0 (all 1) 往低位移 bits, 所以一定是高位 0, 直到 h 低位 1 的形式.
  • ((~0UL) >> (l) << (RIGHT)) : 先把 ~0 往低位移, 再往高位移, 所以會是高位跟低位為 0, 中間 為 1 的形式. 而高位的 h 以前的 0 交給前者負責, 所以這裡的值只要確保低位的 l 右邊都是 0 就可以.

那麼, LEFT 就是 63-h, RIGHTl.
想到 ((~0UL) >> (l) << (RIGHT)) 如果目的只是高位為 1, 低位為 0 的格式會更合理, 所以 ((~0UL) << (l)) 難道不好嗎 ?

延伸問題

比較 Linux 核心 GENMASK 巨集的實作,闡述其額外的考量
Linux 當中的 GENMASK 在 bits.h:

#define __GENMASK(h, l) \
	(((~UL(0)) - (UL(1) << (l)) + 1) & \
	 (~UL(0) >> (BITS_PER_LONG - 1 - (h))))
#define GENMASK(h, l) \
	(GENMASK_INPUT_CHECK(h, l) + __GENMASK(h, l))

GENMASK_INPUT_CHECK 如文字所述就是檢查輸入的參數是否合格.
所以本體是 __GENMASK(h, l) , 而也是兩個部分, 只是把往高位移的部分放在前面:

  • ((~UL(0)) - (UL(1) << (l)) + 1) : 並不是單純的 ~UL(0) << (l), 而是 ~0UL - (1UL << l), 這樣會做出一個從低位元算過來第 l 位是 0 , 其他都是 1 的 mask, 接著再 + 1 就會讓第 l 位置 0 右邊的 1 都變 0, 然後讓原本 l 位置的 0 進位變成 1.
  • (~UL(0) >> (BITS_PER_LONG - 1 - (h))): 的確跟 ((~0UL) >> (63 - h)) 格式一樣

舉出 Linux 核心原始程式碼中二處 GENMASK 巨集和 include/linux/bitfield.h 的應用案例


測驗 2