1.进程

进程就是出于执行期的程序。但是进程并不仅仅局限于一段可执行的代码。通常进程还要包括其他资源,像打开的文件,挂起的信号,内核内部数据,处理器状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还包括用来存放全局变量的数据段等。实际上进程就是正在执行的程序代码的实时结果。内核需要有效而又透明地管理所有细节。

执行线程,简称线程,是在进程中活动的对象。每个线程都拥有一个独立的程序计数器、线程栈和一组进程寄存器。内核调度的对象是线程,而不是进程。

在现在操作系统中,进程提供两种虚拟机制:虚拟处理器和虚拟内存。虽然实际上可能是许多进程正在分享一个处理器,但是虚拟处理器给进程一种假象,让这些进程觉得自己在独享处理器。

程序本身并不是进程,进程是处于执行期的程序以及相关资源的总称。实际上,完全可能存在;两个或多个不同的进程执行的是同一个程序。并且两个或两个以上并存的进程还可以共享许多诸如打开的文件,地址空间之类的资源。

进程在创建它的时刻开始存活。在linux系统中,这通常是调用fork()系统的结果,该系统调用通过复制一个现有进程来创建一个全新的进程。调用fork()的进程成为父进程,新产生的进程为子进程。在该调用结束时,在返回点这个相同位置上,父进程恢复执行子进程开始执行。fork()系统调用从内核返回2次,一次回到父进程,另一次回到新产生的子进程。

通常新创建的进程都是为了立即执行新的,不同程序,而接着调用exec()这组函数就可以创建新的地址空间,并把新的程序载入其中。

最终,程序通过exit()系统调用退出执行。这个函数会终结进程并将其占有的资源释放掉。父进程可以通过wait4()系统调用查询子进程是否终结,这其实使得进程拥有了等待特定进程执行完毕的能力。进程退出执行后设置为僵死状态,直到它的父进程调用wait()或waitpid()为止。

2.进程描述符及任务结构

        内核把进程的列表放在任务队列(task list)的双向循环链表中。链表中的每一项都是类型为task_struct,称为进程描述符的结构。进程描述符中包含一个具体进程的所有信息。

        task_struct相对较大,在32位机器上,它大约有1.7KB,但是如果考虑到该结构包含了内核管理一个进程所需的所有信息,那么它的大小也算相对小了。进程描述符中包含的数据能完整地描述一个正在执行的程序:它打开的文件,进程的地址空间,挂起的信号,进程的状态,还有其他更多信息。

2.1分配进程描述符

        linux通过slab分配器分配task_struct结构,这样能达到对象复用和缓存着色的目的。在2.6以前的内核中,各个进程的task_struct存放在它们内核栈的尾端,这样做的目的是为了让那些像x86那样寄存器较少的硬件体系结构只要通过栈指针就能计算出它的位置,而避免使用额外的寄存器专门记录。由于现在用slab分配器动态生成task_struct,所以只需在栈底(对于向下增长的栈来说)或栈顶(对于向上增长的栈来说)创建一个新的结构struct thread_info。

        每个任务的thread_info结构在它的内核栈的尾端分配。结构中task域中存放的是指向该任务实际task_struct的指针。

2.2 进程描述符的存放

        内核通过一个唯一的进程标志值或PID来识别每个进程。PID是一个数,PID的最大默认值设置为32768(short int短整型的最大值)。内核把每个进程的PID存放在它们各自的进程描述符中。

        这个最大值很重要,因为它实际上就是系统中允许同时存在的进程的最大数目。这个值越小,转一圈就越快,本来数值大的进程比数值小的进程迟运行,但这样一来就破坏这一原则,如果需要由系统管理员修改/proc/sys/kernel/pid_max来提高上限。

        在内核中,访问任务通常需要获取指向其task_struct的指针。实际上,内核大部分处理进程的代码都是通过task_struct进行的。因此,通过current宏查找到当前正在运行进程的进程描述符的速度就显得尤为重要。

 

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