switch_mm()进行用户空间的切换,更确切地说,是切换地址转换表(pgd),由于pgd包括进程
系统空间(0xc000 0000 ~ 0xffff ffff)和
用户空间(0x0000 0000 ~ 0xbfff ffff)的地址映射,但是由于所有进程的系统空间的地址映射都是相同的。所以实质上就是进行用户空间的切换。
至于LDT则仅在VM86模式中才使用,所以不在我们关心之列。
将进程next的页目录表首地址next->pgd转换成物理地址,并将其写入寄存器%%cr3中
- 每个进程都有其自身的页目录表pgd
- 读者也许会问:进程本身尚未切换,而存储管理机制的页目录指针cr3却已经切换了,这样不会造成问题吗?不会的,因为这个时候CPU在系统空间运行,而所有进程的页目录表中与系统空间对应的目录项都指向相同的页表,所以,不管切换到哪一个进程的页目录表都一样,受影响的只是用户空间,系统空间的映射则永远不变
static
inline
void
switch_mm(
struct
mm_struct
*
prev,
struct mm_struct * next,
struct task_struct * tsk)
{
int cpu = smp_processor_id();
if (likely(prev != next)) {
cpu_clear(cpu, prev->cpu_vm_mask);
#ifdef CONFIG_SMP
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
per_cpu(cpu_tlbstate, cpu).active_mm = next;
#endif
cpu_set(cpu, next->cpu_vm_mask);
load_cr3(next->pgd);
if (unlikely(prev->context.ldt != next->context.ldt))
load_LDT_nolock(&next->context, cpu);
}
#ifdef CONFIG_SMP
else {
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
BUG_ON(per_cpu(cpu_tlbstate, cpu).active_mm != next);
if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) {
load_cr3(next->pgd);
load_LDT_nolock(&next->context, cpu);
}
}
#endif
}
struct mm_struct * next,
struct task_struct * tsk)
{
int cpu = smp_processor_id();
if (likely(prev != next)) {
cpu_clear(cpu, prev->cpu_vm_mask);
#ifdef CONFIG_SMP
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
per_cpu(cpu_tlbstate, cpu).active_mm = next;
#endif
cpu_set(cpu, next->cpu_vm_mask);
load_cr3(next->pgd);
if (unlikely(prev->context.ldt != next->context.ldt))
load_LDT_nolock(&next->context, cpu);
}
#ifdef CONFIG_SMP
else {
per_cpu(cpu_tlbstate, cpu).state = TLBSTATE_OK;
BUG_ON(per_cpu(cpu_tlbstate, cpu).active_mm != next);
if (!cpu_test_and_set(cpu, next->cpu_vm_mask)) {
load_cr3(next->pgd);
load_LDT_nolock(&next->context, cpu);
}
}
#endif
}
将进程next的页目录表首地址next->pgd转换成物理地址,并将其写入寄存器%%cr3中
#define
load_cr3(pgdir)
asm volatile ( " movl %0,%%cr3 " : : " r " (__pa(pgdir)))
asm volatile ( " movl %0,%%cr3 " : : " r " (__pa(pgdir)))
- 内核将新进程的页目录表的物理地址写入cr3控制寄存器时,会自动刷新旧页表的本地TLB表项