日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

Linux kernel中常见的宏整理

發布時間:2023/12/20 linux 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Linux kernel中常见的宏整理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

0x00 宏的基本知識

// object-like #define?宏名 替換列表 換行符 //function-like #define?宏名 ([標識符列表]) 替換列表 換行符

替換列表和標識符列表都是將字符串 token 化以后的列表。區別在于標識符列表使用,作為不同參數之間的分割符。每一個參數都是一個 token 化的列表。在宏中空白符只起到分割 token 的作用,空白符的多少對于預處理器是沒有意義的。

?

宏的一些奇技淫巧:

https://gaomf.cn/2017/10/06/C_Macro/

?

以下是整理的一些linux kernel中的常見宏,由于不同體系架構,或者不同模塊的宏定義不同,只挑選了其中容易看懂的宏作為記錄,實現的功能大體一樣。

?

Linux內核中do{...}while(0)意義:

  • 輔助定義復雜的宏,避免引用的時候出錯,如果不用{},if后面的語句只有第一條進行了判斷。同時避免宏展開后“;”造成編譯不通過.

  • 避免使用goto,對程序流進行統一的控制,使用break跳出

  • 避免空宏引起的warning

  • 定義一個單獨的函數塊來實現復雜的操作

0x01 常見宏整理

__CONCAT宏

"##"用于粘貼兩個參數,"#"用于替換參數:

#define __CONCAT(a, b) a ## b

BUG_ON(condition)

條件為真,產生崩潰, 原理:未定義的異常。


相對應的有 WARN_ON:

#define?BUG() assert(0) #define?BUG_ON(x) assert(!(x))/* Does it make sense to treat warnings as errors? */ #define?WARN() BUG() #define?WARN_ON(x) (BUG_ON(x), false)

BUILD_BUG_ON宏

#define?BUILD_BUG_ON(condition) ((void)sizeof(char[1?- 2*!!(condition)]))

