频道栏目
首页 > 系统 > 其他 > 正文

调度时机分析之被动调度(之系统调用返回)

2015-07-14 10:33:45         来源:weijitao的博客  
收藏   我要投稿

分析基于内核版本2.6.12.6

在什么情况下,会触发调度?

Linux进程的调度主要分为主动调度和被动调度两大类。

◆主动调度

主动调度就是进程自己缺少相应的所申请的资源,显示调用schedule,让出处理器。

◆被动调度

在整个linux运行过程中,被动调度又可细分为两种:

●用户态抢占调度

●内核态抢占调度

 

下面就结合内核代码分析上述各种调度时机的详细情况。

被动调度

整个linux运行过程中,被动调度分为用户态抢占调度和内核态抢占调度。

用户态抢占调度

用户态抢占调度发生在当系统调用、中断处理、异常处理等返回用户态时,或者进程的时间片用完时。

系统调用返回

当一个进程由于系统调用进入内核态,在系统调用处理完,返回用户态时,是一个调度点。这个时候会检测有没有设置TIF_NEED_RESCHED标志。下面分析下内核中系统调用返回相关的代码片段。

 

ENTRY(system_call) /*系统调用入口*/

CFI_STARTPROC /*用在每个函数的开始,用于初始化一些内部数据结构*/

swapgs /*当处理器进入或者离开内核时,会使用swapg命令在gs寄存器的内核与用户值之间切换*/

movq %rsp,%gs:pda_oldrsp /*将用户态栈指针压入gs寄存器指向的x8664_pda结构体的oldrsp字段*/

movq %gs:pda_kernelstack,%rsp /*将内核栈帧赋给rsp*/

sti /*开中断*/

SAVE_ARGS 8,1 /*将一些寄存器压栈*/

movq %rax ,ORIG_RAX-ARGOFFSET(%rsp) /*120-48=72 8*9(%rsp)*/

movq %rcx ,RIP-ARGOFFSET(%rsp) /*128-48=80 8*10(%rsp)*/

GET_THREAD_INFO(%rcx) /*获取当前进程的thread_info结构的地址*/

testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT|_TIF_SECCOMP),threadinfo_flags(%rcx)

jnz tracesys

cmpq $__NR_syscall_max ,%rax /*判断系统调用号是否大于最大值*/

ja badsys

movq %r10 ,%rcx

call *sys_call_table(,%rax,8) # XXX: rip relative /*rax为系统调用号*/

movq %rax ,RAX-ARGOFFSET(%rsp)

 

 

上面代码片段是系统调用的入口。这个系统调用的入口是在syscall_init函数里调用wrmsrl(MSR_LSTAR, system_call)设置的,通过一个MSR寄存器来保存系统调用的地址,而不再像I386架构下面是通过中断。

用户进程和内核都使用gs段寄存器访问状态数据。用户进程使用这个寄存器保存每个线程的数据,内核使用这个寄存器管理每个处理器的数据。当处理器进入或者离开内核时,都会使用swapgs命令在gs寄存器的内核和用户态值之间切换。

 

/* Per processor datastructure. %gs points to it while the kernel runs */

struct x8664_pda {

struct task_struct *pcurrent; /* Current process */

unsigned long data_offset; /* Per cpu data offset from linker address */

struct x8664_pda *me; /* Pointer to itself */

unsigned long kernelstack; /* top of kernel stack for current */

unsigned long oldrsp; /* user rsp for system call */

unsigned long irqrsp; /* Old rsp for interrupts. */

int irqcount; /* Irq nesting counter. Starts with -1 */

int cpunumber; /* Logical CPU number */

char *irqstackptr; /* top of irqstack */

unsigned int __softirq_pending;

unsigned int __nmi_count; /* number of NMI on this CPUs */

struct mm_struct *active_mm;

int mmu_state;

unsigned apic_timer_irqs;

} ____cacheline_aligned;

 

x8664_pda数据结构的kernelstack字段指向当前cpu的内核栈顶,oldrsp字段存储的是由系统调用进入内核态的用户进程的栈帧rsp。在进入系统调用入口后,就会保存用户进程的栈帧,并恢复当前cpu的栈帧到rsp。

 

SAVE_ARGS宏主要是将一些寄存器压栈。

下面接着分析系统调用代码:

 

.globl ret_from_sys_call

ret_from_sys_call:

movl $_TIF_ALLWORK_MASK,%edi

/*edi: flagmask */

sysret_check:

GET_THREAD_INFO(%rcx)

cli /*关中断*/

movl threadinfo_flags(%rcx),%edx

andl %edi,%edx /*检测是否还有其它工作需要完成*/

jnz sysret_careful /*有工作完成则跳转到sysret_careful */

movq RIP-ARGOFFSET(%rsp),%rcx

RESTORE_ARGS 0,-ARG_SKIP,1

movq %gs:pda_oldrsp,%rsp

swapgs

sysretq

 

/* Handle reschedules */

/*edx: work, edi: workmask */

sysret_careful:

bt $TIF_NEED_RESCHED,%edx /*检测是否有设置TIF_NEED_RESCHED 标志*/

jnc sysret_signal /*没有设置TIF_NEED_RESCHED标志,跳转到sysret_signal*/

sti

pushq %rdi

call schedule

popq %rdi

jmp sysret_check

 

 

上面代码是系统调用后的处理部分。在处理完系统调用返回用户态前,首先检查是否还有其他工作需要完成,如果有其他工作,则跳转到sysret_careful标签处执行。如果没有其他工作,则将之前压栈的寄存器出栈,调用swapgs命令,切换gs寄存器的内核和用户态值,最后调用sysretq指令退出系统调用。

 

sysret_careful开始就检测是否有设置TIF_NEED_RESCHED标志,如果没有设置此标志,则跳转到sysret_signal标签处执行。如果有设置此标志,则首先开中断,然后调用schedule函数。

 

下面接着分析sysret_signal标签处的代码:

 

sysret_signal:

sti /*开中断*/

testl $(_TIF_SIGPENDING|_TIF_NOTIFY_RESUME|_TIF_SINGLESTEP),%edx /*检测是否有信号需要处理*/

jz 1f

 

/* Really a signal */

/* edx:work flags (arg3) */

leaq do_notify_resume(%rip),%rax /*将do_notify_resume函数的地址赋给rax寄存器*/

leaq -ARGOFFSET(%rsp),%rdi # &pt_regs -> arg1 /* do_notify_resume函数参数1*/

xorl %esi,%esi # oldset -> arg2 /* do_notify_resume函数参数2*/

call ptregscall_common

1:movl $_TIF_NEED_RESCHED,%edi

jmp sysret_check

 

上面代码片段是处理信号部分代码。首先检测是否有信号需要处理,有信号处理的情况下,将do_notify_resume函数的地址赋给rax寄存器,然后准备好do_notify_resume函数需要使用的3个参数,通过寄存器rdi,esi和edx三个寄存器传递。然后跳转到ptregscall_common函数处执行。在ptregscall_common中会调用call *%rax,进入do_notify_resume函数执行。

 

下面是上面分析的系统调用相关代码的流程图。

\

 

图-systemcall流程图

 

\

 

图-ret_from_sys_call流程图

 

相关TAG标签 时机 系统
上一篇:VMwareunrecoverableerror解决方法
下一篇:关于highcharts的中文导出问题
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站