进程

来源:互联网 发布:手机答题软件 编辑:程序博客网 时间:2024/06/08 14:03

进程是多道程序设计的操作系统的基本概念。进程是程序运行的实体。他是描述程序已经执行到了何种程度的数据结构的汇集,也是操作系统分配系统资源(cpu时间,内存,磁盘)的实例。

一. 进程描述符

进程是比较抽象的概念,linux下描述进程的数据结构为进程描述符。进程描述符代表了一个进程所有的数据结构,文件,以及运行上下文。进程描述符是这么的复杂,在linux中大约占用1.7K个字节,完全读懂他还是有一定难度的。但是总的划分为几部分:
 

(1)thread_info 指针,指像进程的基本信息,这个结构非常重要,他与进程的内核栈存放在一起。一般占用8K个字节,两个页。这个结构体里包含如进程的状态,内核抢占标志,本地CPU号等一些重要的信息。

(2)mm_struct 指针,指向内存描述符指针,内存描述符描述了进程的基本内存信息,包括内存地址空间等。

(3)tty_struct 与进程相关的tty

(4)fs_struct 当前目录

(5)files_struct 指向文件描述符的指针

(6)signal_struct 所接收的信号

thread_info 数据结构描述了进程基本信息,又叫线程描述符。他与进程的内核态栈存放在一起,一般占用8K的内存空间,他的首地址是8K的整数倍。所以只要或得了内核栈指针就可以方便的找到当前进程的线程描述符。从而找到当前进程的进程描述符。current宏就是通过进程的内核栈来找到当前进程的进程描述符。

linux下所有的进程描述符都链接在以init_task进程为首的进程双向链表中,可运行进程还链接在可运行链表中,等待进程链接在不同的等待队列中。这样方便管理。

二. 进程的状态

进程像一个生命一样经历着生老病死。所以进程的状态包括了这个过程。进程包括了七种状态:可运行状态,可中断的等待状态,不可中断的等待状态,暂停状态,跟踪状态,僵死状态,僵死撤销状态。

(1) 可运行状态,进程从建立之初起就是本着运行去的,所以可运行状态是进程的最好的状态,当然可运行不代表这就一定在运行,这种状态代表进程随时准备运行,在CPU的可运行队列里

(2) 可中断的等待状态,进程因为等待一些资源或者磁盘IO而不具备运行的条件。这中状态叫进程的等待状态,可中断的等待状态是,进程可以对信号进行相应而结束等待状态。而不可中断等待状态与其相反。

(3) 暂停状态,进程也不能运行,这种状态是进程收到了一些暂停的信号造成的。

(4) 跟踪状态,一个进程可以跟踪另外一个进程的运行,当跟踪发生时,他就处于跟踪状态。

(5) 僵死状态,进程已经退出,释放了所有的资源,不能够再运行了。但是进程描述符还没有被回收,这时进程处于僵死的状态。

(6) 僵死退出状态,父进程对僵死的进程发出wait4系统调用,回收了进程的描述符,进程就彻底死亡了。

三  标识一个进程与进程关系

linux采用进程标识符来标识一个进程,就是一种编码。linux下进程标识符的上限为32767,用pidmap_array位图来表示当前已经分配的PID号和闲置的PID号。每使用一个进程,linux就在这个位图中空闲位置1来表示进程号已用。进程之间是有关系的,每一个进程都有父进程,然后从属于一个父继承的进程还是兄弟进程,进程描述符中有专门的域来表示进程的这种关系。

四. 由PID到进程描述符

在一些情况下,内核必须由PID导出进程描述符的指针。例如,利用KILL系统调用时。这时必须建立起PID与进程描述符的关系。为了提高效率,linux建立了四个散列表来表示这种映射关系。,之所以采用散列表的原因是linux上限进程数量为32768项,但是系统实际的进程数量却远远少于这个数值,所以如果建立32768个表项的话是一种资源的浪费,所以通过散列表的方式来达到空间的节省。但是散列表不能避免冲突,这里采用链表的方式避免冲突。


本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2012-03/57750.htm

 

五. 进程切换

进程切换是内核控制进程运行必须的能力,在进程切换中,需要保存上个进程的CPU硬件上下文。。在linux中,进程硬件上下问一部分保存在TSS段中,剩余部分保存在内核态的堆栈中。进程切换只发生在内核态。在进程切换之前,用户进程使用的所有寄存器都已保存在内核态堆栈上,这是由中断或者异常处理程序完成的,被保存的硬件上下文在进程描述符的thread_struct的thread字段。

x86计算机体系包含了一个特殊的段,任务状态段,他主要用来存放硬件上下文。linux中就一个TSS段,反映了当前进程的硬件上下文,每次进程切换内核都会更新里面的内容,来让硬件做相应的检查。

进程切换从本质上讲由两步完成:

(1)切换全局页目录安装一个新的地址空间。

(2)切换内核态堆栈和硬件上下文

六. 创建进程

linux中用户的需求是通过创建进程的方式来完成的。如果没有新载入可执行代码,新创建的进程与父进程是一样的。共享一样的代码,但是具有不同的数据。新建进程的时候需要大量的数据复制,很浪费时间,同时新进程也许马上就会调用新的代码来擦除旧的数据。linux采用一些技术来解决这个问题:写时复制,共享数据结构,共享内存地址空间clone, fork,vfork系统调用在底层都是由clone函数实现的,他是C语言的一个封装函数,他负责建立新进程的堆栈并调用clone系统嗯调用,实现clone系统调用的函数为sys_clone,sys_clone 服务例程调用do_fork函数,而do_fork函数利用copy_process函数来穿件进程描述符以及子进程执行所需要的其他数据结构。

do_fork主要实现一下步骤:

(1)分配PID号

(2)检查父进程跟踪标志

(3)调用copy_process 创建进程描述符以及子进程所需要的其他数据结构

(4)调用wake_up_new_task函数来处理进程运行的参数与调度参数。

(6)检查VFORK标志,如果设置,父进程等待子进程结束或执行新的程序

(7)返回PID

七. 内核线程

内核线程也是进程,但是与普通进程有一些区别。一般内核线程的优先级都比较低,内核线程只运行在内核态。常见的内核线程,进程0,他初始化内核所有的数据结构,激活中断,创建进程1内核线程,当线程1执行init程序时变为普通的进程。

八 进程死亡

程序调用了exit函数,或者隐含调用(C编译器在Main函数的最后一条语句后都加上exit函数),有两个进程终止系统调用exit_group与exit两个不同之处是,前一个终止一个线程组的进程,后一个只结束一个进程。linux允许进程查询内核以获得其任何子进程的执行状态。为了达到这一目标,进程结束不应该回收进程描述符。所以尽管进程已经死亡但是还保留进程描述符来等父进程的通知。如果父进程在没有调用wait系统调用之前就先他的子进程而亡。那么子进程就得不到超脱,也就是会成为僵尸,永久占用RAM而不能运行。所以linux的进程如果成为僵尸进程,那么会找到init进程作为其父进程来结束自己的僵尸命运。

 

原创粉丝点击