操作系统02——进程和线程2

147 阅读6分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

线程同步有哪些方式?

为什么需要线程同步:线程可能会共享一些资源,比如内存数据库等。当多个线程同时读写同一份共享资源的时候,可能会发生冲突。因此需要线程的同步,多个线程按顺序访问资源。

  • 信号量 Semaphore:信号量是内核对象允许同一时刻多个线程访问同一资源信号量会控制同一时刻访问此资源的最大线程数量。信号量对象保存了最大资源计数当前可用资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就减1,只要当前可用资源计数大于0,就可以发出信号量信号,如果为0,则将线程放入一个队列中等待。线程处理完共享资源后,应在离开的同时通过ReleaseSemaphore函数将当前可用资源数加1。如果信号量的取值只能为0或1,那么信号量就成为了互斥量
  • 互斥量 Mutex:互斥量是内核对象只有拥有互斥对象的线程才有访问互斥资源的权限互斥量保证了关键指令的原子性,使得在同一时刻只有一个线程能操作临界资源,因为互斥对象只有一个,所以可以保证互斥资源不会被多个线程同时访问;当前拥有互斥对象的线程处理完任务后必须将互斥对象交出,以便其他线程访问该资源;
  • 条件变量条件变量允许线程睡眠,当满足条件时,可以向线程发送信号将其唤醒。
  • 读写锁读写锁是一种特殊的自旋锁,允许多个者同时访问资源以提高读性能,对于操作则是互斥的(同一时刻只能允许线程对临界资源有多个读操作或者一个写操作)。
  • 自旋锁:使用自旋锁的线程反复检查自旋锁变量是否可用,若自旋锁被占用,该线程不会让出CPU,会进入死循环的忙等待状态直到锁被释放
  • 临界区 Critical Section:任意时刻只允许一个线程对临界资源进行访问。拥有临界区对象的线程可以访问该临界资源,其它试图访问该资源的线程将被挂起,直到临界区对象被释放
  • 事件 Event:允许一个线程在处理完一个任务后,主动唤醒另外一个线程执行任务。事件分为手动重置事件和自动重置事件。手动重置事件被设置为激发状态后,会唤醒所有等待的线程,而且一直保持为激发状态,直到程序重新把它设置为未激发状态。自动重置事件被设置为激发状态后,会唤醒一个等待中的线程,然后自动恢复为未激发状态。

互斥量和临界区有什么区别?

互斥量是可以命名的,可以用于不同进程之间的同步;而临界区只能用于同一进程中线程的同步。创建互斥量需要的资源更多,因此临界区的优势是速度快,节省资源

进程有哪几种状态?

进程控制块(PCB)是进程实体的一部分,是操作系统中重要的记录型数据结构。PCB中记录了操作系统所需要的、用于描述进程情况及控制进程运行所需要的全部信息

  • 创建状态:创建进程时分配了PCB,但其他资源尚未就绪的状态称为创建状态。
  • 就绪状态:当进程已经获得除CPU以外的所需资源,只要再获得CPU使用权就可以立即运行。
  • 执行状态:进程获得CPU,程序正在执行,在单CPU中一个时刻只能有一个进程可处于执行状态。
  • 阻塞状态:进程等待某种条件而放弃CPU的状态称为阻塞状。
  • 中止状态:进程结束由系统清理、归还PCB的状态称为终止状态。

就绪队列:一个系统中多个处于就绪状态的进程排成一个队列。

阻塞队列:一个系统中多个处于阻塞状态的进程排成一个队列。

进程切换步骤

进程切换的几步:

(1)保存之前运行的进程上下文

将PC寄存器指向当前进程,用PSW(程序状态寄存器)记录进程的制信息,并将PC寄存器和PSW存入进程的堆栈中,然后用处理器的堆栈指针SP(Stack Point)记录当前运行进程的PCB信息,再将SP存入PCB中。

(2)调用准备运行的进程上下文

然后调用即将运行的进程上下文也是类似的过程,将PCB中的信息存入SP中,从即将运行的进程的私有堆栈中恢复各个处理器寄存器的数据。

(3)CPU使用权交接

概念

PCB(进程控制块) ,是进程实体的一部分,是操作系统中重要的记录型数据结构。PCB中记录了操作系统所需要的、用于描述进程情况及控制进程运行所需要的全部信息

PC寄存器(程序计数器) ,用于存放程序将被CPU执行的指令的地址。

PSW(程序状态寄存器) ,CPU有两种工作模式:内核态和用户态,用PSW中的二进制位控制这两种模式。

SP(堆栈指针) ,程序有自己的堆栈数据结构,SP指针指向栈顶的地址,CPU会根据SP指示的地址与程序的堆栈数据结构交换数据。

JVM 程序计数器、操作系统的程序计数器的关系和区别?

\1. 较小的内存空间,当前线程执行的字节码的行号指示器;

\2. 各线程之间独立存储,互不影响。

\3. 程序计数器是一块很小的内存空间,主要用来记录各个线程执行的字节码的地址,例如,分支、循环、跳转、异常、线程恢复等都依赖于计数器。

\4. 由于 Java 是多线程语言,当执行的线程数量超过 CPU 核数时,线程之间会根据时间片轮询争夺 CPU 资源。如果一个线程的时间片用完了,或者是其 它原因导致这个线程的 CPU 资源被提前抢夺,那么这个退出的线程就需要单独的一个程序计数器,来记录下一条运行的指令。

\5. 因为 JVM 是虚拟机,内部有完整的指令与执行的一套流程,所以在运行 Java 方法的时候需要使用程序计数器(记录字节码执行的地址或行号),如果 是遇到本地方法(native 方法),这个方法不是 JVM 来具体执行,所以程序计数器不需要记录了,这个是因为在操作系统层面也有一个程序计数器,这个 会记录本地代码的执行的地址,所以在执行 native 方法时,JVM 中程序计数器的值为空 (Undefined)。

\6. 另外程序计数器也是 JVM 中唯一不会 OOM (OutOfMemory) 的内存区域。