[linux小水滴]kernel源码中常见函数

1 clamp()

float clamp(float minnumber, float maxnumber, float parameter)

最小数值和最大数值指定返回值的范围。
parameter值要限制在范围内的属性或变量。
如果参数位于最小数值和最大数值之间的数值范围内,则该函数将返回参数值。
如果参数大于范围,该函数将返回最大数值。
如果参数小于范围,该函数将返回最小数值。

clamp(4,6,22)
//返回 6,因为 22 大于 6 而 6 是范围的最大数值。
clamp(4,6,2)
//返回 4,因为 2 小于 4 而 4 是范围的最小数值。
clamp(4,6,5)
//返回 5,因为该数值位于范围内。
Ball.scaleY = clamp(0,3,time);
//每次执行表达式都返回介于 0 到 3 之间的值。

2 INIT_LIST_HEAD()

这是一个很基础的接口,在内核中随处可见。

//kernel5.4/include/linux/list.h
static inline void INIT_LIST_HEAD(struct list_head *list)
{
	WRITE_ONCE(list->next, list);
	list->prev = list;
}

这个接口是对链表的初始化。
首先调用WRITE_ONCE()以确保在多线程的情况下,数据可以被正确写入,此处调用WRITE_ONCE()让list->next指向自己,然后将list->prev也指向自己。
用法:
list_head变量一般包含在某数据结构中,这样就可以用链表将所有该类型的数据结构串起来。常见的链表初始化的方式有两种:(1)动态初始化 (2)静态创建
下面以fox数据结构为例,例举用INIT_LIST_HEAD初始化链表的两种方法1

/*** fox数据结构 ***/
struct fox {
	unsigned long tail_length;/*尾巴长度,以cm为单位*/
	unsigned long weight;/*重量,以千克为单位*/
	bool 		  is_fantastic;/*?*/
	struct list_head list;/*所有fox结构体形成链表*/
}/***(1)动态初始化 ***/
struct fox *red_fox;
red_fox = kmalloc(sizeof(*red_fox), GFP_KERNEL);
red_fox->tail_length = 40;
red_fox->weight = 6;
red_fox->is_fantastic = true;
INIT_LIST_HEAD(&red_fox->list);
/***(2)在编译期静态创建 ***/
struct fox red_fox = {
	.tail_length = 40;
	.weight = 6;
	.list = LIST_HEAD_INIT(red_fox.list);
};

3 container_of()

container_of(ptr, type, member)

在Linux内核中是一个常用的宏函数,用于从包含在某个结构中的指针获得结构本身的指针,通俗地讲就是通过结构体变量中某个成员的首地址进而获得整个结构体变量的首地址。
ptr:表示结构体中member的地址
type:表示结构体类型
member:表示结构体中的成员

4 of_property_count_strings()

static inline int of_property_count_strings(struct device_node *np,
                        const char *propname)
{
    return of_property_read_string_helper(np, propname, NULL, 0, 0);
}

参数 np 指向设备节点;propname 指向属性名字。
函数直接调用 of_property_read_string_helper() 函数获得 string-list 中字符串的个数。

5 devm_kzalloc() 和kzalloc()

函数 devm_kzalloc() 和kzalloc()一样都是内核内存分配函数,但是devm_kzalloc()是跟设备(device)有关的,当设备(device)被detached或者驱动(driver)卸载(unloaded)时,内存会被自动释放。另外,当内存不在使用时,可以使用函数devm_kfree()释放。而kzalloc()则需要手动释放(使用kfree()),但如果工程师检查不仔细,则有可能造成内存泄漏。

6 kmalloc()、kzalloc()、kcalloc()

kmalloc定义如下:

