前言
记录一下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
。