您的位置:首页 > 运维架构 > Linux

Linux中的进程

2015-09-02 10:45 387 查看
这篇文章算是自己最近学习Linux内核设计与实现的笔记, 主要是对学到的东西做一个总结, 让自己对知识的理解更加清晰.
 

进程是什么

进程就是处于执行期的程序.Linux的进程产生方式很特别, 被分解到了fork()和exec()两个系统调用中.
进程有时候也被称作任务(task),这是从进程调度的角度去看进程的. 如果是从资源管理的角度, 我们还是把它们叫做process.

线程和内核线程等又是什么

Linux将线程当作一种特殊的进程处理(个人觉得这是相当优雅的抽象).内核调度的对象是线程而不是进程. 每个线程有独立的程序计数器, 进程栈和一组进程寄存器.
内核线程是由kthread内核进程通过系统调用do_fork()创建的. 内核线程指向地址空间的mm指针被设置成NULL. 内核进程和普通进程一样, 可以被调度, 也可以被抢占.
每个进程都有自己的内核栈(一般为一页或两页内存大小, 默认为两页, 可配置). 通过在栈底创建一个struct thread_info结构. 对于x86来说, 通过栈指针就能计算出它的位置. 避免了使用另外的寄存器. (对于mips来说, 确实是使用的$28寄存器来保存该结构体指针的, 参见current_thread_info()宏的定义)
 

关于fork系统调用和进程创建

不管是进程, 线程, 还是内核线程, 最终都是通过传递不同的参数来调用do_fork()来完成的. 
Linux在用户调用fork之后, 会将一些页设置成只读状态,这样当发生页写入的事件的时候, 内核可以捕捉到异常, 这样通过相应的处理函数, 实现写时拷贝技术. 写时拷贝技术可以让创建进程的开销减少很多.
Fork()系统调用会从内核返回两次, 一次回到父进程, 一次回到新产生的子进程. 子进程会优先执行, 这是因为子进程一般会马上调用exec, 这样可以避免写时拷贝带来的额外开销.
内核把进程列表存放在tasklist双向循环链表中. 链表每一项为一个taskstruct(Linux的task struct结构通过slab分配器来分配, 以实现对象复用和缓存着色). 称之为processdescriptor(进程描述符). 
 

进程的状态和退出

进程描述符的state域描述了进程的当前状态, 有5种情况:TASK_RUNNING, TASK_INTERRUPTABLE(可中断睡眠),TASK_UNINTERRUPTIBLE(不可中断睡眠), TASK_TRACED(被调试), TASK_STOPPED(接收到SIGSTOP, SIGTSTP,SIGTTIN,SIGTTOUT等信号后).
进程退出执行后被设置为僵死状态,直到它的父进程调用wait或者waitpid为止.
进程退出时, 会将exit_state设置为EXIT_ZOMBIE状态, 进程最后会调用do_exit(), 然后do_exit()会调用schedule()切换到新的进程, 此后进程将不会再被调度.
父进程调用wait系统调用之后, 子进程的task_struct结构才会释放. 这样做是为了让父进程能够获取到子进程的退出状态(或者还有别的原因). 
 

进程调度和抢占

Linux从2.6.23版本之后, 使用CFS算法(完全公平调度算法)来对普通进程进行调度. CFS是针对普通进程的一种调度算法. 它使用红黑树来组织可运行进程的队列.
实时进程使用SCHED_FIFO或者SCHED_RR调度策略, 普通进程使用SCHED_NORMAL调度策略.
普通进程通过nice值(-20~+19)来确定优先级. 实时进程有实时进程的优先级(0~99). 默认情况下, nice值-20到+19对应的是从100到139的实时优先级范围.
 
我们称Linux是一种抢占式多任务系统. Linux同时支持用户抢占和内核抢占. 抢占可以让高优先级的进程更快的得到执行以满足实时性要求.
用户抢占发生在中断返回或者从系统调用返回用户空间的时候, 此时肯定是安全的. 
内核抢占发生在中断返回, 或者当前任务显示的调用schedule()或阻塞的时候, 或是内核代码再一次具有可抢占性的时候. (need_resched被设置, 且preempt_count为0, 即当前任务没有持有锁)
 

可中断睡眠与不可中断睡眠的区别

我们在等待信号量的时候, 可以选择调用down()或者down_interruptible()这两个api. 它们的区别是使用down()如果获取不到信号量则进程会进入到TASK_UNINTERRUPTIBLE状态, 而down_interruptiable会进入到TASK_INTERRUPTIBLE状态. 当进程进入TASK_INTERRUPTIBLE状态的时候, 是可以响应外部信号(通过kill或者其它方式发送的信号)的,
此时down_interruptiable会返回-EINTR, 但是信号量并没有被获取, 开发者需要自己做判断. 而处理TASK_UNINTERRUPTIBLE状态的进程则无法响应任何信号, 直到获取到信号量为止.
 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息