操作系统 进程和线程简介

操作系统 进程和线程简介

Posted by julyerr on January 29, 2018

进程和线程

早期的计算机系统只允许一个程序运行,运行完毕一个之后才能运行新的程序。为了提高系统的资源利用率和吞吐量,引入多程序并发技术。

  • 进程

    相对独立的程序运行实体,拥有状态、资源等属性,是系统进行资源分配和调度的独立单位,从而表现出自己的生命周期(见下文)。

  • 线程

    也称为轻量级进程,主要是为了减少进程切换的开销,提高系统的并发效率。

两者的对比

主要下面四方面

  • 调度方面
    不同系统实现技术不同,Linux使用进程作为调度和分配单位,Windows则是采用线程进行调度。
  • 并发性
    引入线程的系统,进程之间并发同时线程之间也可以并发。
  • 资源占用
    进程作为资源拥有对象,占用文件描述符、信号量等信息;同一个进程中的多个线程共享进程的资源,每个线程只保留栈、寄存器、程序计数器和状态等。
  • 系统开销
    创建和删除进程需要回收PCB和系统资源,切换时也需要保存和恢复CPU环境。线程切换只需要保存和恢复少量的寄存器,开销很小。

线程的实现

有三种实现方式

  1. 用户空间实现线程
    用户程序调用线程库函数创建多个线程,而线程库内部只有一个内核级进程。每一个进程针对自己的线程维护了一个线程表(Thread Table)
    • 优势
      • 线程创建调用线程库函数实现,开销很小;
      • 线程切换没有必要和内核打交道,切换开销也很小;
      • 用户可以自定义调度算法
    • 缺点
      • 内核按照单线程进程处理进程,即使进程中有多线程每个时刻只能有一个线程运行;
      • 如果正在执行的线程阻塞,整个进程阻塞
  2. 内核空间实现线程
    线程表保存在内核中,线程调度直接通过内核实现
    • 优势
      • 一个进程的多个线程会被调度到多个CPU上同时运行
      • 一个线程阻塞之后不会影响其他线程工作
    • 劣势
      • 创建线程进行系统调用,开销比较大;
      • 线程切换,用户态和内核态切换,开销大

    Java线程在JDK 1.2之前是基于用户线程实现的,而JDK1.2中,线程模型替换为基于操作系统原生线程来实现。线程模型只是对线程的并发规模和操作成本产生影响,对Java程序的编码和运行来说,这些差异都是透明的。

  3. 用户和内核空间结合
    结合前两者优势,内核线程上有部分用户空间线程

进程的调度

  • 先来先服务(FCFS)
    先来临的进程运行完成之后才能运行下一个进程,不利于短作业运行
  • 短作业优先(SPF)
    选择运行时间最短的进程运行,不适合长作业的运行
  • 优先级调度算法(HPF)
    选择进程等待队列中优先级最高的进程进行调度
  • 高响应比调度算法(HRN)
    响应比 = 1 + 进程等待时间/进程执行时间,短作业响应比较高,能够优先运行完成;等待时间较长的进程也能通过响应比不断提高执行完成;兼顾长短作业,但是响应比有一定开销,适用于批处理系统
  • 多级反馈调度算法
    设置不同优先级队列,队列中使用时间片轮转的调度算法,且优先级越高时间片越短;进程先进入第一个队列中,时间片结束还没有运行完成则放入第二个队列,依次下去…只有第一个队列为空才会调度下一个队列进程运行。目前比较好的调度算法,适用于各种场景.
  • 时间片轮转(RR)
    执行完单位时间片之后,调度下一个进程运行。兼顾长短作业,但是平均等待时间较长,上下文切换频繁,适用于分时操作系统。

生命周期

进程生命周期

