FreeBSD 基礎建設 - Context Switch

FreeBSD 基礎建設 - Context Switch
by thinker
關鍵字:

Context Switch

在 multi-tasking 的作業系統中,kernel 不斷的切換 CPU 於各個工作間。FreeBSD kernel 以 thread 為工作的單位,透過不斷的切換,達成多工的目的。切換 thread 的過程,可以切割成三個步驟:

  1. 選擇下一個 thread
  2. 保存目前 thread 的狀態
  3. 切換到新的 thread
這裡不討論如何選擇下一個 thread,只討論保存和切換 thread 的方法。

下面是 FreeBSD kernel 實作 context switch 的三個重要 function:

  • mi_switch(int flags, struct thread *newtd) /* in kern_synch.c */
  • void sched_switch(struct thread *td, struct thread *newtd, int flags) /* in sched_*.c */
  • void cpu_switch(struct thread *old, struct thread *new) /* in sys/proc.h , i386/i386/swtch.s */

cpu_switch()

cpu_switch() 將 CPU 狀態儲存在 thread object 的 PCB 裡,並從 new thread 的 PCB 裡,將 CPU 狀態回復,以切換成 new thread 。由於 CPU 的不同,CPU 的狀態內容也不同,下面是 i386 定義的 PCB ,儲存 CPU 的 register 內容和 gs 和 fs 的 segment descriptor 。PCB 內容,於 kernel 其它部分而言,是一個黑盒子,不知其內容,只是將之視為一塊存放 CPU 狀態的記憶體。

struct pcb {                                                                    
int pcb_cr3;
int pcb_edi;
int pcb_esi;
int pcb_ebp;
int pcb_esp;
int pcb_ebx;
int pcb_eip;

int pcb_dr0;
int pcb_dr1;
int pcb_dr2;
int pcb_dr3;
int pcb_dr6;
int pcb_dr7;

union savefpu pcb_save;
u_int pcb_flags;
#define FP_SOFTFP 0x01 /* process using software fltng pnt emulator */
#define PCB_DBREGS 0x02 /* process using debug registers */
#define PCB_NPXTRAP 0x04 /* npx trap pending */
#define PCB_NPXINITDONE 0x08 /* fpu state is initialized */
#define PCB_VM86CALL 0x10 /* in vm86 call */

caddr_t pcb_onfault; /* copyin/out fault recovery */
int pcb_gs;
struct segment_descriptor pcb_fsd;
struct segment_descriptor pcb_gsd;
struct pcb_ext *pcb_ext; /* optional pcb extension */
int pcb_psl; /* process status long */
u_long pcb_vm86[2]; /* vm86bios scratch space */
};

下面程式碼,是取自 i386/i386/swtch.s ,儲存 CPU 狀態,包括所有 register 的內容。

ENTRY(cpu_switch)                                                               

/* Switch to new thread. First, save context. */
movl 4(%esp),%ecx

#ifdef INVARIANTS
testl %ecx,%ecx /* no thread? */
jz badsw2 /* no, panic */
#endif

movl TD_PCB(%ecx),%edx

movl (%esp),%eax /* Hardware registers */
movl %eax,PCB_EIP(%edx)
movl %ebx,PCB_EBX(%edx)
movl %esp,PCB_ESP(%edx)
movl %ebp,PCB_EBP(%edx)
movl %esi,PCB_ESI(%edx)
movl %edi,PCB_EDI(%edx)
movl %gs,PCB_GS(%edx)
pushfl /* PSL */
popl PCB_PSL(%edx)
/* Test if debug registers should be saved. */
testl $PCB_DBREGS,PCB_FLAGS(%edx)
jz 1f /* no, skip over */
movl %dr7,%eax /* yes, do the save */
movl %eax,PCB_DR7(%edx)
andl $0x0000fc00, %eax /* disable all watchpoints */
movl %eax,%dr7
movl %dr6,%eax
movl %eax,PCB_DR6(%edx)
movl %dr3,%eax
movl %eax,PCB_DR3(%edx)
movl %dr2,%eax
movl %eax,PCB_DR2(%edx)
movl %dr1,%eax
movl %eax,PCB_DR1(%edx)
movl %dr0,%eax
movl %eax,PCB_DR0(%edx)
1:

#ifdef DEV_NPX
/* have we used fp, and need a save? */
cmpl %ecx,PCPU(FPCURTHREAD)
jne 1f
addl $PCB_SAVEFPU,%edx /* h/w bugs make saving complic$
pushl %edx
call npxsave /* do it in a big C function */
popl %eax
1:
#endif

將 cr3 切換到新 thread 的 page table (page directory table) 。

        /* Save is done.  Now fire up new thread. Leave old vmspace. */         
movl 8(%esp),%ecx /* New thread */
#ifdef INVARIANTS
testl %ecx,%ecx /* no thread? */
jz badsw3 /* no, panic */
#endif
movl TD_PCB(%ecx),%edx
movl PCPU(CPUID), %esi

/* switch address space */
movl PCB_CR3(%edx),%eax
#ifdef PAE
cmpl %eax,IdlePDPT /* Kernel address space? */
#else
cmpl %eax,IdlePTD /* Kernel address space? */
#endif
je sw1
movl %cr3,%ebx /* The same address space? */
cmpl %ebx,%eax
je sw1
movl %eax,%cr3 /* new address space */

修改新舊 pmap 的 active CPU,舊的清成空 (沒有 active CPU),新的設定成目前 CPU 。

        /* Release bit from old pmap->pm_active */                              
movl PCPU(CURPMAP), %ebx
#ifdef SMP
lock
#endif
btrl %esi, PM_ACTIVE(%ebx) /* clear old */

/* Set bit in new pmap->pm_active */
movl TD_PROC(%ecx),%eax /* newproc */
movl P_VMSPACE(%eax), %ebx
addl $VM_PMAP, %ebx
movl %ebx, PCPU(CURPMAP)
#ifdef SMP
lock
#endif
btsl %esi, PM_ACTIVE(%ebx) /* set new */

將 CPU 狀況恢復成新 thread 前一次執行的最後狀態

sw1:                                                                            
/*
* At this point, we've switched address spaces and are ready
* to load up the rest of the next context.
*/
cmpl $0, PCB_EXT(%edx) /* has pcb extension? */
je 1f /* If not, use the default */
movl $1, PCPU(PRIVATE_TSS) /* mark use of private tss */
movl PCB_EXT(%edx), %edi /* new tss descriptor */
jmp 2f /* Load it up */

1: /*
* Use the common default TSS instead of our own.
* Set our stack pointer into the TSS, it's set to just
* below the PCB. In C, common_tss.tss_esp0 = &pcb - 16;
*/
leal -16(%edx), %ebx /* leave space for vm86 */
movl %ebx, PCPU(COMMON_TSS) + TSS_ESP0

/*
* Test this CPU's bit in the bitmap to see if this
* CPU was using a private TSS.
*/
cmpl $0, PCPU(PRIVATE_TSS) /* Already using the common? */
je 3f /* if so, skip reloading */
movl $0, PCPU(PRIVATE_TSS)
PCPU_ADDR(COMMON_TSSD, %edi)
2:
/* Move correct tss descriptor into GDT slot, then reload tr. */
movl PCPU(TSS_GDT), %ebx /* entry in GDT */
movl 0(%edi), %eax
movl 4(%edi), %esi
movl %eax, 0(%ebx)
movl %esi, 4(%ebx)
movl $GPROC0_SEL*8, %esi /* GSEL(GPROC0_SEL, SEL_KPL) */
ltr %si
3:

/* Copy the %fs and %gs selectors into this pcpu gdt */
leal PCB_FSD(%edx), %esi
movl PCPU(FSGS_GDT), %edi
movl 0(%esi), %eax /* %fs selector */
movl 4(%esi), %ebx
movl %eax, 0(%edi)
movl %ebx, 4(%edi)
movl 8(%esi), %eax /* %gs selector, comes straight after *$
movl 12(%esi), %ebx
movl %eax, 8(%edi)
movl %ebx, 12(%edi)

/* Restore context. */
movl PCB_EBX(%edx),%ebx
movl PCB_ESP(%edx),%esp
movl PCB_EBP(%edx),%ebp
movl PCB_ESI(%edx),%esi
movl PCB_EDI(%edx),%edi
movl PCB_EIP(%edx),%eax
movl %eax,(%esp)
pushl PCB_PSL(%edx)
popfl

movl %edx, PCPU(CURPCB)
movl %ecx, PCPU(CURTHREAD) /* into next thread */

/*
* Determine the LDT to use and load it if is the default one and
* that is not the current one.
*/
movl TD_PROC(%ecx),%eax
cmpl $0,P_MD+MD_LDT(%eax)
jnz 1f
movl _default_ldt,%eax
cmpl PCPU(CURRENTLDT),%eax
je 2f
lldt _default_ldt
movl %eax,PCPU(CURRENTLDT)
jmp 2f
1:
/* Load the LDT when it is not the default one. */
pushl %edx /* Preserve pointer to pcb. */
addl $P_MD,%eax /* Pointer to mdproc is arg. */
pushl %eax
call set_user_ldt
addl $4,%esp
popl %edx
2:

/* This must be done after loading the user LDT. */
.globl cpu_switch_load_gs
cpu_switch_load_gs:
movl PCB_GS(%edx),%gs

/* Test if debug registers should be restored. */
testl $PCB_DBREGS,PCB_FLAGS(%edx)
jz 1f

/*
* Restore debug registers. The special code for dr7 is to
* preserve the current values of its reserved bits.
*/
movl PCB_DR6(%edx),%eax
movl %eax,%dr6
movl PCB_DR3(%edx),%eax
movl %eax,%dr3
movl PCB_DR2(%edx),%eax
movl %eax,%dr2
movl PCB_DR1(%edx),%eax
movl %eax,%dr1
movl PCB_DR0(%edx),%eax
movl %eax,%dr0
movl %dr7,%eax
andl $0x0000fc00,%eax
movl PCB_DR7(%edx),%ecx
andl $~0x0000fc00,%ecx
orl %ecx,%eax
movl %eax,%dr7
1:
ret

FreeBSD 以 cpu_switch() 為基礎,提供誇 CPU 平臺的 context switch 能力。

sched_switch()

sched_switch() 是由 scheduler 提供的 function ,從 run queue 挑選下一個被執行的 thread ,並呼叫 cpu_switch() 切換 CPU 狀態,執行新的 thread 。目前 FreeBSD 提供三種 scheduler ,分別於 kern/sched_core.c 、 kern/sched_ule.c 和 kern/sched_4bsd.c ,各自有自己的 schedule 方式,應該有各自的 sched_switch() ,以挑選下一個 thread 。

sched_switch() 的主要功能是決定執行順序,真正進行 context switch ,還是呼叫 cpu_switch() 進形。因此,如果你想設計新的 scheduler ,最重要的就是 implement sched_switch() ,決定 thread 的執行次序,並呼叫 cpu_switch() 進行切換。

mi_switch()