!!(e) 對 e 的結果進行兩次求非。如果e為0,則結果為0;如果 e 不為 0,則結果為1。所以上述表達式的結果有兩種:

  • condition為真時,sizeof(char[-1]),產生錯誤,編譯不通過

  • condition為假時,sizeof(char[1]),編譯通過


  • BUILD_BUG_ON_ZERO(e) 宏

    #define?BUILD_BUG_ON_ZERO(e) (sizeof(struct?{ int:-!!(e); }))

    檢查表達式e是否為0,為0編譯通過且返回0;如果不為0,則編譯不通過。

    struct?{ int?: –!!(0); } -=> struct?{ int?: 0; }

    如果e為0,則該結構體擁有一個int型的數據域,并且規定它所占的位的個數為0。

    struct?{ int?: –!!(1); } -=> struct?{ int?: –1; }

    如果e非0,結構體的int型數據域的位域將變為一個負數,產生語法的錯誤。

    ?

    typeof獲得x的變量類型,根據傳入參數類型的不同,產生不同的行為,實現“編譯時多態”。實際typeof是在預編譯時處理,最后實際轉化為數據類型被編譯器處理。

    所以其中的表達式在運行時是不會被執行的,比如typeof(fun()),fun()函數是不會被執行的,typeof只是在編譯時分析得到了fun()的返回值而已。

    typeof還有一些局限性,其中的變量是不能包含存儲類說明符的,如static、extern這類都是不行的。

    typecheck宏

    宏typecheck用于檢查x是否為type類型,如果不是會拋出(warning: comparison of distinct pointer types lacks a cast),typecheck_fn用于檢查函數function是否為type類型,不一致跑出(warning: initialization from incompatible pointer type)。

    /** Check at compile time that something is of a particular type.* Always evaluates to 1 so you may use it easily in comparisons.*/ #define?typecheck(type,x) \ ({ type __dummy; \typeof(x) __dummy2; \(void)(&__dummy == &__dummy2); \1; \ }) /*GCC的一個擴展特性,形如({ ... })這樣的代碼塊會被視為一條語句, * 其計算結果是{ ... }中最后一條語句的計算結果。 * 所以上述會返回1 */ /** Check at compile time that 'function' is a certain type, or is a pointer* to that type (needs to use typedef for the function type.)*/ #define?typecheck_fn(type,function) \ ({ typeof(type) __tmp = function; \(void)__tmp; \ })

    min宏

    通過type進行隱式轉換安全通過編譯,否則會跑出warning:

    #define?min(x, y) __careful_cmp(x, y, <) #define?__cmp(x, y, op) ((x) op (y) ? (x) : (y)) #define?__safe_cmp(x, y) \(__typecheck(x, y) && __no_side_effects(x, y)) #define?__no_side_effects(x, y) \(__is_constexpr(x) && __is_constexpr(y))#define?__cmp_once(x, y, unique_x, unique_y, op) ({ \typeof(x) unique_x = (x); \typeof(y) unique_y = (y); \__cmp(unique_x, unique_y, op); }) /*重新賦值為了防止x++這種重復+1 */ #define?__careful_cmp(x, y, op) \__builtin_choose_expr(__safe_cmp(x, y), \ //比較x, y的類型__cmp(x, y, op), \ //x,y類型一樣時__cmp_once(x, y, __UNIQUE_ID(__x), __UNIQUE_ID(__y), op))//x, y類型不同時

    __UNIQUE_ID保證變量唯一。

    __is_constexpr宏

    判斷x是否為整數常量表達式:

    /** This returns a constant expression while determining if an argument is* a constant expression, most importantly without evaluating the argument.* Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de>*/ #define?__is_constexpr(x) \(sizeof(int) == sizeof(*(8?? ((void?*)((long)(x) * 0l)) : (int?*)8)))

    如果x是常量表達式,則(void )((long)(x)?0l)是一個空指針常量,就會使用第三個操作數即((int *)8)的類型。如果不是常量表達式,則會使用第二個操作數void類型。


    所以會出現以下兩種情況:

    sizeof(int) == sizeof(*((int?*) (NULL))) // if `x` was an integer constant expression sizeof(int) == sizeof(*((void?*)(....))) // otherwise

    因為sizeof(void) = 1,所以如果x是整數常量表達式,則宏的結果為1,否則為0。


    https://stackoverflow.com/questions/49481217/linux-kernels-is-constexpr-macro

    int??__builtin_types_compatible_p(type_a, type_b);

    描述:此函數為GNU擴展,用來判斷兩個類型是否相同,如果type_a與 type_b相同的話,就會返回1,否則的話,返回0。

    int?__builtin_choose_expr(exp, e1, e2);

    max宏

    同min 宏。

    roundup宏

    返回一個能夠整除y并且大于x,最接近x的值,向上取整,可用于地址的內存對齊:

    #define?roundup(x, y) ( \ { \const?typeof(y) __y = y; \(((x) + (__y - 1)) / __y) * __y; \ } \ )

    clamp 宏

    判斷val是否在lo和hi的范圍內,如果小于lo,返回lo,如果大于hi則返回hi,如果在lo和hi之間就返回val:

    /*** clamp - return?a?value clamped to?a?given range?with strict typechecking* @val:?current value* @lo: lowest allowable value* @hi: highest allowable value** This macro does strict typechecking of @lo/@hi to?make?sure they are of the* same type?as?@val. See the unnecessary pointer comparisons.*/ #define clamp(val, lo, hi) min((typeof(val))max(val, lo), hi)

    abs宏

    取絕對值:

    /*** abs - return absolute value of an argument* @x: the value. If it is unsigned type, it is converted to signed type first.* char is treated as if it was signed (regardless of whether it really is)* but the macro's return type is preserved as char.** Return: an absolute value of x.*/ #define abs(x) __abs_choose_expr(x, long long, \__abs_choose_expr(x, long, \__abs_choose_expr(x, int, \__abs_choose_expr(x, short, \__abs_choose_expr(x, char, \__builtin_choose_expr( \__builtin_types_compatible_p(typeof(x), char), \(char)({ signed?char?__x = (x); __x<0?-__x:__x; }), \((void)0)))))))#define __abs_choose_expr(x, type, other) __builtin_choose_expr( \__builtin_types_compatible_p(typeof(x), signed?type) || \__builtin_types_compatible_p(typeof(x), unsigned?type), \({ signed?type __x = (x); __x < 0?? -__x : __x; }), other)

    swap 宏

    利用typeof獲取要交換變量的類型:

    /** swap - swap value of @a?and @b*/ #define swap(a, b) \do?{ typeof(a) __tmp = (a); (a) = (b); (b) = __tmp; } while?(0)

    container_of宏

    根據一個結構體變量中的成員變量來獲取整個結構體變量的指針。

    #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) /*結構體地址為0,將member地址轉成size_t類型作為偏移 /*** container_of - cast a member of a structure out to the containing structure* @ptr: the pointer to the member.* @type: the type of the container struct this is embedded in.* @member: the name of the member within the struct.**/ #define container_of(ptr, type, member) ({ \const?typeof( ((type *)0)->member ) *__mptr = (ptr); \ //*__mptr保存該member變量的指針(type *)( (char *)__mptr - offsetof(type,member) );}) //變量指針減去自身偏移得到指向結構體的指針

    likely和unlikely宏

    把分支預測的信息提供給編譯器,以降低因為指令跳轉帶來的分支下降:

    #define?likely(x) __builtin_exp?ect(!!(x), 1) #define?unlikely(x) __builtin_exp?ect(!!(x), 0)

    GCC的內建方法會判斷 EXP == C 是否成立,成立則將if分支中的執行語句緊跟放在匯編跳轉指令之后,否則將else分支中的執行語句緊跟匯編跳轉指令之后。

    這樣cache在預取數據時就可以將分支后的執行語句放在cache中,提高cache的命中率。

    ?

    http://www.169it.com/article/17243108930910839727.html

    ALIGN對齊宏

    對齊是采用上對齊的方式,例如0x123以16對齊,結果是0x130,因為對齊常在分配內存時使用,所以分配的要比需要的大。

    #define?ALIGN(x, a) __ALIGN_KERNEL((x), (a)) #define?__ALIGN_KERNEL(x, a) __ALIGN_KERNEL_MASK(x, (typeof(x))(a) - 1) #define?__ALIGN_KERNEL_MASK(x, mask) (((x) + (mask)) & ~(mask)) #define?__ALIGN_MASK(x, mask) __ALIGN_KERNEL_MASK((x), (mask))

    __get_unaligned_le(ptr)宏

    獲取未對齊的數據,主要是識別數據大小:

    #define?__get_unaligned_le(ptr) ((__force typeof(*(ptr)))({ \__builtin_choose_expr(sizeof(*(ptr)) == 1, *(ptr), \__builtin_choose_expr(sizeof(*(ptr)) == 2, get_unaligned_le16((ptr)), \__builtin_choose_expr(sizeof(*(ptr)) == 4, get_unaligned_le32((ptr)), \__builtin_choose_expr(sizeof(*(ptr)) == 8, get_unaligned_le64((ptr)), \__bad_unaligned_access_size())))); \}))static?inline?u32 get_unaligned_be32(const?void?*p) {return?__get_unaligned_cpu32((const?u8 *)p); }static?inline?u32 __get_unaligned_cpu32(const?void?*p) {const?struct?__una_u32?*ptr?= (const?struct?__una_u32?*)p;return?ptr->x; }struct?__una_u16?{?u16 x; } __packed; struct?__una_u32?{?u32 x; } __packed; struct?__una_u64?{?u64 x; } __packed;

    編譯器默認會對結構體采用字節對齊的方式,__packed關鍵字可以取消字節對齊,采用1字節對齊。


    類似:

    #pragma?pack (1)

    __put_unaligned_le宏

    寫入未對齊的數據。

    #define?__put_unaligned_le(val, ptr) ({ \void?*__gu_p = (ptr); \switch?(sizeof(*(ptr))) { \case?1: \*(u8 *)__gu_p = (__force u8)(val); \break; \case?2: \put_unaligned_le16((__force u16)(val), __gu_p); \break; \case?4: \put_unaligned_le32((__force u32)(val), __gu_p); \break; \case?8: \put_unaligned_le64((__force u64)(val), __gu_p); \break; \default: \__bad_unaligned_access_size(); \break; \} \(void)0; })static?inline void?put_unaligned_be32(u32 val, void?*p) {__put_unaligned_cpu32(val, p); }static?inline void?__put_unaligned_cpu32(u32 val, void?*p) {struct?__una_u32 *ptr = (struct?__una_u32 *)p;ptr->x = val; }

    ACCESS_ONCE 宏

    訪問目標地址一次,先取得x的地址,然后把這個地址轉換成一個指向這個地址類型的指針,然后再取得這個指針所指向的內容,達到了訪問一次的目的。volatile表示不進行優化,強制訪問一次。

    在一些并發的場景中對變量進行優化有可能導致錯誤,需要時刻得到變量的最新值,所以用volatile強制訪問一次進行更新。


    使用 ACCESS_ONCE() 的兩個條件是:

  • 在無鎖的情況下訪問全局變量

  • 對該變量的訪問可能被編譯器優化成合并成一次或者拆分成多次

  • #define?ACCESS_ONCE(x) (*(volatile typeof(x) *)&(x))

    https://blog.csdn.net/ganggexiongqi/article/details/24603363


    ACCESS_OK宏

    CVE-2017-5123(waitid系統調用),檢查指針是不是屬于用戶空間的,x86架構下ACCESS_OK宏的實現:

    /*** access_ok: - Checks if a user space pointer is valid* @addr: User space pointer to start of block to check* @size: Size of block to check** Context: User context only. This function may sleep if pagefaults are* enabled.** Checks if a pointer to a block of memory in user space is valid.** Returns true (nonzero) if the memory block may be valid, false (zero)* if it is definitely invalid.** Note that, depending on architecture, this function probably just* checks that the pointer is in the user space range - after calling* this function, memory access functions may still return -EFAULT.*/ #define?access_ok(addr, size) \ ({ \WARN_ON_IN_IRQ(); \likely(!__range_not_ok(addr, size, user_addr_max())); \ }) /*__range_not_ok返回0才能驗證通過#define __range_not_ok(addr, size, limit) \ ({ \__chk_user_ptr(addr); \__chk_range_not_ok((unsigned long __force)(addr), size, limit); \ })/** Test whether a block of memory is a valid user space address.* Returns 0 if the range is valid, nonzero otherwise.*/ static?inline?bool?__chk_range_not_ok(unsigned?long?addr, unsigned?long?size, unsigned?long?limit) {/** If we have used "sizeof()" for the size,* we know it won't overflow the limit (but* it might overflow the 'addr', so it's* important to subtract the size from the* limit, not add it to the address).*/if?(__builtin_constant_p(size))return?unlikely(addr > limit - size);/*__builtin_constant_p判斷編譯時是否為常數,如果是則返回1 *//* Arbitrary sizes? Be careful about overflow */addr += size;if?(unlikely(addr < size))return?true;return?unlikely(addr > limit); }

    mdelay宏

    忙等待函數,在延遲過程中無法運行其他任務,會占用CPU時間,延遲時間是準確的。


    msleep是休眠函數,它不涉及忙等待.用msleep(200)的時候實際上延遲的時間,大部分時候是要多于200ms,是個不定的時間值。

    #define?MAX_UDELAY_MS 5 #define?mdelay(n) (\ /*延遲毫秒級*/(__builtin_constant_p(n) && (n)<=MAX_UDELAY_MS) ? udelay((n)*1000) : \({unsigned long?__ms=(n); while?(__ms--) udelay(1000);}))static?void?udelay(int?loops) /*延遲微秒級 */ {while?(loops--)io_delay(); /* Approximately 1 us */ }static?inline void?io_delay(void) {const?u16 DELAY_PORT = 0x80;asm volatile("outb %%al,%0"?: : "dN"?(DELAY_PORT)); } /*對 I/O 端口 0x80 寫入任何的字節都將得到 1 us 的延時*/

    系統調用宏

    linux 內核中最常見的宏使用之一,系統調用:

    #define?SYSCALL_DEFINE1(name, ...) SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) #define?SYSCALL_DEFINE2(name, ...) SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) #define?SYSCALL_DEFINE3(name, ...) SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) #define?SYSCALL_DEFINE4(name, ...) SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) #define?SYSCALL_DEFINE5(name, ...) SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) #define?SYSCALL_DEFINE6(name, ...) SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) /*…:省略號代表可變的部分,用__VA_AEGS__ 代表省略的變長部分*/ #define?SYSCALL_DEFINE_MAXARGS????6??/*系統調用最多可以帶6個參數*/

    以open系統調用為例:


    SYSCALL_DEFINE 后面跟系統調用所帶的參數個數n,第一個參數為系統調用的名字,然后接2*n個參數,每一對指明系統調用的參數類型及名字。

    SYSCALL_DEFINE3(open, const?char?__user *, filename, int, flags, umode_t, mode) {if?(force_o_largefile())flags |= O_LARGEFILE;return?do_sys_open(AT_FDCWD, filename, flags, mode); } SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode) 展開之后是: SYSCALL_DEFINEx(3, _open, __VA_ARGS__) 再次展開為: __SYSCALL_DEFINEx(3, _open, __VA_ARGS__) #define?__SYSCALL_DEFINEx(x, name, ...) \asmlinkage?long?sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) \ 最后展開為: asmlinkage?long?sys_open(__MAP(3,__SC_DECL,__VA_ARGS__))#define?__MAP0(m,...) #define?__MAP1(m,t,a) m(t,a) #define?__MAP2(m,t,a,...) m(t,a), __MAP1(m,__VA_ARGS__) #define?__MAP3(m,t,a,...) m(t,a), __MAP2(m,__VA_ARGS__) #define?__MAP4(m,t,a,...) m(t,a), __MAP3(m,__VA_ARGS__) #define?__MAP5(m,t,a,...) m(t,a), __MAP4(m,__VA_ARGS__) #define?__MAP6(m,t,a,...) m(t,a), __MAP5(m,__VA_ARGS__) #define?__MAP(n,...) __MAP##n(__VA_ARGS__)#define?__SC_DECL(t, a) t?a__MAP(3,__SC_DECL,__VA_ARGS__) -->__MAP3(__SC_DECL,const char __user *, filename, int, flags, umode_t, mode) -->__SC_DECL(const char __user *, filename), __MAP2(__SC_DECL,__VA_ARGS__) -->const?char?__user?* filename,__SC_DECL(int, flags),__MAP1(__SC_DECL,__VA_ARGS__) -->const?char?__user?* filename, int?flags, __SC_DECL(umode_t, mode) -->const?char?__user?* filename, int?flags, umode_t?mode最后調用asmlinkage?long?sys_open(const char __user *filename,int flags, umode_t mode);

    為什么要將系統調用定義成宏?CVE-2009-0029,CVE-2010-3301,Linux 2.6.28及以前版本的內核中,將系統調用中32位參數傳入64位的寄存器時無法作符號擴展,可能導致系統崩潰或提權漏洞。


    內核開發者通過將系統調用的所有輸入參數都先轉化成long類型(64位),再強制轉化到相應的類型來規避這個漏洞。

    asmlinkage long?__se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ { \long ret = __do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\__MAP(x,__SC_TEST,__VA_ARGS__); \__PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \return ret; \ } \#define?__TYPE_AS(t, v) __same_type((__force t)0, v) /*判斷t和v是否是同一個類型*/ #define?__TYPE_IS_L(t) (__TYPE_AS(t, 0L)) /*判斷t是否是long 類型,是返回1*/ #define?__TYPE_IS_UL(t) (__TYPE_AS(t, 0UL)) /*判斷t是否是unsigned long 類型,是返回1*/ #define?__TYPE_IS_LL(t) (__TYPE_AS(t, 0LL) || __TYPE_AS(t, 0ULL))/*是long類型就返回1*/ #define?__SC_LONG(t, a) __typeof(__builtin_choose_expr(__TYPE_IS_LL(t), 0LL, 0L)) a /*將參數轉換成long類型*/ #define?__SC_CAST(t, a) (__force t) a /*轉成成原來的類型*/ # define?__force __attribute__((force)) 表示所定義的變量類型可以做強制類型轉換

    time_after32(a, b)宏和time_before32(b, a)宏

    time_after32(a, b)宏:返回true時,說明time a比b大,在后面。

    ?

    time_before32(b, a)宏:返回true時,說明time b在a前。


    只比較兩個32位的數:

    /*** time_after32 - compare two 32-bit relative times* @a: the time which may be after @b* @b: the time which may be before @a** time_after32(a, b) returns true if the time @a?is after time @b.* time_before32(b, a) returns true if the time @b?is before time @a.** Similar to time_after(), compare two 32-bit timestamps for relative* times. This is useful for comparing 32-bit seconds values that can't* be converted to 64-bit values (e.g. due to disk format or wire protocol* issues) when it is known that the times are less than 68 years apart.*/ #define time_after32(a, b) ((s32)((u32)(b) - (u32)(a)) < 0) #define time_before32(b, a) time_after32(a, b)

    barrier()宏

    內存屏障,該語句不產生任何代碼,但是執行后刷新寄存器對變量的分配。

    /* Optimization barrier */ /* The "volatile" is due to gcc bugs */ #define?barrier() __asm__?__volatile__("": : :"memory")

    執行該語句后cpu中的寄存器和cache中已緩存的數據將作廢,重新讀取內存中的數據。這就阻止了cpu將寄存器和cache中的數據用于去優化指令,而避免去訪問內存。例如:

    int?a = 5, b = 6; barrier(); a = b;

    第三行中,GCC不會用存放b的寄存器給a賦值,而是invalidate b 的cache line,重新讀取內存中的b值給a賦值。

    ?

    另外的內存屏障宏定義:

    • mfence:在mfence指令前的讀寫操作當必須在mfence指令后的讀寫操作前完成。

    • lfence:在lfence指令前的讀操作當必須在lfence指令后的讀操作前完成,不影響寫操作

    • sfence:在sfence指令前的寫操作當必須在sfence指令后的寫操作前完成,不影響讀操作

    • lock 前綴(或cpuid、xchg等指令)使得本CPU的Cache寫入內存,該寫入動作也會引起別的CPU invalidate其Cache。用來修飾當前指令操作的內存只能由當前CPU使用

    內存對于緩存更新策略,要區分Write-Through和Write-Back兩種策略。前者更新內容直接寫內存并不同時更新Cache,但要置Cache失效,后者先更新Cache,隨后異步更新內存。通常X86 CPU更新內存都使用Write-Back策略。

    #ifdef?ASSEMBLY宏

    一些常量宏同時在匯編和C中使用,然而,我們不能像注釋C的常量宏那樣加一個“UL”或其他后綴。所以我們需要使用以下的宏解決這個問題。

    ?

    例如調用:#define DEMO_MACRO _AT(1, UL):在C中會被解釋為 #define DEMO_MACRO 1UL; 而在匯編中什么都不做,就是:#define DEMO_MACRO 1

    #ifdef __ASSEMBLY__ #define _AC(X,Y) X #define _AT(T,X) X #else #define __AC(X,Y) (X##Y) #define _AC(X,Y) __AC(X,Y) #define _AT(T,X) ((T)(X)) #endif#define _UL(x) (_AC(x, UL)) #define _ULL(x) (_AC(x, ULL))

    force_o_largefile宏

    判斷是否支持大文件。

    #define?force_o_largefile() \(personality(current->personality) != PER_LINUX32)PER_LINUX32 = 0x0008, PER_MASK = 0x00ff,/*,* Return the base personality without flags.*/ #define?personality(pers) (pers & PER_MASK)

    邏輯地址和物理地址互相轉換

    #define?__pa(x) __virt_to_phys((unsigned long)(x)) #define?__va(x) ((void *)__phys_to_virt((unsigned long)(x)))

    錯誤碼相關的宏

    linux 內核的一些錯誤碼,以它們的負數來作為函數返回值,簡單地使用大于等于-4095的虛擬地址來分別表示相應的錯誤碼。

    在32位系統上,-4095轉換成unsigned long類型的值為0xFFFFF001,也就是說地址區間[0xFFFFF001, 0xFFFFFFFF]被分別用來表示錯誤碼從-4095到-1。

    ?

    判斷一個函數返回的指針到底是有效地址還是錯誤碼:

    #define?MAX_ERRNO 4095#define?IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)static?inline?long?__must_check IS_ERR(const?void?*ptr) {return?IS_ERR_VALUE((unsigned?long)ptr); }

    錯誤碼與相應地址的互換:

    static?inline?void?* __must_check ERR_PTR(long?error) {return?(void?*) error; } 長整型轉化為指針static?inline?long?__must_check PTR_ERR(const?void?*ptr) {return?(long) ptr; } 指針轉化為長整型

    額外有意思的宏

    遞歸宏,顛倒字節:

    #define?BSWAP_8(x) ((x) & 0xff) #define?BSWAP_16(x) ((BSWAP_8(x) << 8) | BSWAP_8((x) >> 8)) #define?BSWAP_32(x) ((BSWAP_16(x) << 16) | BSWAP_16((x) >> 16)) #define?BSWAP_64(x) ((BSWAP_32(x) << 32) | BSWAP_32((x) >> 32))

    交換宏,不需要額外定義變量

    #define?swap(a, b) \ (((a) ^= (b)), ((b) ^= (a)), ((a) ^= (b)))


    - End -


    掃碼或長按關注

    回復「?籃球的大肚子」進入技術群聊

    總結

    以上是生活随笔為你收集整理的Linux kernel中常见的宏整理的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。