小张学linux内核:十四. elf文件的加载和进程切换寄存器的保存

重点关注虚拟内存的切换,pgd,mm_struct, start_code;理清楚这三个概念。
pgd不是在这里设置的遵循COW,是在写时缺页中断填充的。
start_code只是一个段开始的标记;真正的进程上下文,存在tast_struct->thread_inf->cpu_context中。
mm_struct:是进程的整个内存空间描述。

进程内核栈示意图

首先看task内核栈的分布图
在这里插入图片描述

task_struct->stack;即为进程的内核栈,指向栈底。
栈底,为magic word,进程调度时用来检测是否栈溢出的。然后是task_struct结构体,task_struct里面包含thread_info结构。
thread_info->保存进程切换两个进程的寄存器现场。

struct thread_info {
	unsigned long		flags;		/* low level flags */
	int			preempt_count;	/* 0 => preemptable, <0 => bug */
	mm_segment_t		addr_limit;	/* address limit */
	struct task_struct	*task;		/* main task structure */
	__u32			cpu;		/* cpu */
	__u32			cpu_domain;	/* cpu domain */
	struct cpu_context_save	cpu_context;	/* cpu 寄存器上下文*/
	__u32			syscall;	/* syscall number */
	__u8			used_cp[16];	/* thread used copro */
	unsigned long		tp_value[2];	/* TLS registers */
#ifdef CONFIG_CRUNCH
	struct crunch_state	crunchstate;
#endif
	union fp_state		fpstate __attribute__((aligned(8)));
	union vfp_state		vfpstate;
#ifdef CONFIG_ARM_THUMBEE
	unsigned long		thumbee_state;	/* ThumbEE Handler Base register */
#endif
};

cpu_context_save结构体:

struct cpu_context_save {
	__u32	r4;
	__u32	r5;
	__u32	r6;
	__u32	r7;
	__u32	r8;
	__u32	r9;
	__u32	sl;
	__u32	fp;
	__u32	sp;				/*栈指针*/
	__u32	pc;             /*pc值*/
	__u32	extra[2];		/* Xscale 'acc' register, etc */
};

通过宏TI_CPU_SAVE可以访问到这个成员的偏移量。

文件:arch\arm\kernel\asm-offsets.c

DEFINE(TI_CPU_SAVE,		offsetof(struct thread_info, cpu_context));

TI是thread_info的缩写。

进程上下文切换寄存器环境的切换

这些就能解释进程调度中上下文切换的pc的跳了。
来看__switch_to函数,汇编代码
文件arch\arm\kernel\entry-v7m.S

/*
 * Register switch for ARMv7-M processors.
 * r0 = previous task_struct, r1 = previous thread_info, r2 = next thread_info
 * previous and next are guaranteed not to be the same.
 */
ENTRY(__switch_to)
	.fnstart
	.cantunwind
	/*获取old进程cpu_context值*/
	add	ip, r1, #TI_CPU_SAVE          
	/*保存r4-r11寄存器*/ 
	stmia	ip!, {r4 - r11}		@ Store most regs on stack
	/*保存sp寄存器*/
	str	sp, [ip], #4        
	/*保存pc寄存器*/
	str	lr, [ip], #4
	mov	r5, r0
	/*获取新进程的cpu_context*/
	add	r4, r2, #TI_CPU_SAVE        
	ldr	r0, =thread_notify_head
	mov	r1, #THREAD_NOTIFY_SWITCH
	bl	atomic_notifier_call_chain
	mov	ip, r4
	mov	r0, r5
	/*j加载r4-r11寄存器值*/
	ldmia	ip!, {r4 - r11}		@ Load all regs saved previously
	/*加载sp*/
	ldr	sp, [ip]
	/*加载pc,完成进程的切换*/
	ldr	pc, [ip, #4]!
	.fnend
ENDPROC(__switch_to)

问:进程切换为什么不保护寄存器r0-r3的值?

答: r0,r1,r2用来做__switch_to传参用,r0:old task_struct的指针;r1是新进程指针,r2 old thread_info。

内栈顶的pt_regs是作甚的呢?

答:是系统调用保存用户侧上下文环境的寄存器值

#define current_pt_regs() task_pt_regs(current)

arch\arm\include\asm\processor.h中

	/*THREAD_START_SP 为内核栈大小-8*/
#define task_pt_regs(p) \
	((struct pt_regs *)(THREAD_START_SP + task_stack_page(p)) - 1)
	
#define task_stack_page(task)	((void *)(task)->stack)	

elf文件的加载

直接分析函数load_elf_binary

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值