总结来自于javaguide,本文章仅供个人学习复习
javaguide操作系统八股
文章目录
操作系统基础
什么是操作系统?
什么是操作系统?
- 操作系统是管理计算机硬件与软件资源的程序,是计算机的基石
- 操作系统本质上是一个运行在计算机上的软件程序,主要用于管理计算机硬件和软件资源.
- 操作系统的存在屏蔽硬件层的复杂性.操作系统是硬件使用的负责人
- 内核(kernel)是操作系统的核心部分,负责系统的内存管理,CPU\磁盘等硬件管理,文件系统管理,应用程序管理.
应用程序<-->内核<-->CPU,内核是OS层面,CPU是硬件层面
操作系统主要有哪些功能?
操作系统主要有哪些功能?
- 进程和线程的管理
- 存储管理,内存\外存
- 文件管理,文件的CRUD
- 设备管理,完成设备(输入输出设备)
- 网络管理,操作系统负责管理计算机网络的使用.
- 安全管理,用户的身份认证\访问控制\文件加密
常见的操作系统有哪些?
常见的操作系统有哪些?
Windows\Unix\Linux\mac
Linux严谨应该称为GNU/Linux,Linux实际就是Linux内核,其余部分主要由GNU工程编写和提供的程序组成,操作系统除了内核还有用户界面\设备驱动程序\文件系统等
用户态和内核态
常见的操作系统有哪些?
- 用户态:用户态运行的进程可以直接读取用户程序的数据,拥有较低的权限.
- 内核态:内核态运行的进程几乎可以访问计算机的任何资源包括系统的内存空间\设备\驱动程序,拥有非常高的权限.
进入内核态需要付出较高的开销(需要进行一系列的上下文切换和权限检查),应该尽量减少进入内核态的次数.
为什么要有用户态和内核态?只有一个内核态不行嘛?
常见的操作系统有哪些?
- 在CPU的所有指令中,有一些指令是比较危险的比如内存分配\IO处理,如果所有的程序都能使用这些指令的话,很危险.
只能由操作系统内核执行的指令被称为特权指令. - 如果只有一个内核态,所有程序或进程必须共享系统资源,CPU等,将导致系统资源的竞争和冲突,影响性能.
为了让不同的进程有不同的权限
用户态和内核态是如何切换的?
用户态切换到内核态的3种方式:
-
系统调用(Trap)
用户态进程主动要求切换到内核态的一种方式,系统调用的机制核心还是使用了OS为用户特别开放的一个中断实现. -
中断(Interrupt)
当外围设备完成用户请求的操作后,向CPU发出响应的中断信号,这时 CPU 会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。 -
异常(Exception)
当CPU在执行运行在用户态时,发生异常.
异常是由指令造成的,中断是来自处理器外部的例子
用户程序通过系统调用请求从硬盘读取数据:
用户程序调用 read(),陷入内核态。
内核通过硬盘驱动向硬盘发送读取请求。//发完请求到读取硬盘数据这段时间,进程或者CPU就可能干用户态别的去了
硬盘完成数据读取后,发出中断信号:
CPU 接收到硬盘中断信号。
当前的用户态程序暂停执行,切换到内核态,执行硬盘中断处理程序。
硬盘中断处理程序完成后:
内核将读取的数据放入内存缓冲区。
通知用户程序读取操作已完成,切换回用户态。
用户态和内核态的切换,本质上是 CPU 的运行模式在用户态和内核态之间的切换,而不是进程本身切换状态。
系统调用
什么是系统调用?
运行的程序基本都运行在用户态,系统调用通过调用操作系统提供的内核态级别的子功能.
按功能分为:
- 设备管理
- 文件管理
- 进程管理
- 内存管理
系统调用和普通库函数的调用很相似,只是系统调用由操作系统内核提供,运行于内核态
系统调用是应用程序与操作系统之间进行交互的一种方式,通过系统调用,应用程序可以访问操作系统底层资源.
系统调用的过程了解吗?
- 用户态的程序发起系统调用,因为系统调用中设计一些特权指令,用户态程序权限不足,因此会中断执行,也就是Trap
- 发生中断后,当前CPU执行的程序会中断,跳转到中断处理程序,内核程序开始执行,也就是开始处理系统调用.
- 内核处理完成后,主动触发Trap,这样会再次发生中断,切换回用户态工作.
进程和线程
linux系统上用户的关系
每个用户都有用户ID(UID),组ID(GID)
文件的面向不同的用户的权限:
- 文件所有者的权限
- 文件所属组的权限
- 其他所有用户的权限
权限有r(读)w(写)x(执行权限)
线程\进程\子进程的关系
子进程由父进程fork()创建,进程\子进程有独立的PCB,一个进程至少有一个主线程
线程是OS调度的最小单位,Linux中线程是特殊类型的进程,多个线程共享同一进程资源(内存\文件描述符),线程有独立的TCB
进程\子进程有独立的PID,子进程有PPID,线程有独立的TID,一个进程的多个线程共属一个PID
系统启动时会创建一个init(较新版本里时systemd)进程(祖父进程),每打开一个终端就是创建了一个进程,在这个终端中运行一个程序就是创建一个子进程,当关闭这个终端,这个程序还在运行,就会被systemd进程收养.
在子进程的父进程结束,还没被systemd收养的时间段是孤儿进程
什么是进程和线程?
什么是进程和线程?
- 进程,计算机中正在运行的一个程序实例
- 线程,被称为轻量级进程.
进程和线程的区别是什么?
进程和线程的区别是什么?
- 从JVM的角度分析,一个进程可以有多个线程,多个线程共享进程的堆和方法区,但每个线程有自己的程序计数器\虚拟机栈和本地方法栈
- 线程是进程划分的更小的运行单位,一个进程在其执行的过程中可以产生多个线程
- 线程和进程最大的不同在于基本上各进程是独立的,而各线程则不一定,因为同一进程中的线程极可能互相影响
- 线程执行开销小,但不利于资源的管理和保护
有了进程为什么还需要线程?
有了进程为什么还需要线程?
- 进程切换是一个开销很大的操作,线程切换的成本低
- 线程更轻量,一个进程可以创建多个线程
- 多个线程可以并发处理不同的任务,更有效地利用了多处理器和多核计算机.而进程只能在一个时间干一件事
- 同一进程内的线程共享内存和文件,因此它们之间互相通信无需调用内核
为什么要使用多线程?
为什么要使用多线程?
- 从计算机底层来说:
线程可以比作是轻量级的进程,线程间的切换和调度的成本远远小于进程. - 从当代互联网发展趋势来说:
现在的系统动不动要求几百万级的并发量,多线程正是并发系统的基础,利用好多线程机制可以大大提高系统整体的并发能力性能. - 单核情况:单核时代多线程主要是为了提高进程利用CPU和IO系统的效率.比如当请求IO时,如果JAVA进程中只有一个线程,此线程被IO阻塞则整个进程被阻塞.
- 多核时代:多核时代多线程主要是为了提高进程利用多核CPU的能力.如果一个复杂任务,只用一个线程的话,不论线程有几个CPU都只会有一个CPU核心被利用.
线程间的同步的方式有哪些?
线程间的同步的方式有哪些?(这里的同步指的是一个线程内一段代码的同步,在一个完整的时间片执行)
线程同步是两个或多个共享关键资源的线程的并发执行.应该同步线程以避免关键的资源使用冲突.
几种方式:
- 互斥锁(Mutex)
采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限.比如java中的synchornized锁和Lock锁 - 读写锁(Read-Write Lock)
允许多个线程同时读取共享资源,但只有一个线程可以对共享资源写操作 - 信号量(Semaphore)
允许同一时刻多个线程访问同一资源,但需要控制同一时刻访问此资源的最大线程数量. - 屏障(Barrier)
屏障是一种同步原语,用于等待多个线程到达某个点再一起继续执行.比如java中的CyclicBarrier - 事件(Event)
Wait\Notify,通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作.
PCB是什么?包括哪些信息?
进程控制块,是操作系统中用来管理和跟踪进程的数据结构.每个进程一个PCB,视为进程的大脑.
当创建一个新进程时,为该进程分配一个唯一的进程ID,并创建PCB.进程执行时,PCB中的信息不断变化,操作系统根据这些信息来管理和调度进程.
内容:
- 进程的描述信息.进程的名称\标识符
- 进程的调度信息,进程阻塞原因\进程状态\进程优先级
- 进程对资源的需求情况,CPU时间\内存空间\IO设备
- 进程打开的文件信息,文件描述符\文件类型\打开模式
- 处理机的状态信息(由处理机的各种寄存器中的内容组成),包括通用寄存器\指令计数器\程序状态字PSW\用户栈指针
进程有哪几种状态?
进程有哪几种状态?
这一点和线程很像,但不太一样,具体看多线程
- 创建状态
- 就绪状态,有执行资源(除CPU的一切资源),无执行权(CPU)
- 运行状态
- 阻塞状态,没有执行资源,没有执行权(CPU)
- 结束状态
进程间的通信方式有哪些?
- 管道/匿名管道,有亲缘关系的父子兄弟进程之间
- 有名管道,匿名管道没有名字,只能用于亲缘关系的进程间的通信.有名管道遵循先进先出,以磁盘文件方式存在,实现本机任意两个进程通信
- 信号(signal),信号是一种比较复杂的通信,用于通知接收进程某个事件已发生
- 消息队列,消息队列是消息的链表,有特定的格式,存放在内存中并由消息队列标识符标识.也是先进先出的原则.
与管道不同的是消息队列存放在内核中,只有在内核重启或者显式地删除一个消息队列时,该消息队列才会被真正地删除.消息队列可以实现消息地随机查询,消息不一定要以先进先出次序读取,也可以按消息地类型读取.
消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点
无名管道,只存在于内存中的文件
有名管道,存在于实际的磁盘介质或文件系统 - 信号量(semaphores)
信号量是一个计数器,用于多线程对共享数据的访问,信号量的意图在于进程间同步.这种通信方式主要用于解决与同步相关的问题并避免竞争条件 - 共享内存
使多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程种对共享内存种数据的更新.这种方式需要依靠某种同步操作,如互斥锁和信号量等.可以说这是最有用的进程间通信方式. - 套接字
此方法主要用于在客户端和服务器之间通过网络进行通信.端口通信
进程的调度算法有哪些?(重要)
进程的调度算法有哪些?
- 先到先服务调度算法
- 短作业优先的调度算法,从就绪队列中选出一个估计运行时间最短的进程为之分配资源,使它立即执行并一直执行到完成或发生某事件而被阻塞放弃占用 CPU 时再重新调度。
- 时间片轮转调度算法,每个进程被分配一个时间段,称作它的时间片,即该进程允许运行的时间。
- 多级反馈队列调度算法,
前面介绍的几种进程调度的算法都有一定的局限性。如短进程优先的调度算法,仅照顾了短进程而忽略了长进程 。多级反馈队列调度算法既能使高优先级的作业得到响应又能使短作业(进程)迅速完成,因而它是目前被公认的一种较好的进程调度算法,UNIX 操作系统(windows也是\mac使用类似的)采取的便是这种调度算法。
核心思想:
根据进程的优先级划分多个队列,每个队列可能有不同的时间片大小。
优先级较高的进程先运行,优先级较低的进程在资源空闲时运行。
随着时间推移,长时间未运行的低优先级进程可能被动态提升优先级,以防止饥饿。 - 优先级调度算法,具有相同优先级的进程以 FCFS 方式执行
什么是僵尸进程和孤儿进程?
什么是僵尸进程和孤儿进程?
当一个进程调用 exit()系统调用结束自己的生命时,内核会释放该进程的所有资源,包括打开的文件、占用的内存等,但是该进程对应的 PCB 依然存在于系统中。这些信息只有在父进程调用 wait()或 waitpid()系统调用时才会被释放,以便让父进程得到子进程的状态信息。
父进程结束,僵尸状态的子进程也会被清理,以上的情况适用在子进程结束,父进程还运行的情况
wait()函数,子进程没结束,父进程适用wait()会等待
- 僵尸进程
子进程已经终止,但是其父进程仍在运行,且父进程没有调用 wait()或 waitpid()等系统调用来获取子进程的状态信息,释放子进程占用的资源,导致子进程的 PCB 依然存在于系统中,但无法被进一步使用。这种情况下,子进程被称为"僵尸进程"。避免僵尸进程的产生,父进程需要及时调用 wait()或 waitpid()系统调用来回收子进程。 - 孤儿进程
一个进程的父进程已经终止或者不存在,但是该进程仍在运行。这种情况下,该进程就是孤儿进程。孤儿进程通常是由于父进程意外终止或未及时调用 wait()或 waitpid()等系统调用来回收子进程导致的。为了避免孤儿进程占用系统资源,操作系统会将孤儿进程的父进程设置为 init 进程(进程号为 1),由 init 进程来回收孤儿进程的资源。
如何查看是否有僵尸进程?
Linux 下可以使用 Top 命令查找,zombie 值表示僵尸进程的数量,为 0 则代表没有僵尸进程。
下面这个命令可以定位僵尸进程以及该僵尸进程的父进程:
ps -A -ostat,ppid,pid,cmd |grep -e '^1^'
死锁
什么是死锁?
什么是死锁?
死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。
能列举一个操作系统发生死锁的例子吗?
嵌套锁
产生死锁的四个必要条件是什么?
产生死锁的四个必要条件是什么?
- 互斥
- 占有并等待
- 非抢占,资源不能被抢占.只有在持有资源的进程完成任务后,该资源才会被释放
- 循环等待,有一组等待进程{p0,p1...},P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,......,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。
这四个条件是产生死锁的 必要条件 ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。
能写一个模拟产生死锁的代码吗?
操了,手撕是吧
解决死锁的方法
- 预防,限制并发进程对资源的请求,从而使得死锁的必要条件在系统执行的任何时间上都不满足
- 避免,系统在分配资源时,根据资源的使用情况提前做出预测,从而避免死锁的发生
- 检测,系统设有专门的机构,当死锁发生时,该机构能够检测死锁的发生,并精确地确定与死锁有关的进程和资源
- 解除,与检测相配套的一种措施,用于将进程从死锁状态下解脱出来
- Zz ↩︎