操作系统

  1. 1. 进程和线程
  2. 2. 线程间同步方式
  3. 3. 进程的状态
  4. 4. 进程间通信方式
  5. 5. 僵尸进程和孤儿进程
  6. 6. 死锁
  7. 7. 死锁的必要条件
  8. 8. 如何解决死锁

进程和线程

  • 进程是一个程序的实例
  • 线程是轻量级进程,一个进程可以包含多个线程,同一个进程的所有线程可以共享进程的资源(内存空间、文件句柄、网络连接等),每个线程也拥有自己的资源(程序计数器、虚拟机栈、本地方法栈)
  • 线程执行开销小,不利于资源的管理和保护,进程相反

线程间同步方式

  1. 互斥锁
  2. 读写锁
  3. 信号量
  4. 屏障
  5. 事件:wait/notify

进程的状态

  • 创建状态:进程刚被创建,还未就绪
  • 就绪状态:进程处于可以运行并准备运行的状态
  • 运行状态:进程在就绪状态时被分配到cpu就会在cpu上运行,即为运行状态
  • 阻塞状态:进程因等待别的资源或IO操作而暂停运行
  • 结束状态:进程从系统中消失

进程间通信方式

  1. 管道/匿名管道:父子进程间或兄弟进程间的通信
  2. 有名管道:匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循 先进先出(First In First Out) 。有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。
  3. 信号:信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生
  4. 消息队列:消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。
  5. 信号量:信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
  6. 共享内存
  7. 套接字

僵尸进程和孤儿进程

  • 僵尸进程:子进程已经终止,但是其父进程仍在运行,且父进程没有调用 wait()或 waitpid()等系统调用来获取子进程的状态信息,释放子进程占用的资源,导致子进程的 PCB 依然存在于系统中,但无法被进一步使用。这种情况下,子进程被称为“僵尸进程”。避免僵尸进程的产生,父进程需要及时调用 wait()或 waitpid()系统调用来回收子进程。
  • 孤儿进程:一个进程的父进程已经终止或者不存在,但是该进程仍在运行。这种情况下,该进程就是孤儿进程。孤儿进程通常是由于父进程意外终止或未及时调用 wait()或 waitpid()等系统调用来回收子进程导致的。为了避免孤儿进程占用系统资源,操作系统会将孤儿进程的父进程设置为 init 进程(进程号为 1),由 init 进程来回收孤儿进程的资源。

死锁

  • 死锁:多个进程或线程同时阻塞,都在等待资源释放。由于进程/线程被无限阻塞,因此程序不可能正常终止。

死锁的必要条件

  1. 互斥:资源不能被共享,同一时间只能有一个进程或线程使用。
  2. 非抢占:资源不能被抢占,当前正在使用的进程或线程用完后才会被释放。
  3. 占有并等待
  4. 循环等待

如何解决死锁

从四个条件入手:

  1. 解决互斥:使得资源可以同时被多个线程或进程使用,缺点是很多资源是必须独享的
  2. 解决非抢占:采用剥夺式调度算法,缺点是会导致资源利用率下降。

这两种都不太适合

  1. 解决占有并等待循环等待
    • 静态分配:让进程或线程执行前先获取到所有需要的资源,缺点是会降低资源利用率
    • 层次分配:
    • 使用算法(例如银行家算法)判断线程是否安全,若能够进入到安全的状态,则就 真的分配资源给该进程