FreeBSD 基礎建設 - Context Switch

本文深入探讨了FreeBSD系统中的上下文切换(Context Switch)机制,详细介绍了实现上下文切换的关键函数mi_switch、sched_switch和cpu_switch的工作原理,并分析了i386平台上具体的上下文切换过程。
FreeBSD 基礎建設 - Context Switch
by thinker
2 Columns
關鍵字:

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
豌豆代理(又称豌豆 IP)是一款一站式国内代理 IP 服务平台,主打高匿名、低延迟、高可用的 IP 资源,支持 HTTP/HTTPS/SOCKS5 协议,适配 Windows、Mac、Android、iOS 多平台。 多类型 IP 资源与高覆盖节点 提供动态住宅 IP、静态独享 IP、数据中心 IP,覆盖全国 200 + 城市,可用率 99%+;支持省市精准选择或全国混拨,适配不同业务合规与稳定性需求。 使用:在客户端 “节点 / 线路” 页,按城市 / 类型筛选,一键连接目标 IP,适合爬虫、电商多账号运营等场景。 秒级 IP 切换与灵活调度 支持手动一键切换、秒级动态切换(切换速度低至 100ms)、定时切换(自定义时长),并自动过滤重复 IP,避免重复使用导致风险。 使用:在 “设置” 中开启 “自动切换” 并设时间间隔,或按 Ctrl+Q 快捷键一键换 IP,适配反爬虫、批量测试等高频切换场景。 全协议支持与多端适配 兼容 HTTP/HTTPS/SOCKS5 主流代理协议,可对接浏览器、爬虫脚本、客户端软件;支持 Windows、Mac、安卓、iOS 多端同步使用,跨设备无缝切换。 使用:在客户端 “协议设置” 选择对应协议,生成代理地址与端口,直接填入目标软件即可生效。 隐私安全与数据加密 自研传输加密技术保护数据传输,搭配高匿名 IP 隐藏真实地址,同时支持自动清除 Cookie / 缓存,降低隐私泄露与追踪风险。 使用:在 “安全设置” 中开启 “数据加密” 与 “自动清理缓存”,公共 WiFi 环境下优先启用,提升隐私防护等级。 智能筛选与稳定网络优化 系统自动筛选低延迟、高可用 IP,过滤失效 / 重复地址;依托自建纯净机房与独享带宽,搭配 BGP 多线接入,保障连接稳定性与速度。 使用:无需手动配置,客户端默认智能匹配合适节点,复杂网络环境可在 “网络
在网络高速发展的时代,众多的软件被开发出来,给用户带来了很大的选择余地,而且人们越来越追求更个性的需求。在这种时代背景下,商家只能以用户为导向,以商品的持续创新作为商家最重要的事项。 在新发展的时代,人们对幼儿资源互助共享平台越来越重视,才能实现幼儿资源互助共享平台的有效发挥,本文将通过幼儿资源互助共享平台的信息,分析在日常生活中对幼儿资源互助共享平台存在哪些问题探讨出进一步提升效率,管理能力的对策。 系统采用了Java技术,将所有模块采用以浏览器交互的模式,选择MySQL作为系统的数据库,来进行系统的设计。基本实现了幼儿资源互助共享平台应有的主要功能模块,本系统有管理员:首页、个人中心、用户管理、卖家管理、咨询师管理、萌宝信息管理、幼儿知识管理、保姆推荐管理、音频资源管理、二手商品管理、商品分类管理、资源分类管理、交流论坛、系统管理,用户;首页、个人中心、萌宝信息管理、保姆推荐管理、音频资源管理,卖家;首页、个人中心、二手商品管理、订单管理,咨询师;首页、个人中心、幼儿知识管理,前台首页;首页、萌宝信息、幼儿知识、保姆推荐、音频资源、二手商品、交流论坛、个人中心、后台管理、购物车等功能。 对系统进行测试后,改善了程序逻辑和代码。同时确保系统中所有的程序都能正常运行,所有的功能都能操作,本系统的开发获取幼儿资源互助共享平台信息能够更加方便快捷,同时也使幼儿资源互助共享平台信息变的更加系统化、有序化。系统界面较友好,易于操作。
创新!高级!【日前、日内非滚动、日内滚动调度以及实时修正】考虑需求侧响应的智慧楼宇多时间尺度调度策略(Matlab代码实现)内容概要:本文介绍了名为“创新!高级!【日前、日内非滚动、日内滚动调度以及实时修正】考虑需求侧响应的智慧楼宇多时间尺度调度策略”的Matlab代码实现资源,重点围绕智慧楼宇在多时间尺度下的能量调度优化展开,涵盖日前调度、日内非滚动调度、日内滚动调度及实时修正四个阶段,并充分考虑需求侧响应机制,旨在提升能源利用效率与系统运行经济性。该资源提供了完整的Matlab代码实现,适用于科研复现与工程仿真,属于电力系统与智能建筑交叉领域的前沿研究内容。; 适合人群:具备一定电力系统背景、能源管理或自动化专业知识,熟悉Matlab编程,从事科研、教学或工程应用的研究生、高校教师及研发人员;尤其适合开展综合能源系统、需求响应、微电网调度等相关课题的研究者。; 使用场景及目标:①用于智慧楼宇多时间尺度优化调度模型的搭建与仿真;②支持考虑需求侧响应的电能管理策略研究;③服务于学术论文复现、毕业设计或科研项目开发,帮助理解多阶段优化架构与Matlab求解器应用。; 阅读建议:建议结合电力系统优化理论与Matlab编程实践同步学习,重点关注各调度阶段的数学建模思路与代码实现逻辑,推荐使用YALMIP+CPLEX等工具包进行求解,同时参考文中提及的网盘资料获取完整代码与案例数据以提升学习效率。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值