9.14~10.3
进程和线程之间有什么区别
进程是资源分配和调度的基本单位。
线程是程序执行的最小单位,线程是进程的子任务,是进程内的执行单元。 一个进程至少有一个线程,一个进程可以运行多个线程,这些线程共享同一块内存。
资源开销:
由于每个进程都有独立的内存空间,创建和销毁进程的开销较大,进程间切换需要保存和恢复整个进程的状态,因此上下文切换的开销较高
线程共享相同的内存空间,创建和销毁线程的开销较小。线程切换只需要保存和恢复少量的线程上下文,因此线程上下文切换的开销较小。
通信与同步:
进程:由于进程间相互隔离,进程之间的通信需要使用一些特殊机制,如管道、消息队列、共享内存等。
线程:由于线程共享相同的内存空间,它们之间可以直接访问共享数据,线程间通信更加方便。
安全性:
进程:由于进程间相互隔离,一个进程的崩溃不会直接影响其他进程的稳定性。
线程:由于线程共享相同的内存空间,一个线程的错误可能会影响整个进程的稳定性。
并行和并发有什么区别
并行是在同一时刻执行多个任务,这些任务可以同时进行,每个任务都在不同的处理单元(如多个CPU核心)上执行。在并行系统中,多个处理单元可以同时处理独立的子任务,从而加速整体任务的完成。
并发是指在相同的时间段内执行多个任务,这些任务可能不是同时发生的,而是交替执行,通过时间片轮转或者事件驱动的方式。并发通常与任务之间的交替执行和任务调度有关。
解释一下用户态和核心态,什么场景下,会发生内核态和用户态的切换
用户态是指操作系统中普通进程运行的状态,这些进程拥有较低权限,只能访问特定范围的系统资源,比如文件系统、网络等。当进程执行时,大部分时间都在用户态下,它们按照操作系统的安全策略执行,遇到需要较高权限的操作(如内存管理、中断处理)时会切换到核心态。
核心态则是指操作系统的核心部分,即内核自身运行的状态。在这种状态下,内核拥有最高级别的权限,可以直接访问硬件资源,处理系统关键任务,如内存分配、中断处理、设备驱动等。
在以下场景下会发生模式切换:
- 系统调用:当用户程序需要请求操作系统提供的服务时,会通过系统调用进入内核态。
- 异常:当程序执行过程中出现错误或异常情况时,CPU会自动切换到内核态,以便操作系统能够处理这些异常。
- 中断:外部设备(如键盘、鼠标、磁盘等)产生的中断信号会使CPU从用户态切换到内核态。操作系统会处理这些中断,执行相应的中断处理程序,然后再将CPU切换回用户态。
进程调度算法你了解多少
第一个算法是先来先服务算法,这个算法实现简单,但是能导致较长作业阻塞较短作业。
第二个算法是最短作业优先算法,按估计运行时间最短的顺序进行调度。 但是如果一直有短作业到来,那么长作业永远得不到调度,造成长作业“饥饿”现象。
第三个算法是最短剩余时间有限,是基于最短作业优先的改进,按剩余运行时间的顺序进行调度,当一个新的作业到达时,其整个运行时间与当前进程的剩余时间做比较。如果新的进程需要的时间更少,则挂起当前进程,运行新的进程。否则新的进程等待。
第四个算法是优先级调度,他会为每个进程分配一个优先级,按优先级进行调度。为了防止优先级低的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
第五个算法是时间片轮转,为每个进程分配一个时间片,进程轮流执行,时间片用完后切换到下一个进程。
第六个算法是多级队列,时间片轮转调度算法和优先级调度算法的结合,将进程分为不同的优先级队列,每个队列有自己的调度算法。
进程间有哪些通信方式
首先可以通过管道通信,管道通信分为2种,一种是匿名管道,可以参考Linux的|符号,这个符号通常用于将|前面的输出用于|后面的输入,从这里可以看出,管道是一种半双工的通信方式,数据只能单向流动而且只能在具有父子进程关系的进程间使用。匿名管道效率比较低下,并且匿名管道知识临时的传输机制,结束之后会被释放,也没有给他命名。具体的实现方式是子进程去fork父进程的文件描述符来实现这种通信。还有第二种管道,叫命名管道,首先要给出名字,也是半双工的通信方式,但是它允许在不相关的进程间通信,并不局限与父子进程。管道通信效率是很低的,并不适合两个进程之间频繁的通信。基于这种想法,出现了消息队列。
消息队列是一种全双工的通信,他允许进程发送和接收消息,而消息队列是消息的链表,可以设置消息优先级。消息队列其实有一些缺陷,对资源存在浪费,假如现在对进程传递数据,是同一份资源,就会需要复制很多次,基于此的改进就是共享内存。
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的进程通信方式。但是共享内存也会带来许多问题,比如两个进程同时对一个资源进行操作。
这时候就会需要信号量来控制内存的使用权限,信号量是一个计数器,常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。
如果遇到突发的网络状况,信号的方式是最管用的,信号用于发送通知到进程,告知其发生了某种事件或条件。
最后还有一个Socket套接字的方式,是支持TCP/IP 的网络通信的基本操作单元,主要用于在客户端和服务器之间通过网络进行通信。
解释一下进程同步和互斥,以及如何实现进程同步和互斥
进程同步是指在多个并发执行的进程之间协调和管理它们的执行顺序,以确保它们按照一定的顺序或时间间隔执行。
互斥指的是在某一时刻只允许一个进程访问某个共享资源。当一个进程正在使用共享资源时,其他进程不能同时访问该资源。
解决进程同步和互斥问题的方法包括: 1. 临界区: 将可能引发互斥问题的代码段称为临界区。为了实现互斥,每个进程在进入临界区前必须获取一个锁,退出临界区后释放该锁。这确保同一时间只有一个进程可以进入临界区。
2. 互斥锁: 互斥锁是一种同步机制,用于实现互斥。每个共享资源都关联一个互斥锁,进程在访问该资源前需要先获取互斥锁,使用完后释放锁。只有获得锁的进程才能访问共享资源。
3. 信号量:信号量包括一个计数器和一组等待队列。进程可以尝试获取信号量,如果计数器大于零,则减少计数器并继续执行。否则,进程将进入等待队列,直到其他进程释放信号量。
4. 条件变量(Condition Variable): 条件变量用于在进程之间传递信息,以便它们在特定条件下等待或唤醒。通常与互斥锁一起使用,以确保等待和唤醒的操作在正确的时机执行。
什么是死锁,如何避免死锁?
死锁是系统中两个或多个进程在执行过程中,因争夺资源而造成的一种僵局。
死锁只有同时满足一下四个条件才会发生
- 互斥条件:一个进程占用了某个资源时,其他进程无法同时占用该资源
- 请求保持条件:一个进程因为请求资源而阻塞的时候,不会释放自己的资源
- 不可剥夺条件:资源不能被强制性地从一个进程中剥夺,只能由持有者资源释放
- 循环等待条件:多个进程之间形成一个循环等待资源的链,每个进程都在等待下一个进程所占有的资源。
如何避免死锁?
通过破坏死锁的必要条件之一来预防死锁,比如破坏循环等待条件,让所有进程按照相同的顺序请求资源。
介绍几种典型的锁
- 互斥锁:互斥锁是一种最常见的锁,用于实现互斥访问共享资源。在任何时刻,只有一个线程可以持有互斥锁,其他线程必须等待直到锁被释放。这确保了同一时间只有一个线程能够访问被保护的资源。
- 自旋锁:自旋锁是一种基于忙等待的锁,即线程在尝试获取锁时会不断轮询,直到锁被释放
其他锁都是基于以上两个锁的
- 读写锁:允许多个线程同时读共享资源,只允许一个线程进行写,也就是分为了共享和排他两种状态。
- 悲观锁:认为多线程同时修改共享资源的概率比较高,所以访问共享资源时要上锁
- 乐观锁:先不管冲突的概率,先修改了再说,出现了同时修改的情况,再放弃本次操作
讲一讲你理解的虚拟内存
虚拟内存是指在每一个进程创建加载的过程中,会分配的一个连续虚拟地址空间,不是真实存在的,而是通过映射与实际物理地址空间对应。
需要虚拟内存的原因:
- 内存扩展:虚拟内存使得每个程序都可以使用比实际可用内存更多的内存,从而允许运行更大的程序或处理更多的数据。
- 内存隔离:虚拟内存还提供了进程之间的内存隔离。每个进程都有自己的虚拟地址空间,因此一个进程无法直接访问另一个进程的内存。
- 物理内存管理:虚拟内存允许操作系统动态地将数据和程序的部分加载到物理内存中,以满足当前正在运行的进程的需求。当物理内存不足时,操作系统可以将不常用的数据或程序暂时移到硬盘上,从而释放内存,以便其他进程使用。
- 页面交换:当物理内存不足时,操作系统可以将一部分数据从物理内存写入到硬盘的虚拟内存中,这个过程被称为页面交换。当需要时,数据可以再次从虚拟内存中加载到物理内存中。这样可以保证系统可以继续运行,尽管物理内存有限。
- 内存映射文件:虚拟内存还可以用于将文件映射到内存中,这使得文件的读取和写入可以像访问内存一样高效。
你知道的线程同步的方式有哪些?
线程同步机制是指在多线程编程中,为了保证线程之间的互不干扰,而采用的一种机制,常见的线程同步机制有以下几种:
- 互斥锁:互斥锁是最常见的线程同步机制,他允许只有一个线程同时访问被保护的临界区(共享资源)
- 条件变量:条件变量用于线程间通信,允许一个线程等待某个条件满足,而其他线程可以发出信号通知等待线程。通常与互斥锁一起使用。
- 读写锁:读写锁允许多线程同时读,但只允许一个线程写
- 信号量:用于控制多个线程对共享资源进行访问的工具
有哪些页面置换算法?
首先,页面置换算法就是当cpu访问的页面不在物理内存中的时候,产生一个缺页中断,就需要调入新的页面到内存中,由于此时的内存是满的,所以就需要从内存中选择一个被置换的页面,常用的页面置换算法有以下5种:
- 最近最久未使用算法LRU:LRU算法基于页面的使用历史,通过选择最长时间没有被使用的页面进行置换
- 先进先出算法FIFO:也就是最先进入内存的页面最先一个被置换出去
- 最不经常使用算法LFU:考虑页面的访问频率,淘汰访问次数最少的页面
- 时钟算法CLOCK:clock算法的核心思想是通过使用一个时钟指针在环形链表上遍历,检查页面是否被访问过,当需要进行页面置换时,clock算法从时钟指针的位置开始遍历环形链表。
- 最佳置换算法:该算法根据未来页面访问情况,选择最长时间内不会被访问到的页面进行置换。这种算法只是一种理想情况下的置换算法,还没实现。