1.进程的概念 linux
进程就是处于执行期的程序(目标码存放在某种存储介质上〉。但进程并不只仅局限于一段dom
可执行程序代码( Unix 称其为代码段, text section)。一般进程还要包含其余资源,像打开的文件,挂起的信号,内核内部数据,处理器状态, 一个或多个具备内存映射的内存地址空间及一个或多个执行线程( thread of execution ),固然还包括用来存放全局变量的数据段等。实际上,进程就是正在执行的程序代码的实时结果。内核须要有效而又透明地管理全部细节。ide
2 进程描述符及任务结构函数
atruct t hread_info {优化
struct task_struct • task;操作系统
struct exec_domain • exec_d。main;线程
_ u32 flags;指针
_ u32 status;rest
_u32 cpu;blog
int preempt_ count;
mm_segment_t addr_limit;
struct restart bl。ck restart bl。ck;
v。id •sysenter_return;
int uaccess_err;}
3.进程描述符的存放
内核经过一个惟一的进程标识值(process identification value)或PID 来标识每一个进程。PID 是一个数,表示为pid_t 隐含类型θ,实际上就是一个四类型。为了与老版本的Unix 和Linux 兼容,PID 的最大值默认设置为32768 (sh臼t int 短整型的最大值〉,尽管这个值也能够增长到高达400 万〈<linux/reads.h> 中所定义PID 最大值的限制〉。内核把每一个进程的PID 存放在它们各自的进程描述符中。
4进程建立
4.1 写时拷贝
只有在须要写入的时候,数据才会被复制,从而使各个进程拥有各自的拷贝。也就是说,资
源的复制只有在须要写入的时候才进行,在此以前,只是以只读方式共享。这种技术使地址空间上的页的拷贝被推迟到实际发生写入的时候才进行。在页根本不会被写人的状况下(举例来讲,fork()后当即调用exec(})它们就无须复制了。fork()的实际开销就是复制父进程的页表以反给予进程建立惟一的进程描述符。在通常状况下,进程建立后都会立刻运行一个可执行的文件,这种优化能够避免拷贝大量根本就不会被使用的数据〈地址空间里经常包含数十她的数据〉。因为Unix 强调进程快速执行的能力,因此这个优化是很重要的。
4. 2 fork()
Linux 经过clone()系统调用实现fork() 。这个调用经过一系列的参数标志来指明父、子进程须要共享的资源〈关于这些标志更多的信息请参考本章后面3.4 节〉。fork()、vfork()和一clone()库函数都根据各自须要的参数杨L志去调用clone(),而后由clone()去调用do_fork().do_fork 完成了建立中的大部分工做,它的定义在kemeVfork.c 文件中。该函数调用copy_process()函数,而后让进程开始运行。copy_process()函数完成的工做颇有意思:
l )调用dup_task_ struct()为新进程建立一个内核枝、也read_info 结构和task_struct,这些值与当前进程的值相同。此时, 子进程和父进程的描述符是彻底相同的。
2 )检查并确保新建立这个子进程后,当前用户所拥有的进程数目没有超出绘色分配的资源
的限制.
3 )子进程着孚使本身与父进程区别开来。进程描述符内的许多成员都要被清0 或设为初始值.那些不是继承而来的进程描述符成员,主要是统计信息。task_struct 中的大多数数据都依然未被修改.
4 ) 子进程的状态被设置为TASK_UNJNTERRUPTIBLE,以保证它不会投入运行。
5 ) copy _process()调用copy_flags()以更新task_struct 的组ags 成员.代表进程是否拥有超级用户权限的PF_SUPE盯RIV 标志被清0。代表进程尚未调用exec()函数的PF_FOR.KNOEXEC标志被设置。
6 )调用alloc _pid()为新进程分配一个有效的PID。
7 )根据传递给clone()的参数标志, copy_process()拷贝或共享打开的文件、文件系统信息、信号处理函数、进程地址空间和命名空间等。在通常状况下,这些资源会被给定进程的全部线程共享:不然,这些资源对每一个进程是不一样的,所以被拷贝到这里。的最后, copy_process()傲扫尾工做并返回一个指向子进程的指针。再回到do_fork()函数,若是copy_process()函数成功返回,新建立的子进程被唤醒并让其投入运行。内核有意选择子进程首先执行。.由于通常子进程都会立刻调用exec()函数,这样能够避免写时拷贝的额外开销,若是父进程首先执行的话,有可能会开始向地址空间写入。
小结:在本章中,咱们考察了操做系统中的核心概念一一进程。咱们也讨论了进程的通常特性,它为什么如此重要,以及进程与线程之间的关系。而后,讨论了Linux 如何存放和表示进程(用task_ struct 和thread_info ),如何建立进程(经过fork(),实际上最终是clone()),如何把新的执行映像装入到地址空间(经过execO 系统调用族〉,如何表示进程的层次关系,父进程又是如何收集其后代的信息(经过wait()系统调用族),以及进程最终如何消亡〈强制或自愿地调用exit()) 。进程是一个很是基础、很是关键的抽象概念,位于每一种现代操做系统的核心位置,也是咱们拥有操做系统(用来运行程序〉的最终缘由。