Linux kernel: Misc

前言

记录一下Linux内核中的杂项,希望能帮到你理解源码。

内核宏

container_of

linux-5.15.54-xanmod源码中使用rg container_of | wc -l一共搜索到了18273次使用。

Linux中常常传入一个结构体的成员,但是想要得到所属结构体及其里面的其它成员,可以使用这个内核宏。

其定义在<linux/kernel.h>

/**
 * 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) ({              \
    void *__mptr = (void *)(ptr);                   \
    BUILD_BUG_ON_MSG(!__same_type(*(ptr), ((type *)0)->member) &&    \
             !__same_type(*(ptr), void),            \
             "pointer type mismatch in container_of()");  \
    ((type *)(__mptr - offsetof(type, member))); })

C语言中({})用于返回最后一个表达式的值(dejavu! Scala中的function返回的方法很像这个)

offsetof是编译器的方法,用于返回成员在结构体中的偏移,那么成员的指针减去这个偏移,就找到了这个结构体。

access_ok

不同架构之间有所区别,以x86为例:

/*
 * 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);

    /* Arbitrary sizes? Be careful about overflow */
    addr += size;
    if (unlikely(addr < size))
        return true;
    return unlikely(addr > limit);
}

#define __range_not_ok(addr, size, limit)               \
({                                  \
    __chk_user_ptr(addr);                       \
    __chk_range_not_ok((unsigned long __force)(addr), size, limit); \
})

#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
static inline bool pagefault_disabled(void);
# define WARN_ON_IN_IRQ()   \
    WARN_ON_ONCE(!in_task() && !pagefault_disabled())
#else
# define WARN_ON_IN_IRQ()
#endif

/**
 * 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.
 *
 * 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.
 *
 * Return: true (nonzero) if the memory block may be valid, false (zero)
 * if it is definitely invalid.
 */
#define access_ok(addr, size)                   \
({                                  \
    WARN_ON_IN_IRQ();                       \
    likely(!__range_not_ok(addr, size, TASK_SIZE_MAX));     \
})

其中 __chk_user_ptr 定义于include/linux/compiler_types.h中,用于检查指针是否指向用户空间,但是一般为空。

# define __chk_user_ptr(x)  (void)0

标识

__randomize_layout

在Linux kernel结构体声明最后,经常会看见有一个__randomize_layout标识,这个是内核的结构体随机化,编译器的功能,用于增加内核的安全性。

randomized_struct_fields_{start, end}

有些共享的元素不用随机化地址,那就把剩余的加进一个随机的随机化地址的结构体中。

#define randomized_struct_fields_start  struct {
#define randomized_struct_fields_end    } __randomize_layout;

____cacheline_aligned____cacheline_aligned

在多核环境下,不同的线程或者进程访问同一cacheline的不同内容,就会发生“伪共享问题”,这时会发生数据结构所有部分都会被认为是已经被更改,但是事实上并未改变的情况。需要讲cachelinux大小对齐到相应的数据结构。

前者用于局部数据,后者用于全局数据。

likely() 与 unlikely()

定义于include/linux/compiler.h中:

# define likely(x)  __builtin_expect(!!(x), 1)
# define unlikely(x)    __builtin_expect(!!(x), 0)

__builtin_expect是GCC引入的指令,作用是允许程序员将最有可能执行的分支告诉编译器。

两个!是为了把非0值转换成1,而0值还是0。
因为C语言中,所有非0值都表示真。所以!非0值 == 0,而!0 == 1

知识共享许可协议
本作品采用知识共享署名-相同方式共享 4.0 国际许可协议进行许可。
上一篇
下一篇