SMP结构中的中断机制和进程调度课件_第1页
SMP结构中的中断机制和进程调度课件_第2页
SMP结构中的中断机制和进程调度课件_第3页
SMP结构中的中断机制和进程调度课件_第4页
SMP结构中的中断机制和进程调度课件_第5页
已阅读5页,还剩75页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Linux源代码阅读SMP结构中的中断机制和进程调度张飞Linux源代码阅读SMP结构中的中断1概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制2分布式中断处理APIC简介SMP结构中的中断控制硬件机构全局APIC本地APIC分布式中断处理3高级可编程中断控制器APIC为了充分利用smp体系结构的并行性,要求动态分配中断请求,也就是说可以向任意cpu发出中断请求.传统的i386处理器都采用8259A中断控制器,其作用是提供多个外部中断源与单一cpu之间的连接.如果在SMP结构中还是采用8259A中断控制器,那就只能静态的把所有的外部中断源划分成若干组,分别把每一组都连接到一个8259A,而8259A则与cpu有一对一的连接.这样就达不到动态分配中断请求的目的.为了更好的支持smp结构,从Pentium开始,Intel设计了一种更为通用的中断控制器,称为高级可编程中断控制器APIC(AdvancedProgrammableInterruptController).高级可编程中断控制器APIC为了充分利用smp体系结构的并行4SMP结构中的中断控制硬件机构cpu0本地APICcpu1本地APIC本地APICcpun全局APIC本地中断请求

本地中断请求本地中断请求ICC(中断控制器通信)总线外部中断请求SMP结构中的中断控制硬件机构cpu0本地APICcpu5分布式中断处理硬件机制概述两种APIC:本地APIC和全局APIC,通过中断控制器通信(InterruptControllerCommunication,ICC)总线相连.本地APIC集成在cpu内部,通过内部APIC可以向其他cpu发送中断请求.全局APIC负责把来自外部设备的中断请求提交和分配给系统中各个cpu的任务.分布式中断处理硬件机制概述两种APIC:本地APIC和全局A6全局APIC组成全局APIC由一组IRQ线路,一个有24个表项的中断重定向表(InterruptRedirectionTable),一个可编程寄存器和一个用来发送和接受经过ICC总线的APIC消息的消息单元组成.和8259A的IRQ引脚不同,中断优先级和引脚号无关,重定向表中的每个表项都可以被单独编程来说明中断向量和优先级,目标处理器以及如何选定处理器.重定向表中的消息用来把外部IRQ信号转换成通过ICC总线发往一个或多个本地APIC单元的消息.全局APIC组成7全局APIC 工作模式固定模式把IRQ信号发送到相应的重定向表表项所列出的本地APIC上.最低优先级模式把IRQ信号发送到正在执行优先级最低的进程的处理器的本地APIC上.所有的本地APIC都有一个可编程任务优先级寄存器(taskpriorityregister),它包含了当前正在运行的进程的优先级.在每次任务切换时这个寄存器的值必须由内核进行修改.全局APIC 工作模式8本地APIC组成每个本地APIC都有几个32位的寄存器,一个内部时钟,一个定时器设备,240个不同的中断向量(从0x20~0xff,0~0x1f用于cpu本身的陷阱)以及两条为局部中断保留的IRQ线路,这两条线路用于重启系统.本地APIC的一个重要功能是实现处理器间中断IPI当一个cpu想要向其他cpu发送中断时,将中断向量和目标处理器的本地apic标志符保存到自己本地apic的中断命令寄存器中,然后通过ICC总线向目标处理器的本地apic发送一条消息,目标处理器的本地apic就向自己的cpu发出相应的中断.本地APIC组成9SMP结构中的中断控制硬件机构cpu0本地APICcpu1本地APIC本地APICcpun全局APIC本地中断请求

本地中断请求本地中断请求ICC(中断控制器通信)总线外部中断请求SMP结构中的中断控制硬件机构cpu0本地APICcpu10概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制11中断初始化smp相关的几个主要中断向量设置中断门中断响应程序的建立相关中断处理程序代码smp_reschedule_interrupt()smp_call_function_interrupt()中断初始化12smp相关的几个主要中断向量smp结构专用的几个IRQ向量定义在include/asm-i386/apic.h中

#defineSPURIOUS_APIC_VECTOR 0xff

#defineERROR_APIC_VECTOR 0xfe

#defineINVALIDATE_TLB_VECTOR0xfd

#defineRESCHEDULE_VECTOR 0xfc

#defineCALL_FUNCTION_VECTOR 0xfb

#defineLOCAL_TIMER_VECTOR 0xef其他不常用的向量合并到CALL_FUNCTION_VECTOR中以节省向量空间,使用比较频繁的是TLB、reschedule和localAPIC中断向量.smp相关的几个主要中断向量smp结构专用的几个IRQ向量定13设置中断门void

__init

