20135307张嘉琪linux
进程就是处于执行期的程序(目标码存放在某种存储介质上),但进程并不只仅局限于一段可执行程序代码。一般进程还要包含其余资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具备内存映射的内存地址空间及一个或多个执行线程。固然还包括用来存放全局变量的数据段等,实际上,进程就是正在执行的程序代码的实时结果,内核须要有效而又透明地管理全部细节。算法
执行线程,简称线程,是在进程中活动的对象,每一个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器,内核调度的对象是线程,而不是进程,在传统的Linux系统中,一个进程只包含一个线程,但如今的系统中,包含多个线程的多线程程序司空见惯。Linux系统的线程实现很是特别:它对线程和进程并不特别区分,编程
对Linux而言,线程只不过是一种特殊的进程罢了。缓存
在现代操做系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。数据结构
在现代Linux内核中,fork()其实是由clone()系统调用实现的。多线程
内核把进程的列表存放在叫作任务队列的双向循环链表中。并发
链表中的每项都是类型为task_struct、称为进程描述符的结构,该结构定义在<linux/sched.h>文件中。函数
内核经过一个惟一的进程标识值或PID来标识每一个进程,内核把每一个进程的PID存放在它们各自的进程描述符中。优化
在内核中,访问任务一般须要得到指向其taskstruct的指针,实际上,内核中大部分处理进程程描述符的速度就显得尤其重要。硬件体系结构不一样,该宏的实现也不一样,它必须针对专门的硬件体系结构作处理,有的硬件体系结构能够拿出―个专门寄存器来存放指向当前进程taskstruct的指针,用于加快访问速度。操作系统
Unⅸ系统的进程之间存在—个明显的继承关系,在Linux系统中也是如此。全部的进程都是PID为1的init进程的后代。内核在系统启动的最后阶段启动init进程。该进程读取系统的初始化脚本并执行其余的相关程序,最终完成系统启动的整个过程。
传统的fork()系统调用直接把全部的资源复制给新建立的进程,这种实现过于简单而且效率低下,由于它拷贝的数据也许并不共享,更糟的状况是,若是新进程打算当即执行一个新的映像,那么全部的拷贝都将前功尽弃。Linux的fork()使用写时拷贝页实现,写时拷贝是一种能够推迟甚至免除拷贝数据的技术。内核此时并不复制整个进程地址空间,而是让父进程和子进程共享同一个拷贝。
只有在须要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝,也就是说资源的复制只有在须要写入的时候才进行,在此以前,只是以只读方式共享,这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行在页根本不会被写入的状况下它们就无须复制了。
fork()的实际开销就是复制父进程的页表以及给子进程建立惟一的进程描述符。在通常状况下,进程建立后都会立刻运行一个可执行的文件,这种优化能够避免拷贝大量根本就不会被使用的数据(地址空间里经常包含数十兆的数据)因为Unix强调进程快速执行的能力,因此这个优化是很重要的。
copy_process()完成的工做:
线程机制是现代编程技术中经常使用的一种抽象概念,该机制提供了在同―程序内共享内存地址空间运行的―组线程,这些线程还能够共享打开的文件和其余资源,线程机制支持并发程序设计技术,在多处理器系统上,它也能保证真正的并行处理。 Linux实现线程的机制很是独特,从内核的角度来讲,它并无线程这个概念,Linux把全部的线程都当作进程来实现,内核并无准备特别的调度算法或是定义特别的数据结构来表征线程,相反,线程仅仅被视为―个与其余进程共享某些资源的进程,每一个线程都拥有惟一隶属于本身task_struct,因此在内核中,它看起来就像是一个普通的进程(只是线程和其余一些进程共享某些资源,如地址空间)。
进程的建立与普通进程的建立相似,只不过在调用clone()时须要传递一些参数标志来指明所须要共享的资源。
内核常常须要在后台执行一些操做,这种任务能够经过内核线程完成——独立运行在内核空间的标准进程。内核线程和普通的进程间的区别在于内核线程没有独立的地址空间。它们只在内核空间运行,历来不切换用户空间去,内核进程和普通进程同样,能够被调度,也能够被抢占。 Linux确实会把一些任务交给内核线程去作,像flush和ksofirqd这些任务就是明显的例子,在装有Linux系统的机子上运行ps -ef命令,你能够看到内核线程,有不少!这些线程在系统启动时由另一些内核线程建立,实际上,内核线程也只能由其余内核线程建立,内核是经过从kthreadd内核进程中衍生出全部新的内核线程来自动处理这一点的,在<linux/kthreadd>中申明有接口。
当一个进程终结时,内核必须释放它所占有的资源并把这一不幸告知其父进程。
若是父进程在子进程以前退出,必须有机制来保证子进程能找到一个新的父亲不然这些成为孤儿的进程就会在退出时永远处于僵死状态,白白地耗费内存。前面的部分已经有所暗示于这个问题,解决方法是给子进程在当前线程组内找—个线程做为父亲,若是不行就让init作它们的父进程。
在本章中,咱们考察了操做系统中的核心概念——进程,咱们它为什么如此重要,以及进程与线程之间的关系,然也讨论了进程的通常特性,而后,讨论了Linux如何存放和表示进程,如何建立进程,如何把新的执行映像装入到地址空间,如何表示进程的层次关系,父进程又是如何收集其后代的信息以及进程最终如何消亡。
进程是一个很是基础、很是关键的抽象概念,位于每一种现代操做系统的核心位置,也是咱们拥有操做系统(用来运行程序)的最终缘由。