iT邦幫忙

2023 iThome 鐵人賽

1
Software Development

深入淺出設計模式 - 使用 C++系列 第 37

《進階補充》 — Linux Kernel 中的經典 C Macro

  • 分享至 

  • xImage
  •  

這應該也算是一種 Pattern?

會想特別學習補充這些 Macro 除了本身做嵌入式系統,常寫 C 之外,還有前陣子去面試 NVIDIA System Software 被考了以下某一個 Macro 的實作...
所以認真看待各種巨集也是很重要的

背景知識

Preprocessor operators

  • Stringizing operator (#): 用來將引數變成一個字串
  • Charizing operator (#@): 用來將引數變成一個字元
  • Token-pasting operator (##): 用來拼接引數

Statement expressions

  • Statement expressions 是 GNU C 的一個特定擴展,允許在一個表達式的上下文中使用一個或多個語句
  • 這種特性在需要將多個操作封裝為單一表達式的場合特別有用,例如在 Macro 定義或條件表達式中
  • 特性
    • 類型推斷: Statement expression 的類型是由最後一個語句的類型決定的
    • 作用域: 在 statement expression 內部聲明的變量僅在該表達式內部有效
    • 返回值: 最後一個語句的值會成為整個 statement expression 的值
  • NOTE: (可攜性) 這是 GNU C 的特定擴展,不是標準 C 的一部分。它可能不會在所有 C 編譯器中工作

基本練習

// 很多 IC 廠軟韌必考這些 Bitwise 操作哦
#define SET_BIT(var, bit) ((var) |= (1 << (bit)))     // 用於設置變量中的特定位
#define CHECK_BIT(var, bit) ((var) & (1 << (bit)))    // 用於檢查變量中的特定位是否設置
#define CLEAR_BIT(var, bit) ((var) &= ~(1 << (bit)))  //用於清除變量中的特定位
#define TOGGLE_BIT(var, bit) ((var) ^= (1 << (bit)))  // 用於切換變量中的特定位

// 數學類,想要再進階點可以思考,遇到不同 Type 時可以如何優化?
#define ABS(x) ((x) < 0 ? -(x) : (x))     // 用於計算數字的絕對值
#define MAX(a, b) ((a) > (b) ? (a) : (b)) // 用於找出兩個數字中的最大值
#define MIN(a, b) ((a) < (b) ? (a) : (b)) // 用於找出兩個數字中的最小值

// 基本操作類
#define SWAP(a, b) do { a ^= b; b ^= a; a ^= b; } while (0)
#define UNUSED(x) (void)(x)  // 用於標記未使用的變量,以避免編譯器警告

// 字串類,常跟 print, uart, logging...之類的功能並用
#define STR(x) #x               // 用於將 Macro 參數轉換為字串
#define CONCAT(a, b) a ## b     // 用於連接兩個 Macro 參數

Macro in Linux Kernel

BIT

  • 用於設置位
#define BIT(nr)  (1UL << (nr))

ARRAY_SIZE

  • 計算數組的元素數量
/* &a[0] degrades to a pointer: a different type from an array */
#define __must_be_array(a) BUILD_BUG_ON_ZERO(__same_type((a), &(a)[0]))

#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))

SAFE_MALLOC

  • 這個 Macro 進行記憶體分配並檢查是否成功
#define SAFE_MALLOC(size) \
    ({ void *ptr = malloc(size); \
       if (!ptr) { \
           perror("Memory allocation failed"); \
           exit(EXIT_FAILURE); \
       } \
       ptr; })

offsetof

  • 計算結構體成員的偏移量
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)

container_of

  • 用於從成員指針獲取結構體指針
#define container_of(ptr, type, member) ({          \
    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
    (type *)( (char *)__mptr - offsetof(type,member) );})

likely 和 unlikely

  • 用於編譯器分支預測 (Branch Predict) 優化
    • __builtin_expect的主要功能是:幫助編譯器判斷條件跳轉的預期值,避免因執行jmp跳轉指令造成時間浪費
    • 編譯器優化時,依照條件跳躍的預期值,按正確地順序產生組譯程式碼
      • 把「很有可能發生」的條件分支放在順序執行指令段,而不是jmp指令段(jmp指令會打亂CPU 的指令執行順序,大大影響CPU指令執行效率)
  • 呼叫likely()或unlikely()告訴編譯器這個條件很有可能或不太有可能發生
#define likely(x)       __builtin_expect(!!(x), 1)
#define unlikely(x)     __builtin_expect(!!(x), 0)

// 告訴編譯器 a == 2 不容易發生,可以優化 Assembly 的 jump 指令
if (unlikely (a == 2))
      a++;
else
      a--;

list_entry

#define list_entry(ptr, type, member) \
    container_of(ptr, type, member)

list_for_each_entry

  • 用於遍歷鏈表
#define list_for_each_entry(pos, head, member) \
    for (pos = list_entry((head)->next, typeof(*pos), member); \
         &pos->member != (head); \
         pos = list_entry(pos->member.next, typeof(*pos), member))

BUG_ON

  • 在條件為真時觸發 bug。
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)

BUILD_BUG_ON

  • 在編譯時檢查條件
#define BUILD_BUG_ON(condition) ((void)sizeof(char[1 - 2*!!(condition)]))

WARN_ON

  • 在條件為真時發出警告
#define WARN_ON(condition) (unlikely((condition)) ? WARN_ON__ : 0)

READ_ONCE 和 WRITE_ONCE

  • 用於讀寫變量,防止編譯器優化
#define READ_ONCE(x)  (*(volatile typeof(x) *)&(x))
#define WRITE_ONCE(x, val)  do { (*(volatile typeof(x) *)&(x)) = (val); } while (0)

rcu_dereference

  • 用於 RCU 解引用
#define rcu_dereference(p)     ({ \
    typeof(*p) *_________p1 = READ_ONCE(p); \
    smp_read_barrier_depends(); \
    (_________p1); \
})

spin_lock 和 spin_unlock

  • 用於實現自旋鎖
#define spin_lock(lock)    _raw_spin_lock(lock)
#define spin_unlock(lock)  _raw_spin_unlock(lock)

MEASURE_TIME

// 記得 #include <time.h>
#define MEASURE_TIME(expr) \
    ({ clock_t start = clock(); \
       expr; \
       clock_t end = clock(); \
       double elapsed = ((double) (end - start)) / CLOCKS_PER_SEC; \
       elapsed; })

Reference

  1. https://meetonfriday.com/posts/b7efb858/
  2. https://www.tutorialspoint.com/cprogramming/c_preprocessors.htm
  3. https://zhuanlan.zhihu.com/p/30583695
  4. http://velep.com/archives/795.html

上一篇
《補充》 — 5 Classic Caching Strategies
系列文
深入淺出設計模式 - 使用 C++37
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言