《操作系统概念》第三章笔记 1
进程概念
进程
程序本身不是进程,程序只是一种存储指令的文件,是静态的,相反,进程本身是动态的。
进程不是执行的程序,进程不只是程序代码,进程还包括程序计数器,堆栈,数据段等
进程本身可以作为一个环境,用于执行其他代码(比如Java的JVM)
进程状态
进程在执行时会改变状态,取决于进程的当前活动,状态如下(进程的五态模型)
- new :创建进程
- running: 执行进程/指令
- waiting:进程等待发生事件(比如IO信号),也叫阻塞状态
- ready:进程等待分配处理器
- terminated:完成执行
进程控制块
操作系统内的每个进程表示,采用进程控制块(Process Control Block, PCB),也称为任务控制块,它包含了许多与特定进程的相关信息 - 进程状态:上面的进程五态模型
- 程序计数器:进程将要执行的下个指令的地址
- CPU寄存器:寄存器的数量与类型取决于计算机体系结构,存储了许多进程的信息包括累加器,寄存器,指针等。这些状态使得进程在中断后能够正确的执行
- CPU 调度信息:包括进程优先级,调度队列指针等
- 内存管理信息:
- 说明信息(accounting information):使用时限,CPU使用时间,进程ID等
线程
线程是进程中的执行运算的最小单位,是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源( 程序计数器,一组寄存器和栈),但它可与同属一个进程的其他线程 共享进程所拥有的全部资源。线程的优点
- 易于调度。
- 提高并发性。通过线程可方便有效地实现并发性。进程可创建多个线程来执行同一个程序的不同部分。
- 开销少。创建线程比创建进程要快,所需开销少,占用的资源也少;
- 充分发挥多处理器的功能。通过创建多线程进程,每个线程在一个处理器上运行,从而实现应用程序的并发性,使每个处理器都得到充分的运行。
线程和进程的区别
调度:线程作为处理器调度和分配的基本单位,而进程是作为拥有资源的基本单位
并发性:不仅进程之间可以并发执行,同一个进程的多个线程之间也可以并发执行
拥有资源:进程是拥有资源的一个独立单位,有自己独立的地址空间;线程不拥有系统资源,但可以访问隶属于进程的资源,共享进程的地址空间.
系统开销:在创建或撤消进程时,由于系统都要为之分配和回收资源,导致系统的开销明显大于创建或撤消线程时的开销。
线程和进程的联系
二者均可并发执行.
线程是指进程内的一个执行单元,也是进程内的可调度实体。一个程序至少有一个进程,一个进程至少有一个线程,一个线程只属于一个进程.
资源分配给进程,同一进程的所有线程共享该进程的所有资源。
处理机分给线程,即真正在处理机上运行的是线程。
线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。线程是指进程内的一个执行单元,也是进程内的可调度实体.进程和线程的主要差别在于它们是不同的操作系统资源管理方式。 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其它进程产生影响。而线程只是一个进程中的不同执行路径,线程有自己的堆栈和局部变量,但线程之间没有单独的地址空间, 一个线程死掉就等于整个进程死掉,所以多进程的程序要比多线程的程序健壮,但在进程切换时,耗费资源较大,效率要差一些。但对于一些要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程。
进程调度
为了无论何时都有进程在CPU上运行,最大化CPU利用率,进程调度器选择一个可用进程到CPU上执行。如有多个进程,剩下的需要到CPU空闲后重新等待调用。
调度队列
一般的没有优先级考虑的调度队列模型如下:
如上图,ready queue是就绪队列,用单向链表实现,有两个指针,头指针指向第一个 PCB块,尾指针指向最后一个PCB块。PCB块之间有单向链表链接。
如果多个进程向一个设备发送了I/O请求,那么就需要在设备间进行排队,这个队列称为设备队列。如上图, disk就拥有三个进程等待读写,每个设备拥有自己的设备队列。
上图拥有两种队列:就绪队列和设备队列。新进程被分配到就绪队列,在被CPU执行时会发生下面三种情况- 进程发出 I/O 请求,放到 I/O 队列中
- 创建新的子进程,等待子进程终止
- 由于被CPU强制中断,回到就绪队列中
前两种情况,进程会从阻塞状态切换到就绪状态,并放回就绪队列。调度器
进程会在各种调度队列之间迁移,因此操作系统需要有一种选择方式从队列中选择进程。进程选择由调度器来执行
对于批任务处理系统来说,多数被提交的进程是无法立即执行的,因此保存到大容量存储设备中,由长期调度器执行
短期调度器负责选择就绪队列中的进程交给CPU执行。
短期调度器与长期调度器之间的区别在于执行的频率。短期调度器必须时刻为CPU选择新的进程,进程可能就执行几毫秒就切换。因此对于性能非常重要。而长期调度器执行并不频繁,可能是几分钟的间隔。
长期调度程序应该选择 I/O密集型和 CPU密集型的程序进行合理的进程组合
在分时系统中可能会引入中期调度器,将进程从内存中移出以降低多任务的繁忙程度。这种方案称为交换(swap)
上下文切换
切换 CPU 到另一个进程需要保存当前进程和恢复另一个进程的状态,这个任务称为上下文切换
上下文切换的时间与硬件支持密切相关,操作系统越复杂,上下文切换就需要做的更多。
进程运行
1 | ps -el |
上列命令可以列出系统中的所有当前活动进程的完整信息,通过递归跟踪父进程可以轻松构造进程树
进程创建
1 |
|
在这里使用书上的图3-9的例子进行说明,下面是运行结果。(实验环境Kali-Linux2023.2,gcc版本13.1.0)
可以看到父进程的pid是大于0的,子进程的pid是等于0的,这也是子进程和父进程之间唯一的不同。(实际上,子进程的pid就是父进程的pid)
父进程通过wait方法等待子进程的完成,子进程通过执行 $ls$ 命令,换句话说,子进程用 $ls$ 系统命令代替了其本身,因此最多只会执行一次 $ls$ 命令,之后就会直接退出。
父进程等待子进程执行完命令之后唤起,然后退出。
注:exec
是一个函数族,有六个函数,执行 exec
时新程序会替换进程的堆栈代码段等。如果还想执行下面程序可以使用 system
系统调用
Linux系统编程——进程替换:exec 函数族 - 知乎 (zhihu.com)
进程终止
孤儿进程
孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(pid=1)所收养,并由init进程对它们完成状态收集工作。
僵尸进程
僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。
危害
如果进程不调用wait / waitpid的话, 那么保留的那段信息就不会释放,其进程号就会一直被占用,但是系统所能使用的进程号是有限的,如果大量的产生僵尸进程,将因为没有可用的进程号而导致系统不能产生新的进程。
参考
Operating System Concepts - 10th edition (os-book.com)
操作系统:进程与线程之间的区别及联系 - 知乎 (zhihu.com)
孤儿进程与僵尸进程[总结] - Rabbit_Dale - 博客园 (cnblogs.com)