美文网首页
Linux kernel中常见的宏整理

Linux kernel中常见的宏整理

作者: Then丶 | 来源:发表于2019-11-29 09:22 被阅读0次

    0x0 宏的基本知识

    // 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宏

    "##"用于粘贴两个参数,"#"用于替换参数

    1#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宏

    1#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) 宏

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

    检查表达式e是否为0为0编译通过且返回0;如果不为0,则编译不通过.

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

    如果e为0,则该结构体拥有一个int型的数据域,并且规定它所占的位的个数为0。

    1struct { 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

    1int __builtin_types_compatible_p(type_a, type_b);

    描述:此函数为GNU扩展,用来判断两个类型是否相同,如果type_a与 type_b相同的话,就会返回1,否则的话,返回0。

    1int __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字节对齐。

    类似:

    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() 的两个条件是:

    在无锁的情况下访问全局变量;

    对该变量的访问可能被编译器优化成合并成一次或者拆分成多次

    1#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 

    #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)))

    原文:https://bbs.pediy.com/thread-255903.htm

    相关文章

      网友评论

          本文标题:Linux kernel中常见的宏整理

          本文链接:https://www.haomeiwen.com/subject/akjwwctx.html