现在操作系统添加了其他新状态如下

  • 可中断睡眠状态:进程处于睡眠状态,但是可以被其他进程信号或者时钟中断唤醒
  • 不可中断睡眠状态:不可以被其他进程信号或者时钟中断唤醒
  • 暂停状态:进程暂停执行接受某种处理。如正在接受调试的进程
  • 僵死状态:进程已经结束但未释放PCB
    线程生命周期,基本上和进程类似,后面会结合java中线程状态转换进行详细说明

进程和线程互斥、同步

并发程序之间通过互斥、同步、通信方式来解决资源竞争和协作问题.通过上文两者对比,易知进程和线程竞争和协作实现原理基本一样。

竞争关系

  • 进程互斥
    若干个进程共享一个资源,但是资源只能在一个时刻被一个进程所拥有,被其他进程占用的时候其他的进程必须等待。容易产生问题
    • 死锁(deadlock)
      进程A所需资源被进程B占用,进程B所需资源被进程A所占用。 死锁处理方式
      1. 预防死锁 破坏产生死锁必要条件
        • 互斥条件 进程需要资源不能被其他进程同时占用
        • 请求和保持条件 进程阻塞不释放申请到的资源
        • 不可剥夺条件 进程已经申请的资源不可以被剥夺
        • 环路等待条件 发生死锁时存在进程-资源等待的环形链
      2. 避免死锁
        防止致使系统进入不安全状态资源分配,著名的银行家算法
      3. 检测死锁
        系统运行过程中检测是否存在死锁现象
      4. 解除死锁
        一旦检测到死锁现象存在,立即解除(撤销进程或者剥夺资源)
    • 饥饿(starvation)
      进程所需资源被其他的进程占用,久久不能获得。

协作关系

进程同步

一个进程的执行依赖于另一个协作进程的消息或信号。当没有获取另一进程的消息或者信号的时候需要等待,直到消息或者信号到达才被唤醒。

进程通信(IPC)

进程之间交换信息的过程。通常进程通信需要交换大量数据,进程同步是一种特殊的进程通信。

  • 实现方式
    1. 线程同步
      • 互斥锁
        一个线程进入临界区,首先申请加锁,如果其他线程正在使用,则等待直到其他线程释放锁。
      • 条件变量
        挂起线程直到其他线程触发条件,通常与互斥锁配合使用。
      • 信号量
        允许多个线程在同一个时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目。
    2. 进程间通信
      常见方式:管道、消息队列、共享内存、信号、信号量和套接字等.下文摘抄自Mr希灵1,总结很不错。
      • 管道管道
        是连接两个一个读进程和一个写进程之间用于实现数据交换的一个共享文件。包括无名管道有名管道两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。为了协调管道通信双方,需要管道机制实现如下功能:
      • 互斥:统一时刻只能有一个进程对管道进行读写; - 同步:当读端发现管道为空的时候需要睡眠等待,直到有数据时候被唤醒,相应的写端也是在管道已满的时候等待直到被唤醒; - 确定对方的存在性:只有同时有读端和写端,管道才有存在意义。
    • 信号量
      是一个计数器,可以用来控制某进程正在访问共享资源时,其他进程也访问该资源。
    • 消息队列
      是由消息存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。其基本思想是根据”生产者-消费者”原理,利用内存中公用消息缓冲区实现进程之间的信息交换

      每当一个进程向另一个进程发送消息时,便申请一个消息缓冲区,并把已准备好的消息送到缓冲区,然后把该消息缓冲区插入到接收进程的消息队列中,最后通知接收进程。接收进程收到发送里程发来的通知后,从本进程的消息队列中摘下一消息缓冲区,取出所需的信息,然后把消息缓冲区不定期归还给系统。系统负责管理公用消息缓冲区以及消息的传递。

    • 信号
      用于通知接收进程某个事件已经发生。
    • 共享内存
      映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
    • 套接字
      可以实现不同主机间的进程通信。一个套接口可以看做是进程间通信的端点,每个套接口的名字是唯一的,其他进程可以访问,连接和进行数据通信。

参考资料