进程、轻量级进程(LWP)、线程
2012-04-12 20:06
260 查看
进程、轻量级进程(LWP)、线程
进程、轻量级进程(LWP)、线程 进程:程序执行体,有生命期,用来分配资源的实体 线程:分配CPU的实体。 用户空间实现,一个线程阻塞,所有都阻塞。 内核实现,不会所用相关线程都阻塞。用LWP实现,用线程组表示这些线程逻辑上所属的进程。 进程描述符 进程描述符(简称pd, process descriptors),结构体是:task_struct 数据较多,存放在kenerl的动态内存空间。 pd的引用放在thread_info中, thread_info与内核栈,放在一个8K空间(它的地址8K对齐)。内核程序使用的栈空间很小。 thread_info在底部,内核栈在顶部向下增长。 好处:多CPU时方便,每个CPU根据自己的栈指针就可以找到当前的pd (以后用current表示当前CPU运行的进程描述符)。 esp(内核栈指针)低8位置零,就是thread_info地址。 每进程有自己的thread_info, (分配释放函数: alloc_thread_info, free_thread_info) 描述符的内容 相关的ID (一个4元素数组) 进程ID (PID) PID按创建顺序连续增长,到最大值后从最小值开始。 0号进程:交换进程(swapper) 有PID可用位图,表示那一个PID可用,至少占一个页。 线程组ID(tgid),用LWP实现多线程支持 多进程时,进程id,就是线程组id, 也就是组长的pid(LWP)。 getpid() 取的是线程组的id(tgid), 也是组长的pid. 单线程时,pid = gid。所以getpid,也是真正的pid. 进程组ID(pgrp)。 回话的ID(session). 组ID,都是组长的PID。FIXME: 但pb也有各组长的PID 线程组长:tgid 进程组长:signal->pgrp , 会话长:signal->session 管理ID数据结构——哈希表管理 (利用id找到所用相关的pd,方便)。 一个哈希表数组(pid_hash),存放四个哈希表, 每一个表代表一类id (pid, tgid, pgrp, session) 每个哈希表的由数组(索引为哈希值)和二维链表(嵌入到进程描述符内的pids中)实现 第一维链表:哈希冲突链表。 第二维链表:要查找的值相同的链表, 叫per-PID list(同一组的所有线程,同一组的所有进程,同一会话的所有进程); 进程组ID(pgrp), 回话ID(session)在共享信号的数据结构里。因为同一进程内的所有LWP,这两个ID都是一样的 家族关系:由pd里的链表(下级)和pd指针(上级)实现 关系: 亲生父亲:创建自己的进程,或是托孤进程(创建自己的进程死了)。 父亲:自己死时要发信号告知的。一般是亲生父亲,有时是监控自己的进程 (调用ptrace) 孩子: 兄弟: 监控(自己起的名字,类似于监护。由于管理方式相同,也归为家族关系) 监控的进程列表:ptrace_children 被监控的其他进程:ptrace_list (类似于被监控的兄弟) 在链表里为了管理方便: 最大儿子的兄弟是父亲 最小儿子的弟弟也是父亲 父亲保管最大儿子,和最小儿子 进程资源及资源限制: CPU相关: 占用CPU总时间 用户的最大进程数 内存相关: 进程地址空间 锁住内存大小 进程页数 (只有记录,没有限制) 堆大小,栈大小 资源相关: 文件: core dump大小 最大文件大小 打开文件个数 进程同步与通信 锁数目, 悬挂信号数据 在消息列队中占的大小 相关数据结构 和 处理流程 pd->sigal->rlim 是一个表示进程资源使用情况以及限制的结构 的数组。 表示进程资源使用情况以及限制的结构:包含当前值,最大值两个数值。 只有超级用户才能增大资源限制。 一般用户登陆时: kernel创建root进程,减少limit, 建一个 shell子进程,继承limit. 把shell进程的用户,改成登陆的那个用户 进程状态(state) 运行,TASK_RUNNING 组织pd的结构:就绪进程链: 一个CPU一组链表,每个链表表示一种优先级。 阻塞 可中断阻塞,TASK_INTERRUPTIBLE 可被硬件中断,“释放资源”事件,信号唤醒。 不可中断阻塞,TASK_UNINTERRUPTIBLE 可被硬件中断,“释放资源”事件,唤醒。 但不能被信号唤醒。可用于驱动程序中。 组织pb的结构:等待列队: 每一类事件一个列队,用内嵌链表实现(虽然没列出内嵌链表节点) 列队头: 自旋锁:防止有一个主函数和中断函数同时操作列队。 列队节点: 独占标志:表示该进程是否要独占资源 (不再唤醒别的进程) 指向pd的指针 用于唤醒进程的回调函数。(提供进程的执行机会,是否操作等待列队由用户决定) 停止 停止TASK_STOPPED 被信号停止 追踪TASK_TRACED 该进程被一个调试进程监控以后,收到任何一个信号就进入该状态 组织pb的结构:FIXME: 信号的等待列队? 退出 退出_僵尸EXIT_ZOMBIE 进程终止,资源没有被回收(父进程要用,没有调wait系列函数) 退出_死亡EXIT_DEAD 进程终止,资源正在被回收(父进程要用,没有调wait系列函数)。 一旦资源回收完成,进程描述符也就被回收了。 它防止该进程再次被wait. 组织pb的结构:不挂到队列上,只在家族关系中,等待父进程收回资源 进 程控制 : 阻塞(current阻塞到某个列队上): 基本流程 临时生成一个列队节点,初始化。 改变current的状态,放入节点,挂到列队上。 调度 (=====》至此,阻塞完成。 一旦被别的进程唤醒====》从调度函数中返回) 从等待列队上摘除节点。 变化: 将挂列队、调度、从列队删除三步拆开,便于灵活处理。 可中断的、限时、独占的函数类似。只不过进程状态、调度函数、独占标志不同。 非独占的从列队开始添加,独占的从末尾添加。(但一个列队内既有独占的,又有非独占的等待进程,很少见) 唤醒: 基本流程 唤醒一个进程:调用节点里的回调函数 唤醒的时候从列队开头依次唤醒,直到唤醒一个独占的后停止。 变化 是否只唤醒可中断的进程. (_interruptible后缀) 唤醒的独占进程的数目(1个,多个(_nr后缀),所有(_all后缀)) 唤醒后是否不检查优先级,马上给予CPU (有_sync的不检查优先级)。 进程切换 切换pgd (全局页目录),此章不讨论。 切换内核栈,硬件上下文 硬件上下文,就是CPU的寄存器。 一部分(大多数CPU寄存器(除了通用寄存器))在pd中保存(task_struct->thread, 类型是thread_struct), 一部分(通用寄存器)保存在内核栈中. 原来用硬件指令()保存CPU信息。后来改成软件(一个个MOV指令) 容易控制,可以挑选信息保存,便于优化。不保存的做其他用(如:进程间传递) far jmp:跳至目标进程的TSSD。而linux是每个CPU一个TSS,不是每进程一个 对于一些寄存器(ds、es)可以检查值。 与用硬件指令保存时间差不多。 switch_to 宏 三个参数: prev: 要换走的进程,一般是当前进程 next: 要换到的进程。 last: 传出参数。当前进程再次被换到时,最后一个占用CPU的进程。(prev指向的进程 就是 next指向的进程 的last) 步骤: 栈切换, 完成后就是在新进程的上执行了: 保存prev(放在eax) eflags,ebp入内核栈; 保存并装载新的esp (旧的esp放到prev->thread.esp,新的esp是next->thread.esp) 此时current就是新的esp所指的thread_info内的task指针 设置返回地址: prev进程以后得到执行时的__switch_to的返回地址: __switch_to后的第一条指令, 放入prev->thread.eip, 准备next进程的从__switch_to返回的地址: next->thread.eip入栈. 调用__switch_to ()函数,该函数动作如下: 更新CPU的相关信息(tss和gdt): 存next->thread.esp0(内核栈低)到本地TSS.esp0中。 所在CPU的全局段表里的TLS段, 设成next进程的. 更新tss的I/O位图. 更新CPU的寄存器(pd->thread (tss) 与 CPU寄存器交换数据): 保存FPU, MMX, XMM寄存器, 先不装载以后需要时通过中断装载(TODO: ) 保存prev的fs, gs寄存器. 装载next的 装载next的debug寄存器(debug寄存器一个8个, 进程切换时只需6个) 返回 prev放入eax (prev就是新进程的last) ret ret返回的地址: (__switch_to之前被存入栈中, __switch_to ret时进入eip) 如果是next新进程, next->thread.eip是iret_from_fork. 如果next不是新进程: 弹出ebp, elfags 把eax放入last变量 (prev就是next进程的last) 任务状态段(一个存CPU状态的数组,tss_struct init_tss[]) 每个CPU用段上的一个元素。(FIXME: 用于:用户模式要进入内核模式时,设置相应寄存器) TSS上存内核栈地址。CPU上的程序从用户模式转到内核模式,设置esp。 TSS存I/O端口许可位图。用户模式程序用到I/O时,检查有无权限 所以,进程切换时,要保存的寄存器在pd->thread中。 thread_struct不是thread_info。thread_info中只有少量的数据或指针, 用于通过esp快速定位数据 进程切换时,更新TSS上的信息。 CPU控制单元再从TSS上取需要的信息。 即反应了CPU的当前进程情况,又不需要维护所有进程的状态数据。 TSS的描述符在GDT里。 TSSD:任务状态段描述符 (其实应该叫任务状态描述符,每个TSSD,表示一个CPU的状态, FIXME: :具体以源码为准) CPU原始设计,每个进程一个TSS元素。 linux设计,每个CPU一个TSS元素。 cpu里的tr寄存器,保存着自己的TSSD(即init_ttss[cpu_id]),不用总上gdt里去取。 |
相关文章推荐
- 转载_进程、轻量级进程(LWP)、线程
- 进程、轻量级进程(LWP)、线程
- Linux下的LWP(轻量级进程)、进程 、 线程、用户级线程、内核线程
- Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
- Linux线程 之 线程 线程组 进程 轻量级进程(LWP) -systemtap -mysql
- Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
- 进程、线程、轻量级进程(LWP)
- Linux线程 之 线程 线程组 进程 轻量级进程(LWP)
- 进程、轻量级进程(LWP)、线程
- 进程、轻量级进程(LWP)、线程
- 内核线程、轻量级进程、用户线程
- 内核线程、轻量级进程、用户线程
- 内核线程、轻量级进程、用户线程三种线程概念解惑(线程≠轻量级进程)【转】
- linux内核线程、轻量级进程、用户进程
- 关于进程、线程、轻量级进程的笔记
- 内核线程、轻量级进程、用户线程
- 内核线程、轻量级进程、用户线程
- Go语言 进程、线程、轻量级进程、协程和go中的Goroutine 那些事儿
- 三种线程——内核线程、轻量级进程、用户线程
- 内核线程、轻量级进程、用户线程的区别和联系