进程切换(上下文切换):
- 定义:暂停当前运行进程,从运行状态变成其他状态,调度另一个进程从就绪状态变成运行状态
- 要求:切换前,保存进程上下文;切换后,恢复进程上下文;快速切换
- 进程存储的生命周期的信息:寄存器(PC,SP,...);CPU状态;内存地址空间
进程控制块PCB:内核的进程状态记录
- 内核为每个进程维护了对应的进程控制块PCB
- 内核将相同状态的进程的PCB放置在同一队列
进程控制块结构:
- 进程标识信息
- 进程状态信息
- 进程占用的资源(存储资源、内核堆栈等)
- 保护现场用的内容
- 当前进程在哪个队列
其中内存地址空间结构中会包含:有哪些内存块及地址空间,第一级页表起始地址,置换相关结构
进程创建:
- Windows进程创建API:CreateProcess
- Unix进程创建系统调用:fork/exec。fork()把一个进程复制成两个进程,父子进程有各自的PID;接下来的exec()用新程序来重写当前进程,PID不改变。
空闲进程创建
当用户代码执行完之后,系统将在空闲进程中执行。
第一个内核线程的创建:
fork()的开销:
- 对子进程分配内存
- 复制父进程的内存和CPU寄存器到子进程里
- 开销昂贵
99%调用fork是为了接下来调用exec,考虑到fork()操作中内存复制是没有作用的,子进程可能会关闭打开的文件和连接,因此考虑将其合并到一个调用中,于是出现了vfork()。
vfork():
- 创建进程时,不再创建一个同样的内存映像,而是当调用exec()时才开始复制,而要被覆盖或者关闭的资源不再进行复制
- 一些时候称为轻量级fork()
- 子进程应该几乎立刻调用exec()
- 现在使用Copy on Write(COW)技术
进程加载exec
- exec调用成功时:是相同的进程,运行了不同的程序
- exec()允许一个进程加载一个不同段程序,并在MAIN执行,(_start)
- 允许一个进程指定参数的数量argc和字符串参数数字argv
- 允许进程指定不同的控制流,OS中可指定不同的应用程序,即调用成功=相同进程,运行的是不同的程序!
- Code(代码段)stack(堆栈)heap(堆)等完全重写
进程等待与退出:
父进程等待子进程:
1. wait()系统调用用于父进程等待子进程的结束
- 子进程结束时通过exit()向父进程返回一个值
- 父进程通过wait()接受并处理返回值
2. wait()系统调用的功能
- 有子进程存活时,父进程进入等待状态,等待子进程的返回结果。当某子进程调用exit()时,唤醒父进程,将exit()返回值作为父进程中wait的返回值
- 有子进程调用exit()而父进程还未调用wait()处理时,子进程被称为僵尸进程。如果此时父进程调用wait()则直接处理并返回
- 无子进程或无子进程存活时,wait()也是立刻返回
3. exit()用于进程的有序终止
- 进程结束执行时调用exit(),完成进程资源回收。
4. exit()系统调用的功能
- 将调用参数作为进程的结果返回
- 关闭所有打开的文件等占用资源
- 释放内存
- 释放大部分进程相关的内核数据结构
- 检查是否父进程是存活的:如果父进程存活,保留结果的值直到父进程用wait()处理,进入僵尸状态;如果父进程不存活,则子进程为孤儿进程,直接释放所有数据结果,进程结束
- 清理所有等待的僵尸进程
其他进程控制系统调用
- 优先级控制:nice()指定进程的初始优先级;unix系统中进程优先级会随执行时间而衰落
- 进程调试支持:ptrace()允许一哥进程控制另一个进程的执行;设置断点和查看寄存器
- 定时:sleep()可以让进程在定时器的等待队列中等待指定。
为什么要让父进程等?而不是直接结束?
当进程执行完毕退出后,几乎所有资源都回收到OS中。但有个资源很难回收,就是PCB,PCB是代表进程存在的唯一标识,OS要依据PCB执行回收。这个功能由父进程完成。