#include <linux/slab.h>
/**
 * kmalloc - allocate memory
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate.
 *
 * kmalloc is the normal method of allocating memory
 * for objects smaller than page size in the kernel.
 *
 * The @flags argument may be one of the GFP flags defined at
 * include/linux/gfp.h and described at
 * :ref:`Documentation/core-api/mm-api.rst <mm-api-gfp-flags>`
 *
 * The recommended usage of the @flags is described at
 * :ref:`Documentation/core-api/memory-allocation.rst <memory-allocation>`
 *
 * Below is a brief outline of the most useful GFP flags
 *
 * %GFP_KERNEL
 *	Allocate normal kernel ram. May sleep.
 *
 * %GFP_NOWAIT
 *	Allocation will not sleep.
 *
 * %GFP_ATOMIC
 *	Allocation will not sleep.  May use emergency pools.
 *
 * %GFP_HIGHUSER
 *	Allocate memory from high memory on behalf of user.
 *
 * Also it is possible to set different flags by OR'ing
 * in one or more of the following additional @flags:
 *
 * %__GFP_HIGH
 *	This allocation has high priority and may use emergency pools.
 *
 * %__GFP_NOFAIL
 *	Indicate that this allocation is in no way allowed to fail
 *	(think twice before using).
 *
 * %__GFP_NORETRY
 *	If memory is not immediately available,
 *	then give up at once.
 *
 * %__GFP_NOWARN
 *	If allocation fails, don't issue any warnings.
 *
 * %__GFP_RETRY_MAYFAIL
 *	Try really hard to succeed the allocation but fail
 *	eventually.
 */
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
	if (__builtin_constant_p(size)) {
#ifndef CONFIG_SLOB
		unsigned int index;
#endif
		if (size > KMALLOC_MAX_CACHE_SIZE)
			return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
		index = kmalloc_index(size);
		if (!index)
			return ZERO_SIZE_PTR;
		return kmem_cache_alloc_trace(
				kmalloc_caches[kmalloc_type(flags)][index],
				flags, size);
#endif
	}
	return __kmalloc(size, flags);
}

kzalloc()和kcalloc都是对kmalloc的间接调用,它们在分配内存后,对内存清空。kcalloc分配数组内存。

#include <linux/slab.h>

/******************kzalloc********************/
/**
 * kzalloc - allocate memory. The memory is set to zero.
 * @size: how many bytes of memory are required.
 * @flags: the type of memory to allocate (see kmalloc).
 */
static inline void *kzalloc(size_t size, gfp_t flags)
{
	return kmalloc(size, flags | __GFP_ZERO);
}

/******************kcalloc********************/
/**
 * kcalloc - allocate memory for an array. The memory is set to zero.
 * @n: number of elements.
 * @size: element size.
 * @flags: the type of memory to allocate (see kmalloc).
 */
static inline void *kcalloc(size_t n, size_t size, gfp_t flags)
{
	return kmalloc_array(n, size, flags | __GFP_ZERO);
}
/**
 * kmalloc_array - allocate memory for an array.
 * @n: number of elements.
 * @size: element size.
 * @flags: the type of memory to allocate (see kmalloc).
 */
static inline void *kmalloc_array(size_t n, size_t size, gfp_t flags)
{
	size_t bytes;
	if (unlikely(check_mul_overflow(n, size, &bytes)))
		return NULL;
	if (__builtin_constant_p(n) && __builtin_constant_p(size))
		return kmalloc(bytes, flags);
	return __kmalloc(bytes, flags);
}
//以上三种用kfree释放
void kfree(const void *objp);

7 pr_fmt()

在linux的许多驱动源码中,我们经常会看到在源文件的第一行引用pr_fmt(fmt)宏定义,比如像下面的情况:

#define pr_fmt(fmt) "Power allocator: " fmt
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

pr_fmt可以在该驱动的其他log前面加固定的信息。这里以gpio驱动(gpio_demo.c)为例,我们来看下打上这个log会输出了什么:

//以下三种方式
#define pr_fmt(fmt) "gpio_demo: " fmt //输出log加gpio_demo前缀(注意冒号后面的空格)
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt //和方式1相同
#define pr_fmt(fmt) KBUILD_MODNAME ":%s:%d: " fmt, __func__, __LINE__ //可另加行号
//gpio_demo.c中
//.....
static int gpio_demo_probe(struct platform_device *pdev) {
     struct device *dev = &pdev->dev;
     int ret = 0;
     int i = 0;
     int gpio = -1;
     gpio_demo_data_t *data = NULL;
     struct resource *res = NULL;
     u32 config, pud, drv;
 
     pr_info("%s enter.\n", __func__); //输出函数名,这一句打出来是gpio_demo_probe enter.
     //......
}
//.....
//三种方式的结果
//1
gpio_demo: gpio_demo_probe enter.

