内核级线程的地址空间探秘

881 阅读3分钟

最近一直在看线程进程这一块,学的时候想到这个问题在这里记录一下。

首先我们先回顾一下两个用户态的进程切换的过程。每一个用户态的进程都会对应一个内核级的线程。操作系统调度的实际是内核级的线程。之后内核级线程再返回用户态。举个例子比如有两个进程A,C 他们对应的内核级线程是B, D。那么A到C切换的过程是 A找到对应的内核级线程B,进入内核态。然后Linux调度器找到下一个就绪的内核级线程D,切换到D,然后找到D对应的用户级的进程然后返回用户态完成切换。即A->(进入内核态)C->D->(返回用户态)B。仔细想一想可以发现一个问题,我们在切换线程肯定是要切换堆栈的SP和PC的。切换就要知道对应的地址是什么。操作系统中存的都是虚拟地址。而进程的地址空间都是独立的,即每个进程的映射关系是不一样的。那么从C->D切换这个过程用的是谁的地址空间?如果用的是A或者C的那么另一个就可能存在找不到的问题。我的第一感觉是进程的内核态映射关系对于不同进程应该是一样的。下面就来研究一下这个问题到底是不是这么回事。

在操作系统里面管理进程的是PCB,而PCB这个东西在Linux操作系统里就是task_struct。那么我们就要研究一下这个task_struct里有什么,是什么来管理虚拟地址空间的。看了一下task_struct里面有一个mm字段存储的是mm_struct的数据类型,就是这个玩意管理的这个进程的地址空间。不同的进程就靠这里面的信息来区分不同的地址空间。然后切换到内核状态后这个字段就会变成NULL!这么看来确实内核态的线程地址空间和用户态的地址空间不完全一样。又看了一下资料原来内核的页表在内核初始化的时候就会自己分配好由内核自己管理,每个进程创建的时候拷贝这个资料来初始化自己虚拟地址空间中的内核的部分。也就是说不同进程的虚拟地址空间中内核态的映射规则是一样的。那么内核态中的mm字段为null又去哪里找相关信息呢?PCB还有个active_mm字段来记录当前活跃进程的mm,但是对内核级线程来说是上一个内核线程对应的mm_struct(因为内核态线程的值为NULL),而不同的进程的内核地址空间又是一样的那么只要用我对应的用户态的虚拟地址空间就完事了。

参考资料

www.cnblogs.com/Ph-one/p/84…

blog.csdn.net/tiankong_/a…