# 2019q1 第 7 週測驗題 (上) --- ### 測驗 `1` 考慮到以下 Linux 核心的 `find_next_zero_bit` 的重新實作 (`bits.c`): ```cpp #include <assert.h> #include <stdbool.h> #include <stddef.h> #include <stdint.h> #include <string.h> // number of bits per byte/char #define BITS_PER_BYTE 8 // number of bits per long #define BITS_PER_LONG (sizeof(unsigned long) * BITS_PER_BYTE) /** * DECLARE_BITMAP - declare bitmap to store at least bit 0..(bits -1) * @bitmap: name for the new bitmap * @bits: number of required bits */ #define DECLARE_BITMAP(bitmap, bits) unsigned long bitmap[BITS_TO_LONGS(bits)] /** * BITOPS_DIV_CEIL - calculate quotient of integer division (round up) * @numerator: side effect free expression for numerator of division * @denominator: side effect free expression for denominator of division * * numerator and denominator must be from a type which can store * denominator + numerator without overflow. denominator must be larger than 0 * and numerator must be positive. * * WARNING @numerator expression must be side-effect free */ #define BITOPS_DIV_CEIL(numerator, denominator) \ (((numerator) + (denominator) -1) / (denominator)) // BITS_TO_LONGS - return number of longs to save at least bit 0..(bits - 1) #define BITS_TO_LONGS(bits) BITOPS_DIV_CEIL(bits, BITS_PER_LONG) /** * bitops_ffs() - find (least significant) first set bit plus one * @x: unsigned long to check * Return: plus-one index of first set bit; zero when x is zero */ static inline size_t bitops_ffs(unsigned long x) { return __builtin_ffsl(x); } /** * test_bit() - Get state of bit * @bit: address of bit to test * @bitmap: bitmap to test * Return: true when bit is one and false when bit is zero */ static inline bool test_bit(size_t bit, const unsigned long *bitmap) { size_t l = bit / BITS_PER_LONG; size_t b = bit % BITS_PER_LONG; return !!(bitmap[l] & (1UL << b)); } /** * BITMAP_FIRST_WORD_MASK - return unsigned long mask for least significant long * @start: offset to first bits * * All bits which can be modified in the least significant unsigned long for * offset @start in the bitmap will be set to 1. All other bits will be set to * zero */ #define BITMAP_FIRST_WORD_MASK(start) (~0UL << ((start) % BITS_PER_LONG)) /** * BITMAP_LAST_WORD_MASK - return unsigned long mask for most significant long * @bits: number of bits in complete bitmap * * All bits which can be modified in the most significant unsigned long in the * bitmap will be set to 1. All other bits will be set to zero */ #define BITMAP_LAST_WORD_MASK(bits) (~0UL >> (-(bits) % BITS_PER_LONG)) /** * find_next_zero_bit() - Find next clear bit in bitmap * @bitmap: bitmap to check * @bits: number of bits in @bitmap * @start: start of bits to check * * Checks the modifiable bits in the bitmap. The overhead bits in the last * unsigned long will not be checked * * Return: bit position of next clear bit, @bits when no clear bit was found */ static inline size_t find_next_zero_bit(const unsigned long *bitmap, size_t bits, size_t start) { size_t pos; unsigned long t; size_t l = BITS_TO_LONGS(bits); size_t first_long = start / BITS_PER_LONG; size_t long_lower = start - (start % BITS_PER_LONG); if (start >= bits) return bits; t = bitmap[first_long] | ~BITMAP_FIRST_WORD_MASK(start); t ^= ~0UL; for (size_t i = first_long + 1; !t && i < l; i++) { /* search until valid t is found */ long_lower += BITS_PER_LONG; t = bitmap[i]; t ^= ~0UL; } if (!t) return bits; pos = long_lower + bitops_ffs(t) - 1; if (pos >= bits) return bits; return pos; } #define MAX_TEST_BITS 400 static DECLARE_BITMAP(test, MAX_TEST_BITS); static size_t find_next(unsigned long *bitmap, size_t bits, size_t start) { for (size_t i = start; i < bits; i++) { if (test_bit(i, bitmap)) return i; } return bits; } /** * find_next_bit() - Find next set bit in bitmap * @bitmap: bitmap to check * @bits: number of bits in @bitmap * @start: start of bits to check * * Checks the modifiable bits in the bitmap. The overhead bits in the last * unsigned long will not be checked * * Return: bit position of next set bit, @bits when no set bit was found */ static inline size_t find_next_bit(const unsigned long *bitmap, size_t bits, size_t start) { unsigned long t; size_t l = BITS_TO_LONGS(bits); size_t first_long = start / BITS_PER_LONG; size_t long_lower = start - (start % BITS_PER_LONG); if (start >= bits) return bits; t = bitmap[first_long] & BITMAP_FIRST_WORD_MASK(start); for (size_t i = first_long + 1; !t && i < l; i++) { ...待補... } if (!t) return bits; size_t pos; ...待補... return pos; } static void check_bits(unsigned long *bitmap, size_t bits) { size_t i = 0; while (i < bits) { size_t r1 = find_next(bitmap, bits, i); size_t r2 = find_next_bit(bitmap, bits, i); assert(r1 == r2); i = r1 + 1; } } /** * bitmap_fill() - Initializes bitmap with one * @bitmap: bitmap to modify * @bits: number of bits * * Initializes all modifiable bits to one. The overhead bits in the last * unsigned long will be set to zero. */ static inline void bitmap_fill(unsigned long *bitmap, size_t bits) { size_t l = BITS_TO_LONGS(bits); if (l > 1) memset(bitmap, 0xff, (l - 1) * sizeof(unsigned long)); bitmap[l - 1] = BITMAP_LAST_WORD_MASK(bits); } /** * bitmap_zero() - Initializes bitmap with zero * @bitmap: bitmap to modify * @bits: number of bits * * Initializes all bits to zero. This also includes the overhead bits in the * last unsigned long which will not be used. */ static inline void bitmap_zero(unsigned long *bitmap, size_t bits) { memset(bitmap, 0, BITS_TO_LONGS(bits) * sizeof(unsigned long)); } int main(void) { for (int i = 1; i < MAX_TEST_BITS; i++) { bitmap_fill(test, i); assert(find_next_bit(test, i, 0) == 0); bitmap_zero(test, i); assert(find_next_bit(test, i, 0) == i); } return 0; } ``` 請研讀原始程式碼和對應註解,補完 `find_next_bit` 函式的程式碼,確保符合 Linux 核心的 `find_next_bit` 語意並可通過 `main` 函式給定的測試。應用到 `bitops_ffs` 和確保新增的程式碼行數儘可能少。 :::success 提交答覆時,需要列出完整的 `find_next_bit` 函式 ::: 參考資料: * [find_next_zero_bit](https://zhougy0717.github.io/2012/11/12/find_next_zero_bit/) * [Linux kernel: find_next_zero_bit](https://elixir.bootlin.com/linux/latest/ident/find_next_zero_bit) --- ### 測驗 `2` 承測驗 `1`,改寫 `main` 函式,強化測試的強度,允許檢驗隨機的 BITMAP test 組合 (也就是需要隨機變更 `test` 之位元內容) :::success 提交答覆時,需要列出完整的 `main` 函式 ::: --- ### 測驗 `3` 承測驗 `1`,用 C99 語法改寫 `bitops_ffs` 函式,使其不依賴 GNU extension,需要附上對應的測試程式碼。 :::success 提交答覆時,需要列出完整的 `bitops_ffs` 函式和新撰寫的 `main` 函式程式碼 ::: --- ### 測驗 `4` 承測驗 `3`,透過上述程式碼來實作 `bitops_ffz`: ```clike /** * bitops_ffz() - find (least significant) first zero bit plus one * @x: unsigned long to check * * Return: plus-one index of first zero bit; zero when x is ULONG_MAX */ static inline size_t bitops_ffs(unsigned long x) { ...待補... } ``` 需要附上對應的測試程式碼。 :::success 提交答覆時,需要列出完整的 `bitops_ffz` 函式和新撰寫的 `main` 函式程式碼 ::: ---