系统调用
使进程从用户态陷入内核态, 在内核态进行特权指令的调用, 完成后再回到用户态.
这里需要两次CPU状态切换, 需要进行CPU上下文切换
CPU上下文切换
CPU上下文包括:
- 程序计数器
- CPU寄存器(高速缓存)
切换过程为:
- 保存CPU上下文
- CPU上下文切换到待执行的内核态指令位置
- 跳转到内核态运行内核程序
- 恢复CPU上下文, 切换到用户态
进程
操作系统维护进程表, 表项称为进程表项/进程控制块PCB, 该表项包含进程状态的信息:
- 程序计数器
- 堆栈指针
- 内存分配状况
- 打开文件的状态
- 调度信息(优先级...)
- ...
用来在进程重新调度运行时恢复
进程时计算机资源分配的单位, 将一组相关的资源集中在一起
进程中非线程的内容有:
- 地址空间
- 全局变量
- 打开文件
- 子进程
- 即将发生的报警
- 信号&信号处理程序
- 账户信息
信号量
是一种语言需要支持的功能
down时信号量值-1
up时信号量+1
记录累计唤醒次数
更复杂的生产者消费者模型用full, empty, mutex实现
- mutex表示互斥
- full在缓冲区满时停止生产者
- empty在缓冲区空时停止消费者
可能造成死锁
互斥量
用原子操作将互斥量的值置为0或非0
进程切换仅在时钟中断/其他中断时才会发生, 用关闭中断可以解决一定的并发问题, 但关闭中断是针对CPU核心的, 在多核处理器中其他核心依然可以被中断, 访问并发的变量
管程
为避免死锁发明的由过程,变量,数据结构构成的模块/软件
管程的数据结构只有在管程内的进程可以调用
管程通过判断变量值, 将请求的进程wait; 通过signal/notify将进程唤醒进入管程
synchronized就是管程的一种实现方式. synchronized是针对对象的, 对其中一个方法家里锁, 则不允许其它线程访问该对象的其它方法
消息传递
信号量是一种语言成分, 而消息传递是一种系统调用.
将send, receive系统调用方法加入库例程, 不同进程可以调用
屏障
所有进程达到屏障, 才可继续向下执行
共享内存
可以通过缓冲区/其他数据结构实现, 还可以是共享文件
线程
线程是运行在进程上文中的程序流.
线程上下文包括:
- 线程ID
- 栈
- 栈指针
- 程序计数器
- 通用寄存器
- 条件码
线程出现的原因
- 共享地址空间和数据
- 更轻量, 切换上下文简单
- 同一任务可分为不同线程, 对同一组数据进行操作
线程的地址空间不互相隔离, 每个线程可以读写另一个线程的堆栈
线程创建
进程从单个线程开始, 该线程可创建新的线程, 通常线程间不存在父子关系而是平等的
线程运行
- create
- exit
- join 阻塞, 直到另一线程exit
- yield 主动放弃CPU(由于线程不会被中断强制让出CPU, 因此可通过这种方式调度)
用户空间线程
POSIX线程是IEEE定义的UNIX线程标准, 其线程包为Pthread. 目的时实现可移植的线程
用户空间线程由用户管理, 每个进程需要有专用的线程表, 可使用进程专有的线程调度算法
内核空间线程
内核中有记录所有线程的线程表, 不需进程记录.
线程创建通过系统调用完成, 对线程阻塞也需要系统调用, 代价更高
但不需自己实现新的非阻塞的系统调用
混合实现线程
使用内核线程, CPU对其进行调度
在用户空间将多个用户级线程多路复用到一个内核线程上