init_IRQ(void){

for(i=

0;i<

NR_IRQS;i++){

intvector=

FIRST_EXTERNAL_VECTOR

+i;

if(vector!=

SYSCALL_VECTOR)

set_intr_gate(vector,interrupt[i]); }#ifdefCONFIG_SMP

set_intr_gate(FIRST_DEVICE_VECTOR,interrupt[0]);

set_intr_gate(RESCHEDULE_VECTOR,reschedule_interrupt);

set_intr_gate(INVALIDATE_TLB_VECTOR,invalidate_interrupt);

set_intr_gate(CALL_FUNCTION_VECTOR,call_function_interrupt);#endif

…}设置中断门void__initinit_IRQ(void14设置中断门for循环设置了除SYSCALL_VECTOR外从FIRST_EXTERNAL_VECTOR开始的NR_IRQS个中断门向量,在smp结构中覆盖了其中的4个,其他的中断向量基本上没有什么变化,还与采用8259A时大致相同,不同的是现在由全局APIC取代8259A将外部中断请求送达各个cpu.为中断向量FIRST_DEVICE_VECTOR设置的中断响应入口程序是interrupt[0].中断向量RESCHEDULE_VECTOR的中断响应入口程序是reschedule_interrupt.中断向量INVALIDATE_TLB_VECTOR的中断响应入口程序设置为invalidate_interrupt.中断向量CALL_FUNCTION_VECTOR对应call_function_interrupt程序.设置中断门for循环设置了除SYSCALL_VECTOR外从15中断响应程序的建立上述几个主要中断向量的实际中断处理程序由下面一组宏语句建立:#ifdefCONFIG_SMPBUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR)BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)#endif中断响应程序的建立上述几个主要中断向量的实际中断处理程序由下16中断响应程序的建立其中BUILD_SMP_INTERRUPT宏定义如下:#define

BUILD_SMP_INTERRUPT(x,v)XBUILD_SMP_INTERRUPT(x,v)#define

XBUILD_SMP_INTERRUPT(x,v)\asmlinkage

voidx(void);\asmlinkage

voidcall_##x(void);\__asm__(\"\n"__ALIGN_STR"\n"\SYMBOL_NAME_STR(x)":\n\t"\"pushl$"#v"\n\t"\SAVE_ALL\SYMBOL_NAME_STR(call_##x)":\n\t"\"call"SYMBOL_NAME_STR(smp_##x)"\n\t"\"jmpret_from_intr\n");中断响应程序的建立其中BUILD_SMP_INTERRUPT17中断响应程序的建立BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)宏展开如下:asmlinkage

void

reschedule_interrupt(void);\asmlinkage

void

call_reschedule_interrupt(void);\__asm__(

reschedule_interrupt: pushl$RESCHEDULE_VECTOR#中断号存进核心栈

SAVE_ALL#保存各个寄存器值

call_smp_reschedule_interrupt: callsmp_reschedule_interrupt jmpret_from_intr)中断响应程序的建立BUILD_SMP_INTERRUPT(r18中断响应程序的建立至此,结合前面说的中断门的初始化,smp专有的主要中断向量及其响应机制建立起来:当发生RESCHEDULE_VECTOR中断时,响应程序的入口是reschedule_interrupt(),实际负责中断处理程序的函数为smp_reschedule_interrupt().同理,与INVALIDATE_TLB_VECTOR相对应的入口程序是invalidate_interrupt(),实际中断处理程序的则是smp_invalidate_interrupt();与CALL_FUNCTION_VECTOR相对应的入口程序是call_function_interrupt(),而实际处理中断请求的是smp_call_function_interrupt().中断响应程序的建立至此,结合前面说的中断门的初始化,smp专19具体中断处理程序smp_reschedule_interrupt()asmlinkage

void

smp_reschedule_interrupt(void){

ack_APIC_irq();//发送中断请求确认}externinlinevoid

ack_APIC_irq(void){

apic_write_around(APIC_EOI,0);/*向本地APIC的控制寄存器写入0,表示已经收到了中断请求*/}具体中断处理程序smp_reschedule_interru20具体中断处理程序smp_reschedule_interrupt()该函数应其他cpu的请求进行一次进程调度,但是从程序代码上看,该函数仅仅发回一个中断确认.实际上,对中断请求的服务隐藏在内核对中断处理的公共部分:内核在针对特定中断请求的服务完成后都要检查(本cpu)是否需要进行进程调度,这正是smp_reschedule_interrupt()的所要达到的目的—引发目标CPU一次中断,以便检查是否需要重新调度.具体中断处理程序smp_reschedule_interru21具体中断处理程序smp_call_function_interrupt()该函数响应CALL_FUNCTION_VECTOR,用于请求目标CPU执行一个指定的函数.发送者先设置好一个全局的call_data_struct数据结构,然后向目标CPU发出请求,目标CPU收到中断向量后,调用该函数进行处理staticspinlock_tcall_lock=SPIN_LOCK_UNLOCKED;struct

call_data_struct{

void(*func)(void*info);//指向要求对方执行的函数

void

*info;//函数参数

atomic_tstarted;

atomic_tfinished;

intwait;};static

struct

call_data_struct*call_data;具体中断处理程序smp_call_function_inte22具体中断处理程序smp_call_function_interrupt()asmlinkage

void

smp_call_function_interrupt(void){

void(*func)(void*info)=call_data->func;//取出函数指针

void

*info=call_data->info;

intwait=call_data->wait;

ack_APIC_irq();//先发回中断确认

atomic_inc(&call_data->started); (*func)(info);//调用指定函数

if(wait)//指定动作完成后,wait设置为1

atomic_inc(&call_data->finished);}具体中断处理程序smp_call_function_inte23具体中断处理程序smp_call_function_interrupt()当然,一般的函数是没有必要请其他CPU来执行的,因为系统中所有的CPU都可共享同样的代码和数据.之所以要请求其他CPU执行,是因为某个函数必须由特定的CPU来执行.例如,pentium处理器有一条cpuid指令,通过这条指令可以查询本CPU的型号、版本以及是否支持一些特殊的功能、当前的功能设置等等信息.但是这条指令只能由具体的CPU本身执行,而不能由别的CPU代替.这样,如果要知道系统中某一个CPU的有关情况,就只能请求该CPU来执行cpuid指令.具体中断处理程序smp_call_function_inte24概述SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概述SMP结构中的中断机制25处理器间中断IPIIPI概述IPI中断向量IPI中断请求函数处理器间中断IPI26IPI概述IPI(InterprocessorInterrupt)称为处理机间中断.实际上,前面介绍的RESCHEDULE_VECTOR、INVALIDATE_TLB_VECTOR以及CALL_FUNCTION_VECTOR都属于处理器间中断IPI.从前一部分可以看出,与单cpu系统的中断处理机制相比,smp系统仅仅作了少量修改,并且这些修改又大部分集中在处理器间中断IPI这一部分,也许我们可以从另一个角度来看待smp系统的中断机制-smp中断机制和单CPU系统中断机制在某种程度上是等价的.IPI概述IPI(InterprocessorInter27全局APICCPU0CPU1CPUn本地APIC本地APIC本地APIC外部中断外部中断CPU8259A单个“超级”处理器SMP结构与单CPU系统中断机制的类比全局APICCPU0CPU1CPUn本地APIC本地A28IPI中断向量本地APIC可以识别6种消息,这些消息是由接收消息的CPU作为不同中断向量来解释的.(之所以不同于一般中断向量,可能是因为这些中断向量并不对应着实际的中断请求引脚).SPURIOUS_APIC_VECTOR(0xff)入口程序spurious_interrupt(),实际中断处理程序smp_spurious_interrupt().ERROR_APIC_VECTOR(0xfe)出错计数器溢出中断,入口程序error_interrupt(),中断处理程序smp_error_interrupt().INVALIDATE_TLB_VECTOR(0xfd)RESCHEDULE_VECTOR(0xfc)CALL_FUNCTION_VECTOR(0xfb)LOCAL_TIMER_VECTOR(0xef)I/OAPIC把定时中断自动发给所有的CPU.相应的入口程序是apic_timer_interrupt(),实际中断服务程序为smp_apic_timer_interrupt().IPI中断向量本地APIC可以识别6种消息,这些消息是由接收29中断请求函数中断请求函数用于向目标CPU发送指定的IPI请求向量staticinlinevoidsend_IPI_allbutself(intvector)向除自己以外的所有CPU发送一个IPI.staticinlinevoidsend_IPI_all(intvector)向所有的CPU(包括自己)发送一个IPI.voidsend_IPI_self(intvector)向自己发送一个IPI.staticinlinevoidsend_IPI_mask(intmask,intvector)向由mask指定的一个或多个CPU发送一个IPI.这几个函数功能大同小异,实现代码也都比较简单.其中以send_IPI_mask函数最为灵活,因而也稍为复杂一些.中断请求函数中断请求函数用于向目标CPU发送指定的IPI请求30send_IPI_mask函数staticinlinevoid

send_IPI_mask(intmask,intvector){

unsignedlongcfg;

unsignedlongflags; __save_flags(flags);//中断前状态信息保存在flags中 __cli();//关中断

apic_wait_icr_idle();//确认或等待APIC_ICR处于空闲状态

/*ICR2、ICR是本地APIC的2个控制寄存器*/

cfg=__prepare_ICR2(mask);//根据中断请求的目标CPU准备将要

apic_write_around(APIC_ICR2,cfg)//写入寄存器APIC_ICR2的值 cfg=__prepare_ICR(0,vector); //根据要发送的中断向量准备将要

apic_write_around(APIC_ICR,cfg);//写入寄存器APIC_ICR的值/*对APIC_ICR的写入操作引发并完成将中断向量发送至目标cpu的工作*/ __restore_flags(flags);//恢复标志}staticinlineint__prepare_ICR2(unsignedintmask){returnSET_APIC_DEST_FIELD(mask);}staticinlineint__prepare_ICR(unsignedintshortcut,intvector){returnAPIC_DM_FIXED|shortcut|vector|APIC_DEST_LOGICAL;}send_IPI_mask函数staticinlinev31其他相关发送函数再看一个较为简单的函数send_IPI_allstaticinlinevoid

send_IPI_all(intvector){ __send_IPI_shortcut(APIC_DEST_ALLINC,vector);}staticinlinevoid__send_IPI_shortcut(unsignedintshortcut,intvector){

unsignedintcfg;

apic_wait_icr_idle();//确认或等待ICR空闲 cfg=__prepare_ICR(shortcut,vector);.//对ICR进行编程

apic_write_around(APIC_ICR,cfg);}/*与send_IPI_reschedule相比,少了耗时的开/关中断动作,并且只对APIC编程一次*/除send_IPI_mask()之外的几个发送函数实际上都是简单的调用__send_IPI_shortcut函数,只不过传给这个函数的参数不同而已.在这些函数基础上,还定义了一些功能更为专一、明确的函数.比如当一个CPU需要另一个CPU进行一次进程调度时,可以调用下面的函数:void

smp_send_reschedule(intcpu){

send_IPI_mask(1<<cpu,RESCHEDULE_VECTOR);}其他相关发送函数再看一个较为简单的函数send_IPI_al32概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制33相关数据结构单CPU系统中,任一时刻只有当前进程是在运行中的,但在SMP系统中同时有好几个进程在运行,因此在task_struct结构中引入两个字段:一个是has_cpu,为1时说明进程正在运行,为0则表示不在运行;另一个字段是processor,当has_cpu为1时指出进程是在哪一个cpu上运行.一个相关的宏操作can_schedule():

#ifdef

CONFIG_SMP

#define

idle_task(cpu)(init_tasks[cpu_number_map(cpu)])

#define

can_schedule(p,cpu)((!(p)->has_cpu)&&\ ((p)->cpus_allowed&(1<<cpu)))

#else

#define

idle_task(cpu)(&init_task)

#define

can_schedule(p,cpu)(1)

#endif相关数据结构单CPU系统中,任一时刻只有当前进程是在运行中的34调度函数Schedule()中相关代码当一个CPU通过schedule()从系统的就绪队列中挑选了一个进程作为运行的下一个进程next,即从当前进程prev切换到这个进程时,就将其task_struct结构中的has_cpu字段设置成1,并将processor设置成该CPU的逻辑编号.asmlinkage

void

schedule(void){…this_cpu=prev->processor;//取得当前CPU逻辑号…#ifdefCONFIG_SMP

next->has_cpu=1;//将要上台进程的has_cpu设置为1

next->processor=this_cpu;//设置processor字段#endif

spin_unlock_irq(&runqueue_lock);

if(prev==next) gotosame_process;…switch_to(prev,next,prev);//从prev切换到next进程__schedule_tail(prev);//处理下台的prev进程,看能否在其他cpu上重新调度…}调度函数Schedule()中相关代码当一个CPU通过sch35staticinlinevoid__schedule_tail(structtask_struct

*prev)函数保存先前进程调度策略并清零prev->policy中SCHED_YIELD标志位将prev的has_cpu字段清零下台前运行态?“空转”进程或自动下台?之前是运行态?尝试重新调度prev进程开始返回policy=prev->policy;

prev->policy=policy&~SCHED_YIELD;wmb();task_lock(prev);prev->has_cpu=0;mb();NYYNNYif(prev->state==TASK_RUNNING)if((prev==idle_task(smp_processor_id()))||(policy&SCHED_YIELD))if(prev->state==TASK_RUNNING)reschedule_idle(prev);staticinlinevoid__schedule_36staticvoid

reschedule_idle(structtask_struct

*p)函数staticvoid

reschedule_idle(structtask_struct

*p){#ifdefCONFIG_SMP

intthis_cpu=smp_processor_id();//取得当前CPU逻辑号

struct

task_struct*tsk,*target_tsk;

intcpu,best_cpu,i,max_prio;

cycles_toldest_idle; best_cpu=p->processor;//最好可以在原来的CPU上重新运行

if(can_schedule(p,best_cpu)){//可以在原来的CPU上调度么 tsk=idle_task(best_cpu);//取得该CPU的”空转”进程

if(cpu_curr(best_cpu)==tsk){//判断目的CPU是否空闲

intneed_resched;send_now_idle: need_resched=tsk->need_resched;//保存原先的need_resched

tsk->need_resched=1;//设置need_resched标志

/*如果need_resched为-1,则没有必要发送IPI,idle进程会自动监视该变量*/

if((best_cpu

!=this_cpu)&&!need_resched) //需要发送IPI?

smp_send_reschedule(best_cpu);//发送RESCHEDULE_VECTOR

return; } }staticvoidreschedule_idle(st37oldest_idle=(cycles_t)-1;target_tsk=NULL;max_prio=1;

/*查找所有可用CPU,看看能否重新调度进程p*/for(i=0;i<smp_num_cpus;i++){//smp_num_cpus:系统中活动CPU个数 cpu=cpu_logical_map(i);//取得该CPU的逻辑号

if(!can_schedule(p,cpu))

continue; tsk=cpu_curr(cpu);

if(tsk==idle_task(cpu)){//存在空闲CPU,选择运行时间最长的进程来剥夺

if(last_schedule(cpu)<oldest_idle){ oldest_idle=last_schedule(cpu); target_tsk=tsk; } }else{if(oldest_idle==-1ULL){//若没有空闲CPU

intprio=preemption_goodness(tsk,p,cpu);

if(prio>max_prio){//寻找一个运行资格较p最低的进程来剥夺 max_prio=prio; target_tsk=tsk; } } }}(接上)oldest_idle=(cycles_t)-1;(接38tsk=target_tsk;

if(tsk){//如果存在可以剥夺的进程

if(oldest_idle

!=-1ULL){//如果存在空闲CPU best_cpu=tsk->processor; gotosend_now_idle; }

tsk->need_resched=1;//置need_resched标志

if(tsk->processor

!=this_cpu)

smp_send_reschedule(tsk->processor);//发送IPI }

return; #else/*ifundefCONFIG_SMP*/

intthis_cpu=smp_processor_id();

struct

task_struct

*tsk; tsk=cpu_curr(this_cpu);//取得当前进程

if(preemption_goodness(tsk,p,this_cpu)>1)//运行资格高于当前进程

tsk->need_resched=1;#endif}(接上)tsk=target_tsk;(接上)39谢谢大家!

谢谢大家!

40Linux源代码阅读SMP结构中的中断机制和进程调度张飞Linux源代码阅读SMP结构中的中断41概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制42分布式中断处理APIC简介SMP结构中的中断控制硬件机构全局APIC本地APIC分布式中断处理43高级可编程中断控制器APIC为了充分利用smp体系结构的并行性,要求动态分配中断请求,也就是说可以向任意cpu发出中断请求.传统的i386处理器都采用8259A中断控制器,其作用是提供多个外部中断源与单一cpu之间的连接.如果在SMP结构中还是采用8259A中断控制器,那就只能静态的把所有的外部中断源划分成若干组,分别把每一组都连接到一个8259A,而8259A则与cpu有一对一的连接.这样就达不到动态分配中断请求的目的.为了更好的支持smp结构,从Pentium开始,Intel设计了一种更为通用的中断控制器,称为高级可编程中断控制器APIC(AdvancedProgrammableInterruptController).高级可编程中断控制器APIC为了充分利用smp体系结构的并行44SMP结构中的中断控制硬件机构cpu0本地APICcpu1本地APIC本地APICcpun全局APIC本地中断请求

本地中断请求本地中断请求ICC(中断控制器通信)总线外部中断请求SMP结构中的中断控制硬件机构cpu0本地APICcpu45分布式中断处理硬件机制概述两种APIC:本地APIC和全局APIC,通过中断控制器通信(InterruptControllerCommunication,ICC)总线相连.本地APIC集成在cpu内部,通过内部APIC可以向其他cpu发送中断请求.全局APIC负责把来自外部设备的中断请求提交和分配给系统中各个cpu的任务.分布式中断处理硬件机制概述两种APIC:本地APIC和全局A46全局APIC组成全局APIC由一组IRQ线路,一个有24个表项的中断重定向表(InterruptRedirectionTable),一个可编程寄存器和一个用来发送和接受经过ICC总线的APIC消息的消息单元组成.和8259A的IRQ引脚不同,中断优先级和引脚号无关,重定向表中的每个表项都可以被单独编程来说明中断向量和优先级,目标处理器以及如何选定处理器.重定向表中的消息用来把外部IRQ信号转换成通过ICC总线发往一个或多个本地APIC单元的消息.全局APIC组成47全局APIC 工作模式固定模式把IRQ信号发送到相应的重定向表表项所列出的本地APIC上.最低优先级模式把IRQ信号发送到正在执行优先级最低的进程的处理器的本地APIC上.所有的本地APIC都有一个可编程任务优先级寄存器(taskpriorityregister),它包含了当前正在运行的进程的优先级.在每次任务切换时这个寄存器的值必须由内核进行修改.全局APIC 工作模式48本地APIC组成每个本地APIC都有几个32位的寄存器,一个内部时钟,一个定时器设备,240个不同的中断向量(从0x20~0xff,0~0x1f用于cpu本身的陷阱)以及两条为局部中断保留的IRQ线路,这两条线路用于重启系统.本地APIC的一个重要功能是实现处理器间中断IPI当一个cpu想要向其他cpu发送中断时,将中断向量和目标处理器的本地apic标志符保存到自己本地apic的中断命令寄存器中,然后通过ICC总线向目标处理器的本地apic发送一条消息,目标处理器的本地apic就向自己的cpu发出相应的中断.本地APIC组成49SMP结构中的中断控制硬件机构cpu0本地APICcpu1本地APIC本地APICcpun全局APIC本地中断请求

本地中断请求本地中断请求ICC(中断控制器通信)总线外部中断请求SMP结构中的中断控制硬件机构cpu0本地APICcpu50概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制51中断初始化smp相关的几个主要中断向量设置中断门中断响应程序的建立相关中断处理程序代码smp_reschedule_interrupt()smp_call_function_interrupt()中断初始化52smp相关的几个主要中断向量smp结构专用的几个IRQ向量定义在include/asm-i386/apic.h中

#defineSPURIOUS_APIC_VECTOR 0xff

#defineERROR_APIC_VECTOR 0xfe

#defineINVALIDATE_TLB_VECTOR0xfd

#defineRESCHEDULE_VECTOR 0xfc

#defineCALL_FUNCTION_VECTOR 0xfb

#defineLOCAL_TIMER_VECTOR 0xef其他不常用的向量合并到CALL_FUNCTION_VECTOR中以节省向量空间,使用比较频繁的是TLB、reschedule和localAPIC中断向量.smp相关的几个主要中断向量smp结构专用的几个IRQ向量定53设置中断门void

__init

init_IRQ(void){

for(i=

0;i<

NR_IRQS;i++){

intvector=

FIRST_EXTERNAL_VECTOR

+i;

if(vector!=

SYSCALL_VECTOR)

set_intr_gate(vector,interrupt[i]); }#ifdefCONFIG_SMP

set_intr_gate(FIRST_DEVICE_VECTOR,interrupt[0]);

set_intr_gate(RESCHEDULE_VECTOR,reschedule_interrupt);

set_intr_gate(INVALIDATE_TLB_VECTOR,invalidate_interrupt);

set_intr_gate(CALL_FUNCTION_VECTOR,call_function_interrupt);#endif

…}设置中断门void__initinit_IRQ(void54设置中断门for循环设置了除SYSCALL_VECTOR外从FIRST_EXTERNAL_VECTOR开始的NR_IRQS个中断门向量,在smp结构中覆盖了其中的4个,其他的中断向量基本上没有什么变化,还与采用8259A时大致相同,不同的是现在由全局APIC取代8259A将外部中断请求送达各个cpu.为中断向量FIRST_DEVICE_VECTOR设置的中断响应入口程序是interrupt[0].中断向量RESCHEDULE_VECTOR的中断响应入口程序是reschedule_interrupt.中断向量INVALIDATE_TLB_VECTOR的中断响应入口程序设置为invalidate_interrupt.中断向量CALL_FUNCTION_VECTOR对应call_function_interrupt程序.设置中断门for循环设置了除SYSCALL_VECTOR外从55中断响应程序的建立上述几个主要中断向量的实际中断处理程序由下面一组宏语句建立:#ifdefCONFIG_SMPBUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)BUILD_SMP_INTERRUPT(invalidate_interrupt,INVALIDATE_TLB_VECTOR)BUILD_SMP_INTERRUPT(call_function_interrupt,CALL_FUNCTION_VECTOR)#endif中断响应程序的建立上述几个主要中断向量的实际中断处理程序由下56中断响应程序的建立其中BUILD_SMP_INTERRUPT宏定义如下:#define

BUILD_SMP_INTERRUPT(x,v)XBUILD_SMP_INTERRUPT(x,v)#define

XBUILD_SMP_INTERRUPT(x,v)\asmlinkage

voidx(void);\asmlinkage

voidcall_##x(void);\__asm__(\"\n"__ALIGN_STR"\n"\SYMBOL_NAME_STR(x)":\n\t"\"pushl$"#v"\n\t"\SAVE_ALL\SYMBOL_NAME_STR(call_##x)":\n\t"\"call"SYMBOL_NAME_STR(smp_##x)"\n\t"\"jmpret_from_intr\n");中断响应程序的建立其中BUILD_SMP_INTERRUPT57中断响应程序的建立BUILD_SMP_INTERRUPT(reschedule_interrupt,RESCHEDULE_VECTOR)宏展开如下:asmlinkage

void

reschedule_interrupt(void);\asmlinkage

void

call_reschedule_interrupt(void);\__asm__(

reschedule_interrupt: pushl$RESCHEDULE_VECTOR#中断号存进核心栈

SAVE_ALL#保存各个寄存器值

call_smp_reschedule_interrupt: callsmp_reschedule_interrupt jmpret_from_intr)中断响应程序的建立BUILD_SMP_INTERRUPT(r58中断响应程序的建立至此,结合前面说的中断门的初始化,smp专有的主要中断向量及其响应机制建立起来:当发生RESCHEDULE_VECTOR中断时,响应程序的入口是reschedule_interrupt(),实际负责中断处理程序的函数为smp_reschedule_interrupt().同理,与INVALIDATE_TLB_VECTOR相对应的入口程序是invalidate_interrupt(),实际中断处理程序的则是smp_invalidate_interrupt();与CALL_FUNCTION_VECTOR相对应的入口程序是call_function_interrupt(),而实际处理中断请求的是smp_call_function_interrupt().中断响应程序的建立至此,结合前面说的中断门的初始化,smp专59具体中断处理程序smp_reschedule_interrupt()asmlinkage

void

smp_reschedule_interrupt(void){

ack_APIC_irq();//发送中断请求确认}externinlinevoid

ack_APIC_irq(void){

apic_write_around(APIC_EOI,0);/*向本地APIC的控制寄存器写入0,表示已经收到了中断请求*/}具体中断处理程序smp_reschedule_interru60具体中断处理程序smp_reschedule_interrupt()该函数应其他cpu的请求进行一次进程调度,但是从程序代码上看,该函数仅仅发回一个中断确认.实际上,对中断请求的服务隐藏在内核对中断处理的公共部分:内核在针对特定中断请求的服务完成后都要检查(本cpu)是否需要进行进程调度,这正是smp_reschedule_interrupt()的所要达到的目的—引发目标CPU一次中断,以便检查是否需要重新调度.具体中断处理程序smp_reschedule_interru61具体中断处理程序smp_call_function_interrupt()该函数响应CALL_FUNCTION_VECTOR,用于请求目标CPU执行一个指定的函数.发送者先设置好一个全局的call_data_struct数据结构,然后向目标CPU发出请求,目标CPU收到中断向量后,调用该函数进行处理staticspinlock_tcall_lock=SPIN_LOCK_UNLOCKED;struct

call_data_struct{

void(*func)(void*info);//指向要求对方执行的函数

void

*info;//函数参数

atomic_tstarted;

atomic_tfinished;

intwait;};static

struct

call_data_struct*call_data;具体中断处理程序smp_call_function_inte62具体中断处理程序smp_call_function_interrupt()asmlinkage

void

smp_call_function_interrupt(void){

void(*func)(void*info)=call_data->func;//取出函数指针

void

*info=call_data->info;

intwait=call_data->wait;

ack_APIC_irq();//先发回中断确认

atomic_inc(&call_data->started); (*func)(info);//调用指定函数

if(wait)//指定动作完成后,wait设置为1

atomic_inc(&call_data->finished);}具体中断处理程序smp_call_function_inte63具体中断处理程序smp_call_function_interrupt()当然,一般的函数是没有必要请其他CPU来执行的,因为系统中所有的CPU都可共享同样的代码和数据.之所以要请求其他CPU执行,是因为某个函数必须由特定的CPU来执行.例如,pentium处理器有一条cpuid指令,通过这条指令可以查询本CPU的型号、版本以及是否支持一些特殊的功能、当前的功能设置等等信息.但是这条指令只能由具体的CPU本身执行,而不能由别的CPU代替.这样,如果要知道系统中某一个CPU的有关情况,就只能请求该CPU来执行cpuid指令.具体中断处理程序smp_call_function_inte64概述SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概述SMP结构中的中断机制65处理器间中断IPIIPI概述IPI中断向量IPI中断请求函数处理器间中断IPI66IPI概述IPI(InterprocessorInterrupt)称为处理机间中断.实际上,前面介绍的RESCHEDULE_VECTOR、INVALIDATE_TLB_VECTOR以及CALL_FUNCTION_VECTOR都属于处理器间中断IPI.从前一部分可以看出,与单cpu系统的中断处理机制相比,smp系统仅仅作了少量修改,并且这些修改又大部分集中在处理器间中断IPI这一部分,也许我们可以从另一个角度来看待smp系统的中断机制-smp中断机制和单CPU系统中断机制在某种程度上是等价的.IPI概述IPI(InterprocessorInter67全局APICCPU0CPU1CPUn本地APIC本地APIC本地APIC外部中断外部中断CPU8259A单个“超级”处理器SMP结构与单CPU系统中断机制的类比全局APICCPU0CPU1CPUn本地APIC本地A68IPI中断向量本地APIC可以识别6种消息,这些消息是由接收消息的CPU作为不同中断向量来解释的.(之所以不同于一般中断向量,可能是因为这些中断向量并不对应着实际的中断请求引脚).SPURIOUS_APIC_VECTOR(0xff)入口程序spurious_interrupt(),实际中断处理程序smp_spurious_interrupt().ERROR_APIC_VECTOR(0xfe)出错计数器溢出中断,入口程序error_interrupt(),中断处理程序smp_error_interrupt().INVALIDATE_TLB_VECTOR(0xfd)RESCHEDULE_VECTOR(0xfc)CALL_FUNCTION_VECTOR(0xfb)LOCAL_TIMER_VECTOR(0xef)I/OAPIC把定时中断自动发给所有的CPU.相应的入口程序是apic_timer_interrupt(),实际中断服务程序为smp_apic_timer_interrupt().IPI中断向量本地APIC可以识别6种消息,这些消息是由接收69中断请求函数中断请求函数用于向目标CPU发送指定的IPI请求向量staticinlinevoidsend_IPI_allbutself(intvector)向除自己以外的所有CPU发送一个IPI.staticinlinevoidsend_IPI_all(intvector)向所有的CPU(包括自己)发送一个IPI.voidsend_IPI_self(intvector)向自己发送一个IPI.staticinlinevoidsend_IPI_mask(intmask,intvector)向由mask指定的一个或多个CPU发送一个IPI.这几个函数功能大同小异,实现代码也都比较简单.其中以send_IPI_mask函数最为灵活,因而也稍为复杂一些.中断请求函数中断请求函数用于向目标CPU发送指定的IPI请求70send_IPI_mask函数staticinlinevoid

send_IPI_mask(intmask,intvector){

unsignedlongcfg;

unsignedlongflags; __save_flags(flags);//中断前状态信息保存在flags中 __cli();//关中断

apic_wait_icr_idle();//确认或等待APIC_ICR处于空闲状态

/*ICR2、ICR是本地APIC的2个控制寄存器*/

cfg=__prepare_ICR2(mask);//根据中断请求的目标CPU准备将要

apic_write_around(APIC_ICR2,cfg)//写入寄存器APIC_ICR2的值 cfg=__prepare_ICR(0,vector); //根据要发送的中断向量准备将要

apic_write_around(APIC_ICR,cfg);//写入寄存器APIC_ICR的值/*对APIC_ICR的写入操作引发并完成将中断向量发送至目标cpu的工作*/ __restore_flags(flags);//恢复标志}staticinlineint__prepare_ICR2(unsignedintmask){returnSET_APIC_DEST_FIELD(mask);}staticinlineint__prepare_ICR(unsignedintshortcut,intvector){returnAPIC_DM_FIXED|shortcut|vector|APIC_DEST_LOGICAL;}send_IPI_mask函数staticinlinev71其他相关发送函数再看一个较为简单的函数send_IPI_allstaticinlinevoid

send_IPI_all(intvector){ __send_IPI_shortcut(APIC_DEST_ALLINC,vector);}staticinlinevoid__send_IPI_shortcut(unsignedintshortcut,intvector){

unsignedintcfg;

apic_wait_icr_idle();//确认或等待ICR空闲 cfg=__prepare_ICR(shortcut,vector);.//对ICR进行编程

apic_write_around(APIC_ICR,cfg);}/*与send_IPI_reschedule相比,少了耗时的开/关中断动作,并且只对APIC编程一次*/除send_IPI_mask()之外的几个发送函数实际上都是简单的调用__send_IPI_shortcut函数,只不过传给这个函数的参数不同而已.在这些函数基础上,还定义了一些功能更为专一、明确的函数.比如当一个CPU需要另一个CPU进行一次进程调度时,可以调用下面的函数:void

smp_send_reschedule(intcpu){

send_IPI_mask(1<<cpu,RESCHEDULE_VECTOR);}其他相关发送函数再看一个较为简单的函数send_IPI_al72概要SMP结构中的中断机制分布式中断处理中断初始化处理器间中断IPISMP结构中的进程调度概要SMP结构中的中断机制73相关数据结构单CPU系统中,任一时刻只有当前进程是在运行中的,但在SMP系统中同时有好几个进程在运行,因此在task_struct结构中引入两个字段:一个是has_cpu,为1时说明进程正在运行,为0则表示不在运行;另一个字段是processor,当has_cpu为1时指出进程是在哪一个cpu上运行.一个相关的宏操作can_schedule():

#ifdef

CONFIG_SMP

#define

idle_task(cpu)(init_tasks[cpu_number_map(cpu)])

#define

can_schedule(p,cpu)((!(p)->has_cpu)&&\ ((p)->cpus_allowed&(1<<cpu)))

#else

#define

idle_task(cpu)(&init_task)

#define

can_schedule(p,cpu)(1)

#endif相关数据结构单CPU系统中,任一时刻只有当前进程是在运行中的74调度函数Schedule()中相关代码当一个CPU通过schedule()从系统的就绪队列中挑选了一个进程作为运行的下一个进程next,即从当前进程prev切换到这个进程时,就将其task_struct结构中的has_cpu字段设置成1,并将processor设置成该CPU的逻辑编号.asmlinkage

void

schedule(void){…this_cpu=prev->processor;//取得当前CPU逻辑号…#ifdefCONFIG_SMP

next->has_cpu=1;//将要上台进程的has_cpu设置为1

next->processor=this_cpu;//设置processor字段#endif

spin_unlock_irq(&runqueue_lock);

if(prev==next) gotosame_process;…switch_to(prev,next,prev);//从prev切换到next进程__schedule_tail(prev);//处理下台的prev进程,看能否在其他cpu上重新调度…}调度函数Schedule()中相关代码当一个CPU通过sch75staticinlinevoid__schedule_tail(structtask_struct

*prev)函数保存先前进程调度策略并清零prev->policy中SCHED_YIELD标志位将prev的has_cpu字段清零下台前运行态?“空转”进程或自动下台?之前是运行态?尝试重新调度prev进程开始返回policy=prev->policy;

prev->policy=policy&~SCHED_YIELD;wmb();task_

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论