博文

目前显示的是标签为“linux 内核”的博文

mips 异常处理和进程切换

一.   内核栈 每个进程拥有一个内核栈,调度切换至一个新进程时,会根据 task_struct->stack(struct thread_info  结构体类型) 的值设置  *kernelsp (当前正在运行进程之内核栈栈顶),将 kernelsp 的值设置为  task_struct->stack + THREAD_SIZE - 32 ( MIPS  下,使用 set_saved_sp  宏来完成设置)。 内核栈主要用于进程运行时产生异常,将异常的现场保存在内核栈上(使用 SAVE_ALL 宏, SAVE_ALL 宏包括 SAVE_SOME 宏)。 二.   异常产生时现场的保存 使用 SAVE_SOME  保存上下文时,如发现从用户态切入核心态,则首先用  get_saved_sp  宏将 *kernelsp  置入 sp ,也就是说: 若是用户态  -->  内核态,则  k0 = sp, sp = *kernelsp - PT_SIZE , store k0, PT_R29(sp) ,保存其它寄存器。 若是内核态  -->  内核态,直接  k0 = sp, sp = sp - PT_SIZE , store k0, PT_R29(sp) ,然后保存其它寄存器。 本质是内核栈上分配  PT_SIZE ( =sizeof(struct pt_regs) )大小的空间,作为上下文的保存空间。保存时所有数据精心组织,最后就是一个  struct pt_regs  结构。 三.   进程的切换过程 进程 A -->  时钟中断  --> schedule --> switch_to(resume) --> schedule  返回  --> ret_from_irq -->  进程 B 1 、时钟中断后使用  SAVE_ALL  在内核栈上保存  $0, $2, $3,...

linux 页表

简单来说,讨论linux页表就是讨论linux进程的的页表:linux页表的创建与更新都包含于进程的创建与更新中。当前的linux内核采用的是写时复制方法,在创建一个linux进程时,完全复制父进程的页表,并且将父子进程的页表均置为写保护(即写地址的时候会产生缺页异常等)。那么父子进程谁向地址空间写数据时,产生缺页异常,分配新的页,并将两个页均置为可写,按照这种方式父子进程的地址空间渐渐变得不同(也即页表变得不同)。 按照上面的分析, 只需要讨论第一个进程页表初始化,进程创建时页表的拷贝,以及缺页异常时页表的更新即可。 1.init_task进程页表的初始化 init_task的地址空间是init_mm, init_mm在内核初始化的时候就赋值给了current->active_mm. init_mm的初始化页表是swapper_pg_dir,在mips架构中swapper_pg_dir初始化在函数pagetable_init中,初始化关系是 swapper_pg_dir -> invalide_pmd_table -> invalide_pte_table 或 swapper_pg_dir -> invalide_pte_table. 即在init_mm中,页表指向的全部是invalide_pte_table。 2.创建进程时页表的拷贝 进程创建一般调用的是do_fork函数,按照如下调用关系: do_fork->copy_process->copy_mm->dup_mm->dup_mmap->copy_page_range 找到copy_page_range函数,这个函数便是负责页表的拷贝,函数核心代码如下: 874 do { 875 next = pgd_addr_end(addr, end); 876 if (pgd_none_or_clear_bad(src_pgd)) 877 continue; 878 if (unlikely(copy_pud_range(dst_mm, src_mm, dst_pgd, src_pgd, 879 vma, addr, next))) { 880 ret = -ENOMEM; 881 br...

qemu kvm 内存设备虚拟化

一、qemu中物理内存的注册 cpu_register_physical_memory调用cpu_notify_set_memory cpu_notify_set_memory调用kvm_client_set_memory kvm_client_set_memory调用kvm_set_phys_mem kvm_set_phys_mem调用kvm_set_user_memory_region kvm_set_user_memory_region调用的kvm_vm_ioctl进入内核 内核中会 调用kvm_vm_ioctl_set_memory_region最终调用到__kvm_set_memory_region函数 在__kvm_set_memory_region函数中有如下代码: 738 ____slots->memslots[mem->slot] = new; 739 ____old_memslots = kvm->memslots; 740 ____rcu_assign_pointer(kvm->memslots, slots); 741 ____synchronize_srcu_expedited(&kvm->srcu); 因此函数__kvm_set_memory_region本质是创建并填充了一个临时kvm_memslots结构,并把其赋值给kvm->memslots(全局的)。 二、处理用户态虚拟的地址(主要考虑tlb不能命中的情况) 1、查物理tlb如果不能命中会调用host中do_kvm_tlbmiss 2、do_kvm_tlbmiss会先判断地址是IO地址还是访存的地址,如果是访存地址,会进一步查guest tlb表,如果查guest tlb还没有命中,就会把guest tlb miss异常注入到guest系统中,guest kernel会根据页表来填充guest tlb,当guest调用TLBWI特权指令时,会再次陷入host中,调用do_kvm_cpu异常处理 3、在do_kvm_cpu中模拟TLBWI指令,先填充guest tlb 表项,在调用kvmmips_update_shadow_tlb来更新物理tlb(shadow tlb) 4、在kvmmips_update_shadow_tlb中,通过gf...