mi_switch() 是屬於和 schedule 和 CPU 平臺無關的部分。主要工作是進行資料統計,和環境的檢查。統計資料包括 CPU 使用量,執行時間長短,切換的時間點。做完這些統計之後,就呼叫 sched_switch() ,以切換到下一個合適的 thread。

 
转载自:http://heaven.branda.to/~thinker/GinGin_CGI.py/show_id_doc/172
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
目标检测(Object Detection)是计算机视觉领域的一个核心问题,其主要任务是找出图像中所有感兴趣的目标(物体),并确定它们的类别和位置。以下是对目标检测的详细阐述: 一、基本概念 目标检测的任务是解决“在哪里?是什么?”的问题,即定位出图像中目标的位置并识别出目标的类别。由于各类物体具有不同的外观、形状和姿态,加上成像时光照、遮挡等因素的干扰,目标检测一直是计算机视觉领域最具挑战性的任务之一。 二、核心问题 目标检测涉及以下几个核心问题: 分类问题:判断图像中的目标属于哪个类别。 定位问题:确定目标在图像中的具体位置。 大小问题:目标可能具有不同的大小。 形状问题:目标可能具有不同的形状。 三、算法分类 基于深度学习的目标检测算法主要分为两大类: Two-stage算法:先进行区域生成(Region Proposal),生成有可能包含待检物体的预选框(Region Proposal),再通过卷积神经网络进行样本分类。常见的Two-stage算法包括R-CNN、Fast R-CNN、Faster R-CNN等。 One-stage算法:不用生成区域提议,直接在网络中提取特征来预测物体分类和位置。常见的One-stage算法包括YOLO系列(YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5等)、SSD和RetinaNet等。 四、算法原理 以YOLO系列为例,YOLO将目标检测视为回归问题,将输入图像一次性划分为多个区域,直接在输出层预测边界框和类别概率。YOLO采用卷积网络来提取特征,使用全连接层来得到预测值。其网络结构通常包含多个卷积层和全连接层,通过卷积层提取图像特征,通过全连接层输出预测结果。 五、应用领域 目标检测技术已经广泛应用于各个领域,为人们的生活带来了极大的便利。以下是一些主要的应用领域: 安全监控:在商场、银行
健身国际俱乐部系统是一种专为健身俱乐部设计的管理软件,它通过集成多种功能来提高俱乐部的运营效率和服务质量。这类系统通常包含以下几个核心模块: 1. **会员管理**:系统能够记录会员的基本信息、会籍状态、健身历史和偏好,以及会员卡的使用情况。通过会员管理,俱乐部可以更好地了解会员需求,提供个性化服务,并提高会员满意度和忠诚度。 2. **课程预约**:会员可以通过系统预约健身课程,系统会提供课程时间、教练、地点等详细信息,并允许会员根据个人时间表进行预约。这有助于俱乐部合理安排课程,避免资源浪费。 3. **教练管理**:系统可以管理教练的个人信息、课程安排、会员反馈等,帮助俱乐部评估教练表现,优化教练团队。 4. **财务管理**:包括会员卡销售、课程费用、私教费用等财务活动的记录和管理,确保俱乐部的财务透明度和准确性。 5. **库存管理**:对于俱乐部内的商品销售,如健身装备、营养补充品等,系统能够进行库存管理,包括进货、销售、库存盘点等。 6. **数据分析**:系统能够收集和分析会员活动数据,为俱乐部提供业务洞察,帮助俱乐部制定更有效的营销策略和业务决策。 7. **在线互动**:一些系统还提供在线平台,让会员可以查看课程、预约私教、参与社区讨论等,增强会员之间的互动和俱乐部的社区感。 8. **移动应用**:随着移动设备的普及,一些健身俱乐部系统还提供移动应用,方便会员随时随地管理自己的健身计划。 9. **安全性**:系统会确保所有会员信息的安全,采取适当的数据加密和安全措施,保护会员隐私。 10. **可扩展性**:随着俱乐部业务的扩展,系统应该能够轻松添加新的功能和服务,以适应不断变化的市场需求。 健身国际俱乐部系统的选择和实施,需要考虑俱乐部的具体需求、预算和技术能力,以确保系统能够有效地支持俱乐部的运营和发展。通过这些系统的实施,健身俱乐部能够提供更加专业和高效的服务,吸引和保留更多的会员,从而在竞争激烈的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值