//2
gpio_demo: gpio_demo_probe enter.

//3
gpio_demo: gpio_demo_probe:87: gpio_demo_probe enter.

8 platform_get_resource()

在platform driver的probe()函数中经常会看到它,调用它来从pdev中获取io内存2。源码如下:

struct resource *platform_get_resource(struct platform_device *dev,
                                   unsigned int type, unsigned int num)
{
	int i;
		for (i = 0; i < dev->num_resources; i++) {
			struct resource *r = &dev->resource[i];//从第一份资源开搜索
			if (type == resource_type(r) && num-- == 0)//条件判断
				return r;//返回设备资源指针
		}
	return NULL;
}

驱动中一般通过如下代码拿到第一份资源:

struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

附加:这里的“设备资源”指的是什么?
设备资源指的是能够使这个设备工作的诸多外部条件,比如:GPIO、供电、时钟、内存、中断、虚拟地址空间等3

9 readl()和writel()

linux中readl()和writel()函数用于读写寄存器。
writel()往内存映射的 I/O 空间上写入 32 位数据(4字节)。

#include <asm/io.h> 
void writel (unsigned char data , unsigned short addr )

readl()从内存映射的 I/O 空间读取 32 位数据(4字节)。

#include <asm/io.h> 
unsigned char readl (unsigned int addr )

入口参数:addr 是 I/O 地址。
返回值 : 从 I/O 空间读取的数值。
附加:

unsigned char readb (unsigned int addr ) //读1byte
unsigned char readw (unsigned int addr ) //读4bytes
void writeb (unsigned char data, unsigned short addr) //写1byte
void writew (unsigned char data, unsigned short addr) //写4bytes

10 do_div()

内核的一种用于除法计算的函数,最终调用__div64_32(),函数计算(*n)/base的结果,函数返回除法的余数,商存在(*n)中。
我们拿到指针n,就相当于获得了商值。
源码如下:

//代码路径:v5.9.8 arch/arm/include/asm/div64.h
//The semantics of __div64_32() are:
uint32_t __div64_32(uint64_t *n, uint32_t base)
{
	uint32_t remainder = *n % base;
	*n = *n / base;
	return remainder;
}
/* In other words, a 64-bit dividend with a 32-bit divisor producing
 * a 64-bit result and a 32-bit remainder.  To accomplish this optimally
 * we override the generic version in lib/div64.c to call our __do_div64
 * assembly implementation with completely non standard calling convention
 * for arguments and results (beware).
 */
 #define do_div(n, base) __div64_32(&(n), base)

11 linux initcall机制

