内核中current函数分析

本文详细解释了Linux内核中用于获取处理器特定current_task值的内联汇编代码,展示了如何通过__pcpu_stable_op等宏进行高效且无需同步的内存访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

作用:

这个宏定义用于获取当前正在运行的进程(或线程)的 task_struct 结构体指针

解释:

  • current: 这是一个宏,它会被替换成 get_current() 函数的返回值。
  • get_current(): 这是一个内核函数,它负责获取当前正在执行代码的进程(或线程)的 task_struct 结构体指针,并返回该指针。

task_struct 结构体:

在 Linux 内核中,每个进程(或线程)都用一个 task_struct 结构体来表示,该结构体包含了进程的所有信息,例如:

  • 进程 ID
  • 进程状态
  • 进程优先级
  • 进程内存空间
  • 打开的文件描述符
  • ...

 

current定义:

#define current get_current()

 get_current()定义:

static __always_inline struct task_struct *get_current(void)
{
	return this_cpu_read_stable(pcpu_hot.current_task);
}

 this_cpu_read_stable定义:

#define this_cpu_read_stable(pcp) \
         __pcpu_size_call_return(this_cpu_read_stable_, pcp)

 __pcpu_size_call_return定义:

#define __pcpu_size_call_return(stem, variable)				\
({									\
	typeof(variable) pscr_ret__;					\
	__verify_pcpu_ptr(&(variable));					\
	switch(sizeof(variable)) {					\
	case 1: pscr_ret__ = stem##1(variable); break;			\
	case 2: pscr_ret__ = stem##2(variable); break;			\
	case 4: pscr_ret__ = stem##4(variable); break;			\
	case 8: pscr_ret__ = stem##8(variable); break;			\
	default:							\
		__bad_size_call_parameter(); break;			\
	}								\
	pscr_ret__;							\
})

展开上面的宏:


{
	typeof(pcpu_hot.current_task) pscr_ret__;
	__verify_pcpu_ptr(&(pcpu_hot.current_task));
	switch(sizeof(pcpu_hot.current_task)) {
	case 1: pscr_ret__ = this_cpu_read_stable_1(pcpu_hot.current_task); break;
	case 2: pscr_ret__ = this_cpu_read_stable_2(pcpu_hot.current_task); break;
	case 4: pscr_ret__ = this_cpu_read_stable_4(pcpu_hot.current_task); break;
	case 8: pscr_ret__ = this_cpu_read_stable_8(pcpu_hot.current_task); break;
	default:
		__bad_size_call_parameter(); break;
	pscr_ret__;
}

在32位系统中,指针的长度为4,再次简化上面的代码得到

{
	task_struct * pscr_ret__;
	__verify_pcpu_ptr(&(pcpu_hot.current_task));
	pscr_ret__ = this_cpu_read_stable_4(pcpu_hot.current_task);
	pscr_ret__;
}

接下来我们只需要分析this_cpu_read_stable_4这个函数:

this_cpu_read_stable_4定义:

#define this_cpu_read_stable_4(pcp)	percpu_stable_op(4, "mov", pcp)

percpu_stable_op定义:

#define percpu_stable_op(size, op, _var)				\
({									\
	__pcpu_type_##size pfo_val__;					\
	asm(__pcpu_op2_##size(op, __percpu_arg(P[var]), "%[val]")	\
	    : [val] __pcpu_reg_##size("=", pfo_val__)			\
	    : [var] "p" (&(_var)));					\
	(typeof(_var))(unsigned long) pfo_val__;			\
})

将上面的宏转成语句为:

{
	__pcpu_type_4 pfo_val__;
	asm(__pcpu_op2_4("mov", __percpu_arg(P[var]), "%[val]")
	    : [val] __pcpu_reg_4("=", pfo_val__)
	    : [var] "p" (&(pcpu_hot.current_task)));
	(typeof(pcpu_hot.current_task))(unsigned long) pfo_val__;
}

 __pcpu_type_4定义:

#define __pcpu_type_4 u32

__percpu_arg定义:

#define __percpu_seg		gs
#define __stringify(x) #x
#define __percpu_prefix		"%%"__stringify(__percpu_seg)":"
#define __percpu_arg(x)		__percpu_prefix "%" #x


static inline u16 gs(void)
{
	u16 seg;
	asm volatile("movw %%gs,%0" : "=rm" (seg));
	return seg;
}

 __pcpu_op2_4定义:

#define __pcpu_op2_4(op, src, dst) op "l " src ", " dst

 __pcpu_reg_4定义:

#define __pcpu_reg_4(mod, x) mod "r" (x)

根据宏定义展开语句:

{
	u32 pfo_val__;
	asm("movl %%gs:%P[var], %[val]"
	    : [val] "=" "r" pfo_val__
	    : [var] "p" (&(pcpu_hot.current_task)));
	(typeof(pcpu_hot.current_task))(unsigned long) pfo_val__;
}

在Linux内核代码中,%P通常用作内联汇编中宏的一部分,它让预处理器将给定的宏参数替换为立即数或标号。然而,%P自身不是一个宏定义,它是内联汇编的一个扩展写法。这种用法特定于内核代码中的内联汇编,表示对应约束的扩展模板字符串。这允许通过预处理器处理宏参数后,将处理后的值进行一些特殊的拼接和格式化。

再次展开语句并简化:

{
	u32 pfo_val__;
	asm ("movl %%gs:(%1), %0" 
		: "=r" (pfo_val__)
		: "p" (&(pcpu_hot.current_task))
	);
	(task_struct *)pfo_val__;
}

下面逐一解析这段代码的含义:

  • u32 pfo_val__;:声明了一个无符号32位整型变量pfo_val__

  • asm: 表示这是一段内联汇编代码。

  • "movl %%gs:(%1), %0": 指示汇编器执行movl(move long,即移动四字节数据)指令。%%gs:表示使用gs段寄存器引用的地址。这里(%1)将使用输入约束里的参数替换,%0将使用输出约束里的参数替换。

    • %%gs:(%1): 表示操作数来源是第二个输入约束即"p" (&(pcpu_hot.current_task)),也就是pcpu_hot.current_task的地址。
  • %0:表示汇编指令的目标操作数,也就是输出到C变量pfo_val__中。

这段内联汇编的约束如下:

  • "=r" (pfo_val__): 输出操作数(%0)。"=r"表示结果将被存储在一个普通寄存器中,并将覆盖变量pfo_val__的内容。

  • "p" (&(pcpu_hot.current_task)): 输入操作数(%1)。"p"约束表示值应该是一个有效的内存指针。在这种情况下,它提供了per-CPU变量pcpu_hot.current_task的地址。

 整体来看,这段代码的作用是从每个处理器专有的数据区域中读取pcpu_hot.current_task的值,并将它存入C语言的变量pfo_val__中,这个操作对于每个CPU核心都是独立的。这样做可以很高效地处理与当前CPU直接相关联的数据,而无需担心多CPU间数据同步问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值