内核资料收集
1. 进程/轻量级进程/线程
一般可认为,进程是程序执行时的一个实例,从内核角度看,进程是分配系统资源(cpu时间,内存等)的实体。
进程创建时,几乎与父进程相同。他接收父进程的地址空间的一个副本,并从进程创建系统调用的下一条指令开始
执行与父进程相同的代码。虽然父子进程共享含有程序代码的页,但他们有各自独立的数据拷贝。因此父子进程各
自对自己的内存单元的修改时,对对方都是不可见的。
线程可认为是一个独立执行流,可与其它执行流共享大部分的数据结构。一个进程可以拥有多个这样的执行流
linux用轻量级进程(lightweight process)实现对多线程应用提供支持。linux通过把轻量级进程与每个线程关联,这样
线程之间可以通过简单地共享地址空间,打开文件集等,以访问相同的的应用程序数据,同时每个线程由内核独立
调度,以便一个睡眠的同时另一个仍然可以运行。
2.进程描述符
进程描述符包含了一个进程的所有信息。下图示意性描述了linux的进程描述符task_struct。
2.1 标识一个进程
每个能被独立调度的执行上下文,都必须拥有自己独立的进程描述符。即使共享大部分数据结构的轻量级进程,也有自己的
task_struct结构。进程和进程描述符之间有严格的一一对应关系。内核对进程的大部分引用,是通过进程描述符指针进行的。
类unix操作系统用进程标识符processID(PID)标识进程。PID被顺序编号。默认的PID上限是32767;系统管理员可以设置一
个更小的值来减小PID上限值。内核通过pidmap-array位图管理PID。一个页框包含4096x8=32768个位,所以32位体系结构中
的
pidmap-array位图可以放在一个单独的页中。在64位系统中需要使用更多的页管理PID位图。
2.3 线程相关
linux引入线程组的表示。一个线程组中的所有线程,使用该线程组的领头线程相同的PID。PID存入到进程描述符的tgid字段。getpid()返回的是当前进程的tgid而不是pid的值
2.4 进程描述符的处理
linux把内核态的进程堆栈,及紧挨进程描述符的thread_info(叫做线程描述符)紧凑的存放在一个单独为进程分配的存储区域内;
这块存储区域的大小通常是8192字节(两个页框)。考虑效率因素,内核让这8KB空间占据连续的两个页框,并让第一个页框的地址是
2^13的倍数。下图,线程描述符驻留于这个内存区的开始,而栈从末端向下增长。thread_info结构与task_struct结构通过task和
thread_info字段互相关联。
在80x86系统中,栈起始于末端,并朝内存区开始的方向增长。从用户态进入内核态后,进程的内核栈总是空的,即栈指针esp
指向栈顶。数据入栈,esp递减。thread_info结构52字节长,因此内核栈能扩展到8192-52=8140字节。
union_thread_union {
struct thread_info thread_info;
unsigned long stack[2048]; //对4K的栈数组下标是1024
}
内核使用alloc_thread_info和free_thread_info宏分配和释放存储thread_info结构和内核栈的内存区。
2.5 标识当前进程
thread_info结构与内核态堆栈之间的紧密结合提供的主要好处是:内核很容易从当前内核栈指针(80x86上是esp)取得当前运行的
进程的thread_info结构地址。如果thread_union结构长度是8K(2^13), 则esp的低13位清零就可得到thread_info结构的基地址。
current_thread_info()函数做此工作。
内核调用current宏获得当前CPU上运行进程的描述符指针,其本质上等价于
current_thread_info()->task。进一步,进程的其它
数据都可以得到。早先的linux内核通过引入全局静态变量current来标识正在运行进程的描述符。
阅读(1473) | 评论(0) | 转发(0) |