#define __define_initcall(fn, id) \
        static initcall_t __initcall_##fn##id __used \
        __attribute__((__section__(".initcall" #id ".init"))) = fn; \
        LTO_REFERENCE_INITCALL(__initcall_##fn##id)

/*
 * Early initcalls run before initializing SMP.
 *
 * Only for built-in code, not modules.
 */
#define early_initcall(fn)              __define_initcall(fn, early)

/*
 * A "pure" initcall has no dependencies on anything else, and purely
 * initializes variables that couldn't be statically initialized.
 *
 * This only exists for built-in code, not for modules.
 * Keep main.c:initcall_level_names[] in sync.
 */
#define pure_initcall(fn)               __define_initcall(fn, 0)

#define core_initcall(fn)               __define_initcall(fn, 1)
#define core_initcall_sync(fn)          __define_initcall(fn, 1s)
#define postcore_initcall(fn)           __define_initcall(fn, 2)
#define postcore_initcall_sync(fn)      __define_initcall(fn, 2s)
#define arch_initcall(fn)               __define_initcall(fn, 3)
#define arch_initcall_sync(fn)          __define_initcall(fn, 3s)
#define subsys_initcall(fn)             __define_initcall(fn, 4)
#define subsys_initcall_sync(fn)        __define_initcall(fn, 4s)
#define fs_initcall(fn)                 __define_initcall(fn, 5)
#define fs_initcall_sync(fn)            __define_initcall(fn, 5s)
#define rootfs_initcall(fn)             __define_initcall(fn, rootfs)
#define device_initcall(fn)             __define_initcall(fn, 6)
#define device_initcall_sync(fn)        __define_initcall(fn, 6s)
#define late_initcall(fn)               __define_initcall(fn, 7)
#define late_initcall_sync(fn)          __define_initcall(fn, 7s)
#define module_init(x) __initcall(x);
#define __initcall(fn) device_initcall(fn)

12 register_pm_notifier()

linux 在待机和唤醒时会发出一些事件,通过注册接口函数register_pm_notifier()可以在接收到这些事件时做些处理。该接口在include/linux/suspend.h中声明,如下所示:

//include/linux/suspend.h
extern int register_pm_notifier(struct notifier_block *nb);
//其中notifier_block数据结构如下 include/linux/notifier.h
struct notifier_block {
	notifier_fn_t notifier_call;
	struct notifier_block __rcu *next;
	int priority;
};

我们来看一个实例:

//在thermal子系统中的应用,代码路径kernel5.4/drivers/thermal/thermal_core.c
/****************对各种事件的处理******************/
//每个case是对一种事件的处理
static int thermal_pm_notify(struct notifier_block *nb,
			     unsigned long mode, void *_unused)
{
	struct thermal_zone_device *tz;
	enum thermal_device_mode tz_mode;

	switch (mode) {
	case PM_HIBERNATION_PREPARE:
	case PM_RESTORE_PREPARE:
	case PM_SUSPEND_PREPARE:
		atomic_set(&in_suspend, 1);
		break;
	case PM_POST_HIBERNATION:
	case PM_POST_RESTORE:
	case PM_POST_SUSPEND:
		atomic_set(&in_suspend, 0);
		list_for_each_entry(tz, &thermal_tz_list, node) {
			tz_mode = THERMAL_DEVICE_ENABLED;
			if (tz->ops->get_mode)
				tz->ops->get_mode(tz, &tz_mode);

			if (tz_mode == THERMAL_DEVICE_DISABLED)
				continue;

			thermal_zone_device_init(tz);
			thermal_zone_device_update(tz,
						   THERMAL_EVENT_UNSPECIFIED);
		}
		break;
	default:
		break;
	}
	return 0;
}
/*******************给出事件处理回调函数*******************/
static struct notifier_block thermal_pm_nb = {
	.notifier_call = thermal_pm_notify,
};
/******************注册pm notify接口*********************/
static int __init thermal_init(void)
{
	//.....
	result = register_pm_notifier(&thermal_pm_nb);
	if (result)
		pr_warn("Thermal: Can not register suspend notifier, return %d\n",
			result);
	//.....
}

13 likely()和unlikely()

在kernel5.4中如下定义

//路径为:/linux/include/compiler.h
# define likely(x)	__builtin_expect(!!(x), 1)
# define unlikely(x)	__builtin_expect(!!(x), 0)

这两个函数意在告诉编译器,x通常情况下是为非零数或为零,这样有利于编译优化,比如:
if(likely(dev)) //认为dev通常为非零
if(unlikely(dev)) //认为dev通常为零

14 device_create()

创建device,通常情况下就是在sys/class下面会出现这里注册的设备。此函数完成的工作,主要体现在device_create_vargs中:
1.创建一个“dev file”,
2.注册这个设备,即填充device数据结构,返回该device数据结构的指针。
注意事项:调用该函数之前必须首先创建class。
函数源码如下:

/**
 * device_create - creates a device and registers it with sysfs
 * @class: pointer to the struct class that this device should be registered to
 * @parent: pointer to the parent struct device of this new device, if any
 * @devt: the dev_t for the char device to be added
 * @drvdata: the data to be added to the device for callbacks
 * @fmt: string for the device's name
 *
 * This function can be used by char device classes.  A struct device
 * will be created in sysfs, registered to the specified class.
 *
 * A "dev" file will be created, showing the dev_t for the device, if
 * the dev_t is not 0,0.
 * If a pointer to a parent struct device is passed in, the newly created
 * struct device will be a child of that device in sysfs.
 * The pointer to the struct device will be returned from the call.
 * Any further sysfs files that might be required can be created using this
 * pointer.
 *
 * Returns &struct device pointer on success, or ERR_PTR() on error.
 *
 * Note: the struct class passed to this function must have previously
 * been created with a call to class_create().
 */
struct device *device_create(struct class *class, struct device *parent,
			     dev_t devt, void *drvdata, const char *fmt, ...)
{
	va_list vargs;
	struct device *dev;
 
	va_start(vargs, fmt);
	dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
	va_end(vargs);
	return dev;
}

15 get_task_struct()

这个函数是一个宏函数,实际上做了一个原子加一的操作,让系统知道task usage加一。

//代码路径include/linux/sched.h
#define get_task_struct(tsk) do { atomic_inc(&(tsk)->usage); } while(0)

16 spin_lock_irqsave()

kernel version: 5.4

#define spin_lock_irqsave(lock, flags)				\
	do {								\
		raw_spin_lock_irqsave(spinlock_check(lock), flags);	\
	} while (0)
#define raw_spin_lock_irqsave(lock, flags)			\
	do {						\
		typecheck(unsigned long, flags);	\
		flags = _raw_spin_lock_irqsave(lock);	\
	} while (0)

17 typecheck(type,x)

宏typecheck用于检查x是否为type类型,如果不是会报warning: comparison of distinct pointer types lacks a cast。

//kernel5.4/include/linux/typecheck.h
/*
 * 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; \
})

18 静态per-CPU变量声明与定义

源码中经常会看到per-CPU变量,per_cpu的原理就是定义一个变量(类型可以自定义,一般是一个数据结构),该变量在所有CPU cache上都存一份,这样每次读写就可以避免锁开销,上下文切换和cache miss等一系列问题4 5
per-CPU按照存储变量的空间来源分为静态per-CPU变量和动态per-CPU变量,前者的存储空间是在代码编译时静态分配的,而后者的存储空间则是在代码的执行期间动态分配的。
下面简单介绍下静态per-CPU变量的声明和定义。内核源码示例:

//kernel5.4/lib/percpu_test.c
//相关编译宏CONFIG_PERCPU_TEST,该宏默认关闭
static DEFINE_PER_CPU(long, long_counter);

我们来看下DEFINE_PER_CPU的宏定义:

//kernel5.4/include/linux/percpu-defs.h
#define DEFINE_PER_CPU(type, name)					\
	DEFINE_PER_CPU_SECTION(type, name, "")

#define DEFINE_PER_CPU_SECTION(type, name, sec)				\
	__PCPU_ATTRS(sec) __typeof__(type) name

意思是,定义一个类型为“long”的per-CPU变量,该变量名为“long_counter”。

常见的per_cpu()宏定义如下:用来获得当前CPU的percpu变量的地址
per_cpu()—>per_cpu_ptr()
—>__verify_pcpu_ptr()
—>SHIFT_PERCPU_PTR()—>per_cpu_offset()

//kernel5.4/include/linux/percpu-defs.h
#define per_cpu_ptr(ptr, cpu)						\
({									\
	__verify_pcpu_ptr(ptr);						\
	SHIFT_PERCPU_PTR((ptr), per_cpu_offset((cpu)));			\
})
//调用__verify_pcpu_ptr(ptr)来将ptr转为const void __percpu *类型。
#define __verify_pcpu_ptr(ptr) \
do { \
 const void __percpu *__vpp_verify = (typeof((ptr) + 0))NULL; \
 (void)__vpp_verify; \
} while (0)

#define per_cpu_offset(x) (__per_cpu_offset[x])
//调用SHIFT_PERCPU_PTR宏,这个宏调用RELOC_HIDE宏,返回ptr+offset,从而得到所要访问的percpu变量的地址。
#define SHIFT_PERCPU_PTR(__p, __offset) \
 RELOC_HIDE((typeof(*(__p)) __kernel __force *)(__p), (__offset))

//kernel5.4/include/linux/percpu-defs.h
#define per_cpu(var, cpu)	(*per_cpu_ptr(&(var), cpu))

19 list_for_each_entry()宏

内核经常用该宏函数来遍历链表。定义如下:

//kernel5.4/include/linux/list.h
#define list_for_each_entry(pos, head, member)				\
	for (pos = list_first_entry(head, typeof(*pos), member);	\
	     &pos->member != (head);					\
	     pos = list_next_entry(pos, member))

#define list_first_entry(ptr, type, member) \
	list_entry((ptr)->next, type, member)

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

#define list_next_entry(pos, member) \
	list_entry((pos)->member.next, typeof(*(pos)), member)

for循环的三个条件语句:

  1. pos = list_first_entry(head, typeof(*pos), member)
    这是对pos变量的初始化,pos是个指针变量,其初始化调用流程list_first_entry()—>list_entry()—>container_of(head, type(*pos),member)
    可以看出这里实际上是获取包含链表头的父类型结构体指针,即pos。
  2. &pos->member != (head)
    停止遍历的条件:直到在此遍历至链表头为止。
  3. pos = list_next_entry(pos, member)
    同1,只不过是获取下一个节点的对象对应的父类型结构体指针,pos值被覆盖。

20 cpumask_intersects()

用于判断两个cpumask是否有交集,有交集返回1,无返回0。在内核中的定义如下:

//kernel5.4/include/linux/bitmap.h
/**
 * cpumask_intersects - (*src1p & *src2p) != 0
 * @src1p: the first input
 * @src2p: the second input
 */
static inline bool cpumask_intersects(const struct cpumask *src1p,
				     const struct cpumask *src2p)
{
	return bitmap_intersects(cpumask_bits(src1p), cpumask_bits(src2p),
						      nr_cpumask_bits);
}
cpumask_intersects()--->bitmap_intersects()--->__bitmap_intersects()

//kernel5.4/lib/bitmap.c
int __bitmap_intersects(const unsigned long *bitmap1,
			const unsigned long *bitmap2, unsigned int bits)
{
	unsigned int k, lim = bits/BITS_PER_LONG;
	for (k = 0; k < lim; ++k)
		if (bitmap1[k] & bitmap2[k])
			return 1;

	if (bits % BITS_PER_LONG)
		if ((bitmap1[k] & bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits))
			return 1;
	return 0;
}
EXPORT_SYMBOL(__bitmap_intersects);

21 cpumask_subset()

同20 cpumask_intersects(),只不过返回值刚好相反,有交集返回0,无返回1。

cpumask_subset()--->bitmap_subest()--->__bitmap_subset()
//kernel5.4/lib/bitmap.c
int __bitmap_subset(const unsigned long *bitmap1,
		    const unsigned long *bitmap2, unsigned int bits)
{
	unsigned int k, lim = bits/BITS_PER_LONG;
	for (k = 0; k < lim; ++k)
		if (bitmap1[k] & ~bitmap2[k])
			return 0;

	if (bits % BITS_PER_LONG)
		if ((bitmap1[k] & ~bitmap2[k]) & BITMAP_LAST_WORD_MASK(bits))
			return 0;
	return 1;
}
EXPORT_SYMBOL(__bitmap_subset);

22 cpumask_set_cpu()和cpumask_clear_cpu()

对某cpumask变量的第cpu位置1或清零。
内核中定义如下:

//kernel5.4/include/linux/cpumask.h
/**
 * cpumask_set_cpu - set a cpu in a cpumask
 * @cpu: cpu number (< nr_cpu_ids)
 * @dstp: the cpumask pointer
 */
static inline void cpumask_set_cpu(unsigned int cpu, struct cpumask *dstp)
{
	set_bit(cpumask_check(cpu), cpumask_bits(dstp));
}
/**
 * cpumask_clear_cpu - clear a cpu in a cpumask
 * @cpu: cpu number (< nr_cpu_ids)
 * @dstp: the cpumask pointer
 */
static inline void cpumask_clear_cpu(int cpu, struct cpumask *dstp)
{
	clear_bit(cpumask_check(cpu), cpumask_bits(dstp));
}

23 call_usermodehelper()

背景:内核中执行用户态程序或系统命令的方法:(1)用户态中,通过execev()实现;(2)内核态,通过调用call_usermodehelper()实现

24 kmemdup()

void *kmemdup(const void *src, size_t len, gfp_t gfp)

用于新申请一段内存,并将形参src中的内容复制到新申请的这段内存中。

25 __builtin_constant_p(n)

在查找cpumask_empty函数的过程中碰到了这个函数。
该函数是在编译的时候判断n是否是常量,是的话返回1,否则返回0。
此函数在kernel中找不到定义,因为它是一个内建函数,是编译器内部实现和使用,用于编译优化。
参考链接

26 cpu_online()

kernel 5.15
注意:test_bit
调用流程:

/**
 * cpumask_bits - get the bits in a cpumask
 * @maskp: the struct cpumask *
 *
 * You should only assume nr_cpu_ids bits of this mask are valid.  This is
 * a macro so it's const-correct.
 */
#define cpumask_bits(maskp) ((maskp)->bits)

//include/linux/cpumask.h
/*
 *     cpu_present_mask - has bit 'cpu' set iff cpu is populated
 *     cpu_online_mask  - has bit 'cpu' set iff cpu available to scheduler
 *  If !CONFIG_HOTPLUG_CPU, present == possible, and active == online.
*/
extern struct cpumask __cpu_online_mask;
#define cpu_online_mask	((const struct cpumask *)&__cpu_online_mask)

static inline void cpu_max_bits_warn(unsigned int cpu, unsigned int bits)
{
#ifdef CONFIG_DEBUG_PER_CPU_MAPS
	WARN_ON_ONCE(cpu >= bits);
#endif /* CONFIG_DEBUG_PER_CPU_MAPS */
}

/* verify cpu argument to cpumask_* operators */
static inline unsigned int cpumask_check(unsigned int cpu)
{
	cpu_max_bits_warn(cpu, nr_cpumask_bits);
	return cpu;
}

/**
 * cpumask_test_cpu - test for a cpu in a cpumask
 * @cpu: cpu number (< nr_cpu_ids)
 * @cpumask: the cpumask pointer
 *
 * Returns 1 if @cpu is set in @cpumask, else returns 0
 */
static inline int cpumask_test_cpu(int cpu, const struct cpumask *cpumask)
{
	return test_bit(cpumask_check(cpu), cpumask_bits((cpumask)));
}

static inline bool cpu_online(unsigned int cpu)
{
	return cpumask_test_cpu(cpu, cpu_online_mask);
}

//include/asm-generic/bitops/non-atomic.h
/**
 * arch_test_bit - Determine whether a bit is set
 * @nr: bit number to test
 * @addr: Address to start counting from
 */
static __always_inline int
arch_test_bit(unsigned int nr, const volatile unsigned long *addr)
{
	//addr数组的长度为nr/BITS_PER_LONG=nr/64(64位系统中),切记长度不可以越界
	return 1UL & (addr[BIT_WORD(nr)] >> (nr & (BITS_PER_LONG-1)));
}
#define test_bit arch_test_bit

//include/linux/bits.h
#define BIT_WORD(nr)		((nr) / BITS_PER_LONG)

//include/asm-generic/bitsperlong.h
#ifdef CONFIG_64BIT
#define BITS_PER_LONG 64
#else
#define BITS_PER_LONG 32
#endif /* CONFIG_64BIT */

  1. [参考《linux内核设计与实现(第三版)》P72] ↩︎

  2. LWN-the platform device API ↩︎

  3. wowo-Linux设备模型(9)_device resource management ↩︎

  4. per-CPU变量声明与定义 ↩︎

  5. wowotech-为何引入Per-CPU变量 ↩︎

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值