【Linux】——进程

一、操作系统对进程的管理

1、了解进程

(1)概念
通俗一点来理解,进程就是运行中的程序,这个运行是指的把程序从磁盘中加载到了内存上。
但是具体的来说,进程并不仅仅局限于一段可执行的程序代码(我们称其为代码段)。通常进程还包含了其他资源,像打开的文件,挂起的信号,内核内部数据,处理的状态,一个或多个具有内存映射的内存地址空间及一个或多个执行线程,当然还包括用来存放全局变量的数据段等。所以说进程时一组有序指令+数据+资源的集合。实际上,进程就是正在执行的程序代码的实时结果

(2)进程中的活动对象
线程是进程中的活动对象。每一个线程都拥有一个独立的程序计数器、进程栈和一组进程寄存器。内核调度的对象是线程而不是进程。在现在的操作系统中一个进程可以包含多个线程,对于Linux而言,线程只不过是一种特殊的进程。

(3)存在两个或多个不同的进程进程执行同一个程序
进程在创建的时候开始存活,在Linux系统中调用fork()复制一个现有的进程来创建一个全新的进程。在调用结束时,在返回点这个相同的位置上,父进程恢复执行,子进程开始执行。fork()系统调用从内核返回两次:一次回到父进程,另一次回到新产生的子进程。通常,创建新的进程都是为了立即执行新的、不同的程序,而接着调用exec()这组函数就可以创建新的地址空间。

2、操作系统对进程的控制方式

操作系统通过PCB记录进程的相关属性
(1)初识PCB
PCB又叫做进程描述符,都是类型为task_struct,该结构定义在<linux/sched.h>文件中。进程描述符中包含一个具体进程的所有信息。
task_struct相对较大,在32位机器上,他大约有1.7KB。进程描述符中包含的数据能完整地描述一个正在执行的程序:他打开的文件,进程的地址空间,挂起的信号,进程的状态,还有其他更多信息,如下图所示:
【Linux】——进程
另外,内核把进程的列表存放在叫做任务队列的双向循环链表中,具体图示如下。Linux通过slab分配器分配task_struct结构。
【Linux】——进程

(2)进程的生成和回收

进程新生成时,必须先分配PCB结构,后生成进程主体。内核通过一个唯一的进程标识值或PID来标识每个进程,PID是一个数,最大值默认设置为32768(short int短整型的最大值)内核把每个进程的PID存放在他们各自的进程描述符中。
进程结束时,先释放主体,然后才会释放PCB的结构。在这里释放PCB需要一定的条件,比如说退出码等,所以就有了我们所说的僵死进程和孤儿进程的出现。
僵死进程:进程主体释放,但是PCB依旧保留。僵死进程的存在是有危害的,就像我们之前描述的那样,PCB在内存占1.7k的空间,大量的僵死进程存在会导致内存太满。
孤儿进程:父进程已经结束,但是子进程还没有结束。系统将所有的孤儿进程都挂载在INIT下。

二、进程的状态

1、三种状态
最简单的进程可以划分为三个状态,包括运行、就绪和阻塞态。运行是指CPU正在执行进程中的指令,就绪是指等待CPU执行的进程,阻塞是指等待I/O事件发生的。他们的关系如下图所示:
【Linux】——进程
2、五种状态
(1)TASK_RUNNING(运行):表示它或者正在执行或者在运行队列中等待执行。这是进程在用户空间中执行的唯一可能的状态,这种状态也可以应用到内核空间中正在执行的进程。
(2)TASK_INTERRUPTIBLE(可中断):进程被阻塞,等待某些条件的达成把进程状态设置为运行。
(3)TAK_UNINREREUPTIBLE(不可中断):就算接收到信号也不会唤醒或准备投入运行外,这个状态与可打断状态相同
(4)_TASK)TRACED:被其他进程跟踪的进程
(5)_TASK_STOPPED(停止):进程没有投入运行也不能投入运行。
他们的具体转换图如下:
【Linux】——进程
3、七种状态
在这里插入图片描述
【Linux】——进程
在这里,主要强调一下退出状态,有四种退出方式分别是:
(1)正常退出(自愿的):进程由于完成了他们的工作而退出,比如说调用exit.
(2)出错退出(自愿的):通常是由于程序中的错误导致,例如执行一条非法的指令、引用不存在的内容或除数是零。在这类错误中,进程会收到信号被中断
(3)严重错误(非自愿):比如说要编译一个不存在的程序时。
(4)被其他进程杀死(非自愿):在Linux系统中,系统调用kill.