[Day17] 操作系统八股文复习

操作系统

目录

[toc]

操作系统基础

计算机打开电源操作系统做了什么?

  1. BIOS 通电进行自检,检查计算机的硬件设备是否都正常,比如 CPU、内存、硬盘等设备如果有错则会将错误显示在屏幕上;
  2. 加载引导程序;BIOS 会查找有操作系统的硬盘进入系统盘的引导分区加载系统引导程序
  3. 加载系统内核到内存中;加载完成内核后还会执行初始化操作比如初始化设备、内存、进程调度等核心组件
  4. 然后就是记载硬件所需要的驱动程序;然后启动系统服务,比如 Docker、远程登录、防火墙等;
  5. 进入系统界面等待任务输入;

什么是用户态、内核态?如何切换的?

内核态是操作系统的核心,他能直接地操作所有系统资源,比如 CPU,内存等;如果所有的应用程序都运行在内核态上虽然效率很高但是同时也伴随着超高的故障风险;如果一个应用线程错误地操作了敏感资源那么后果是十分严重的;所以就划分出了用户态,用户应用程序都运行在用户态上;这样就起到了隔离的效果,降低了内核态错误的风险;但是用户态也会在运行过程中请求操作系统资源,这个时候就会将用户态切换到内存态;

用户态切换到内存态有三种方式:系统调用、中断、异常;

  • 系统调用:用户态中的进程主动请求用户态的权限;比如用户态中的进程请求读写文件
  • 中断:中断是指硬件设备比如键盘或则时钟这些输入设备发出了中断信号那么就会将正在执行的用户态切换到内核态
  • 异常:指的是在用户态下进行出现了未处理的异常, 那么就会从用户态切换内核态,内核态检查异常原因然后响应错误信息

什么是系统调用?

系统调用是指操作系统内核态提供给上层用户态的函数操作接口,可以和平时普通的方法调用一样;不同的是用户态的进程如果想调用这些内核态的高权限级别的函数不能自己直接调用,而需要通过系统调用,先向系统发起调用请求然后中断内核态进行,当内核态调用完成后将结果返回给用户态中断处;系统调度的开销很大,所以在用户态的程序开发中我们有时候会通过一些手段尽量减少系统调用和用户态到内核态的切换,比较典型的就是自旋锁,通过短暂的自旋等待来减少线程切换带来的系统开销;

什么是操作系统的多级缓存

多级缓存其实在开发过程中无处不在;比如后端的 Mysql、redis、caffeine;用户层面使用的 DNS 多级缓存;都是多级缓存思想的体现;而多级缓存主要解决的问题就是各级缓存之间容量和读取速度有差异的问题; CPU 缓存采用的是三级缓存架构,L 1 、L 2、 L 3 然后是内存最后是硬盘; L 1 是最靠近 CPU 核心的他缓存的是指令和数据,是每个核心单独的缓存空间; L 2 也是每个核心独立的,他的目的是为 L 1 缓存数据和充当缓冲区的作用; L 3 是多个核心共享的; 越远离 CPU 核心的缓存容量越高速度越慢;之所以设计成三级缓存主要还是要提高利用率和缓存命中率; 多级缓存到来效率提升的同时也有这新的风险,比如共享的缓存就必须要考虑他的并发性;确保并发操作下各个核心和数据之间的可见性;同时要有写操作的时候,脏页没有刷盘发生故障造成数据丢失;还有就是如何设计合理的内存淘汰算法让缓存在有限空间内尽可能多的提高命中率;

什么是 MESI 缓存一致性协议

MESI 是为了解决多核 CPU 共享缓存数据的并发问题;在最开始解决 CPU 层面多核心的并发问题是通过最简单的给总线上锁,确保同一时间只有一个 CPU 对缓存进行操作,但这样会造成其他核心阻塞性能很低下;而 EMSI 的解决思路则是通过对共享的缓存行数据打上四种不同的标记来实现并发控制的;MESI; 先说最理想情况下没有并发问题的两个状态,S Shard,如果一个核心读取数据时发现状态是 S 则说明这个缓存行和主内存还有其他核心中的数据是一致的可以放心读取;E Exclusive 独占的,表示当前缓存行的数据只有当前核心在使用可以读取和修改;如果共享的数据被修改那么就会有并发问题,一个核心修改了状态为 S 的缓存行那么他就会将缓存行改为 M Modified 被修改过了,并且同时其他核心会监听到这个操作会将他们自己缓存中的该数据缓存行改为无效的;然后修改的核心会将 M 状态的数据同步到主缓存中然后把数据改成 S,其他状态为 I 的核心如果需要读取那么会先去主内存中读取,然后状态改为 S;如果一个独占的缓存行被其他核心请求读取那么就会共享出去并且双方的状态都改为 S;这就是 MESI 解决缓存一致性问题时四种状态转换的过程; 局限性:在读写频率很高的情况在会在总线之间发送大量的同步信息,可能造成总线风暴

有了 MESI 为啥还需要 JMM?

MESI 和 JMM 虽然都是解决缓存一致性问题的但是他们所工作的层次不一样;MESI 是针对 CPU 核心缓存层面的 JMM 是 JMV 中各个线程之间的;JMM 除了要保证各个线程对共享变量的可见性外,还需要保证有序性和原子性; MESI 是硬件层面的缓存一致性协议,JMM 是语言成名的内存模型; MESI 实现同步的是状态标志加上总线嗅探,JMM 的实现方式是内存屏障加 happens-before 原则

GPU 和 CPU 区别?为什么挖矿、大模型都用 GPU?

他们两个的区别主要来自于他们的架构不同;最开始是没有独立的显卡只有 CPU,但是 CPU 核心很少,在处理复杂逻辑方面很厉害但是在计算任务量很大的简单计算的时候效率很低,比如个说矩阵运算,而像矩阵运算这种在图像处理和机器学习方面计算量很大的场景在 CPU 就很难应付,所以就出现了 GPU,他就是针对 CPU 的短边进行设计的,把 CPU 不擅长的任务进行分担;所以 GPU 核心很多,并行效率很高,做简单的计算效率很高;总结来说就是复杂的逻辑处理是 CPU 的主要任务,而涉及到需要大量简单计算的任务就是 GPU 的职责;所以在区块链、图像处理还有人工智能这些场景下显卡就等等同于算力,这几年每一次的技术革新都依赖于算力所以做显卡的公司比如英伟达就是快速发展的;

为什么按位与运算要比取模运算高效?

因为位运算的时钟周期很短,比如同样的一个除以 2 的操作,如果通过位运算只需要左移一位即可一个时钟周期,但是如果是用除法的话就会很慢,在取余的时候更明显,比如对 2 取余,位运算只需要对数字与上 1 即可,但是用取模操作底层就是根据牛顿迭代方法一步步除知道除不尽为止,这样的时钟周期会很长

进程和线程

进程,线程和协程的区别

进程是系统分配资源的基本单位,而线程是进程中的基本调度单位;从 JVM 层面理解,一个 JVM 就是一个独立的进程,一个进程内可以有多个线程,进行的目的是在应用之间进行隔离,而线程是为了在一个进程内进行并发的任务;协程这个概念其实是用户态的工具,他不同于进行和线程都是由系统调度,他是用户创建和调度操作系统并不会感知到协程;协程的系统开销和调度开销都是最小的他的目的就是协助线程中的某些任务,比如在高并发和 IO 密集型的场景下协程就能提高效率;

什么是时间片

CPU 时间片是 CPU 进行任务调度时,每个任务执行的基本单位;也就是说 CPU 通过轮转的方式将执行权交给每个任务,让他们交替执行,获得时间片的任务开始执行任务,如果时间片耗尽那么就主动让出时间片挂起当前状态等待下次获取到时间片;

线程间的同步的方式有哪些 ? #🎦review

线程同步指的是在多线程并发操作的工程中如何组织线程之间的执行顺序和对共享资源的操作权限,如果线程之间不同做同步措施的话就发有并发问题;所以同步就是解决两件事情:1. 线程之间如何执行;2. 共享资源如何操作

所以常见的同步方式有: 互斥锁:互斥锁只能让持有锁的线程有执行权和对共享资源的操作权;过程中没有并发所以就避免了并发问题;很明显互斥锁虽然是最安全的但是效率也是最低的,所以在读多写少的场景下我们可以使用读写锁; 读写锁:允许多个线程同时读取资源,但是只能允许一个线程修改资源; 信号量:信号量可以实现控制同一时刻读取资源的数量,一个线程读取完成之后会释放信号量交给其他等待的线程 条件变量: 一般是给定一个前提条件,只有条件满足的情况下才有权限执行;

java 中常见的同步方式有:synchronized,ReentrantLock,Semaphore,countdownLatch,cyclicBarrier

ReentrantLock

public class Example {
    private final Lock lock = new ReentrantLock();

    public void method() {
        lock.lock(); // 获取锁
        try {
            // 临界区代码
        } finally {
            lock.unlock(); // 释放锁
        }
    }
}

Semophore

  • 可以控制是执行任务的数量
public class Example {
    private final Semaphore semaphore = new Semaphore(2); // 允许2个线程同时访问

    public void main() {
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    // 访问资源
                    semaphore.release(); // 释放许可
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

CyclicBarrier

  • 可以控制多个线程同时到达某个点后同步执行后续任务
public class Example {
    private final CyclicBarrier barrier = new CyclicBarrier(2);

    public void main() {
        new Thread(() -> {
            // 执行任务
            try {
                barrier.await(); // 等待所有线程到达
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 继续执行
        }).start();

        new Thread(() -> {
            // 执行任务
            try {
                barrier.await(); // 等待所有线程到达
            } catch (Exception e) {
                e.printStackTrace();
            }
            // 继续执行
        }).start();
    }
}

CountDownLatch

  • 可以控制多个线程执行后的后续执行任务
public class Example {
    private final CountDownLatch latch = new CountDownLatch(2);

    public void main() {
        new Thread(() -> {
            // 执行任务
            latch.countDown(); // 完成任务,减少计数
        }).start();

        new Thread(() -> {
            // 执行任务
            latch.countDown(); // 完成任务,减少计数
        }).start();

        try {
            latch.await(); // 等待所有线程完成
            System.out.println("所有线程已完成");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

PCB 和 TCB 是什么?包含哪些信息?

PCB 是 Process Control Block, 进行控制块,TCB 是 Thread Control Block 线程控制块; 他们分别是一个进程和线程的总要组成部分,系统调度进程和线程都是通过这个控制块进行的;控制块记录了当前进行和线程的线程信息:比如线程 ID,进程所属用户 id;线程上下文:比如线程程序计数器(用于恢复线程线程)、CPU 寄存器(恢复进程现场);持有资源信息:比如持有内存地址段、占用系统端口信息、IO 设备端口占用信息等;

进程和线程有哪几种状态?

进程状态

  • 就绪 Ready
  • 运行 Running
  • 阻塞 Blocked
  • 终止 terminated;

线程状态

线程的状态有 6 种; 这 6 种已经通过枚举的方式定义好了;分别是 new - runnable - blocked - waiting - timed_waiting - terminated

当线程被创建的时候状态为 new; 调用 strat 方法后开始运行线程执行任务, 状态从 new 转为 runnable; 如果在执行过程中需要等待锁那么就会转为 Blocked 阻塞状态, 获取到锁后继续执行; 程序过程中还可能触发等待就会变成 waiting 或者是 timed_waiting 区别就是等待是定时唤醒还是等待其他唤醒; 最后执行完成后 run 就会变成 terminated;

wait 是将当前线程挂起并释放锁等待其他线程唤醒; join 是不释放锁阻塞, 一般是主线程等待子线程完成任务时使用;

状态 说明
NEW(新建) 线程被创建,但尚未调用 start() 方法。
RUNNABLE(可运行) 线程已启动(调用了 start()),可能正在执行或等待 CPU 资源(就绪/运行)。
BLOCKED(阻塞) 线程等待获取锁(如进入 synchronized 代码块时锁被其他线程占用)。
WAITING(无限等待) 线程主动进入等待状态,需其他线程显式唤醒(如调用 wait() 或 join())。
TIMED_WAITING(限时等待) 线程在指定时间内等待(如 sleep(ms)wait(timeout)join(timeout))。
TERMINATED(终止) 线程执行完毕(run() 方法结束)或异常退出。

进程间的通信方式有哪些?

管道:管道技术是单工的,只允许有个方向向另一个方向发送信息,一般用于父进程向子进程发送消息; 共享内存:允许多个进行共享同一片内存取余,数据传输速度快而且是双向的; 信号量:可以控制指定量的进程对资源进行访问 消息队列:一般用借助中间件在分布式系统下实现 套接字: socket 和网络通信中的 webSocket 相同,可以在本地两个进程中建立连接然后通信

常见的进程调度算法有哪些?

先到先服务:顾名思义,这种方式如果执行到了一个长任务可能造成后续任务出现饥饿进程 短作业有限:短作业优先分为可抢占和不可抢占,优先调用执行时间段的任务;如果用长短执行长短来决定调用优先级也会有局限性,比如如何准确判定一个进程的执行时间,还有可能出现长任务饥饿的问题; 优先级调用:同样也是分为可抢占和不可抢占,优点是高优先级的任务可以流畅执行, 但是低优先级任务可能出现饥饿; 时间片轮转:按照固定的时间为每个进程分配时间片;优点比较公平不太会出现线程饥饿,但是频繁的进程切换会带来更大的性能开销; 多级反馈队列: 将队列根据优先级分为多级队列,如果一个任务在高优先级队列多次继续都任然未完成那么就会降级,如果一个任务在低优先级队列存活了很久那么就会讲他升级;唯一的缺点就是实现复杂 完全公平调度:基于虚拟虚拟时间分配 CPU,确保进程按照优先级公平获取执行时间,底层是红黑树定位进程,是目前 Linux 主流的调度方式

什么是僵尸进程和孤儿进程?

僵尸进程是指,子进程退出了并将退出信息交给了父进程,但是父进程没有进行处理,那么子进程就会占用进程号 PID 和一定的系统资源。这就是所说的僵尸进程; 解决方法:父进行调用 wait ()进行阻塞等待子进行执行完成,然后父进程会自动回收子进程资源

孤儿进程是指,一个进程正在执行,但是创建他的父进程已经执行完成了并且回收了,那么当前进行就会由 PID 为 1 的 init 进行接管,当前进程的父进程 id 改为 1

内存管理

内存管理主要做了什么?

系统的内存管理主要职责就是管理内存如何分配,分配后如何映射,内存使用期间如何扩缩容,使用完成后内存如何回收这些核心问题;

什么是内存碎片?

内存碎片是指没有被充分利用的内存空间,可以分成两种:内部碎片和外部碎片; 内部碎片,比如说一个进程申请了一块 100 字节的空间,但是只是用了 80 字节,那么剩余没有被使用的部分就是内存空间 外部碎片指的是内存块之间零散的空闲区域,如果没有机制主动去调整内存结构那么他们就很难被利用,比如有 10 块碎片空间每个大小都是 5 字节,他们不会自动整理为一块 50 B 的空间;

其实系统层面的内存碎片一般由系统自己优化处理了,离我们最近的内存碎片就是 JVM 层面,在堆空间进行垃圾回收时,采用标记删除的方式就会出现大量的内存碎片,所以 GC 算法在不断的优化在内存碎片、空间、效率三者之间寻找最佳实践的平衡;

常见的内存管理方式有哪些?

常见的内存管理方式有三种

  • 连续分配:操作系统为每个进程分配连续的内存空间,这种方式的内存使用率很低
  • 非连续分配 :非连续分配是将内存分为段、页或者段和页结合这三种方式,然后将这些基本单位分配给进程,这些单位都是逻辑上的,相邻的两页在物理内存上不一定连续;
  • 虚拟内存:虚拟内存上将内存分为虚拟和物理两个层面,进程只需要和虚拟内存打交道,而操作系统则会维护虚拟内存到物理内存的映射关系;

什么是虚拟内存?有什么用?

虚拟内存是为了让进程和物理内存进行解耦的中间层;没有虚拟内存的时候,内存的管理方式可能经常出现内存踩踏的问题,就是一个进程修改到其他线程的内存;而有了虚拟内存后,对于进程来说只需要他就不需要担心会影响到其他进程,因为当前进程的所有内存操作都是在虚拟内存上的,而虚拟内存也会记录将每个进行的虚拟内存到真实物理内存的地址映射; 除了进程隔离的优点外,虚拟内存还提供了比实际内存更大的空间给进程,具体实现可以先从虚拟内存的写入和读取说起,当进程在内存上写入数据时,虚拟内存会先映射,如果发现这个虚拟内存没有对于的物理内存于之映射那么就会发出缺页中断,然后 CPU 就会发出缺页中断,然后从用户态切换到内核态执行真正的物理内存非配然后映射完成后回到虚拟内存;当一个进行在进行内存读取的时候也会先映射再读取; 而之所以虚拟内存可以实现内存扩展的效果,主要是依赖内存分页分段或者是页段结合,然后结合局部性原则加上内存页替换算法实现的;虚拟内存会对物理内存进行分页和分段处理,虚拟内存会根据进程的需要来加载页, 而局部性原则是指程序运行过程中倾向于访问集中的内存区域,这样我们就可以读取较为连续的内存页到内存中,内存页替换算法是内存中的数据超过了容量后会将不常读取的数据存到磁盘中,然后通过读取新的数据到内存,类似于滑动窗口的思想内存中保存的都是最可能读取的热内存不常用的就存放在硬盘中,站在进程的角度就是内存变大了;

聊一下局部性原理

局部性原理是计算机中著名的理论,他有两个维度,时间局部性和空间局部性;

  • 时间局部性:指的是一段指令或者数据会在某个较为集中的时间中被反复执行,比如我们的循环或者递归场景;
  • 空间局部性:指的是一个程序对数据的操作倾向于集中来某个集中连续的空间,所以在虚拟内存加载数据页的时候一般会顺带加载相邻的一些页面来提高效率;

文件系统和IO

什么是零拷贝?

在零拷贝出现之前文件的拷贝流程是: 第一步 java 程序从用户态切换到内核态将硬盘中的文件拷贝到页缓存区中,这一步是通过 DMA 拷贝实现的 第二步就是通过 CPU 拷贝将缓存页中的数据拷贝到用户态中;然后切换回到用户态; 第三步是将用户态再次切换到内核态,并通过 CPU 拷贝将数据从用户态堆内存中拷贝到 socket 缓存中 第四步就是 socket 通过 DMA 拷贝将 socket 缓存中的数据拷贝到输出设备;最后切换回用户态完成整个拷贝过程 过程一共四次用户态到内核态之间的切换,并且对同一份文件过程中拷贝了四次;

alt

零拷贝就是为了降低文件复制开销所诞生的,他的目的就是通过各种机制尽量降低 CPU 拷贝次数和用户态到内核态之间的切换;

mmap + write alt

mmap(memoryMap)+write 的方式是当数据从磁盘拷贝到页缓存中后和用户态的用户缓冲区建立映射关系,这样就能减少两次拷贝,页缓存中的数据可以直接拷贝到 socket 缓存中,减少了一次文件拷贝

sendfile alt

如果发送的数据在拷贝过程中并不需要用户干预数据,那么可以使用 sendfile,直接从用户态向内核态发送一个拷贝指令,然后 MDA 会将数据从磁盘拷贝到页缓存,然后用 CPU 拷贝到 socket 缓存,最后 DMA 拷贝到输出设备;这个过程上下文切换降到了两次,文件拷贝次数是三次;但是在内核态中依然出现了两次文件拷贝

sendfile+DMA Scatter/Gather alt

这种方式在内核态中页减少了拷贝次数;数据从磁盘中由 DMA 拷贝到页缓存中之后直接拷贝给输出设备,同时将文件的信息同步给 soket 缓存向用户态响应;

硬链接和软链接有什么区别?

软链接和 windows 中的快捷方式差不多,软链接记录的只是当前链接到文件的映射关系,他和源文没有关联,就算源文件不存在也可以创建软链接;ln -s fileA linkA 硬链接则和软链接不同,硬链接存储的文件真实的 inode 信息,也就是说硬链接可以直接操作源文件;并且一个文件可能存在多个硬链接;这个可以类比成 java 中对象存在堆空间中,但是栈中可以有多个引用指向他;硬链接不能创建目录也不能跨文件系统,因为每个文件中的 inode 信息都是独立的; ln fineA linkA

什么是 AIO、BIO、 NIO 和 IO 多路复用?

要讲清楚这些概念要先将一下什么是同步异步什么是阻塞和非阻塞;同步和异步是针对被调用方而言的,比如 A 调用 B,如果 B 能立刻执行操作并将结果返回给 A 那么就是同步操作,如果 B 不能保证马上执行操作但是保证会去做并且做完之后的结果会通知 A 来接收,这种方式就是异步,异步操作涉及到了唤醒用户态线程的操作,所以需要操作系统层面的支持和参与; 而 A 调用 B 对于 A 来说他可以选择等待 B 完成后再进行后续操作,这就是阻塞;如果 A 不等待 B 的返回直接进行后续操作那么就是非阻塞的;

  • BIO (Blocking IO):同步阻塞 IO 模型,用户态 A 线程请求资源 B,发出请求后 A 会在原地等待内核态中 B 资源传输完成,用户态 A 线程进行了阻塞,内核态执行操作是同步的,所以是同步阻塞 IO; 下面的代码中就体现了,下次能请求支援时会进行阻塞等待资源加载完成;
  • BIO 只能支持连接数较少的场景,并且并发性能很差;
// 服务端代码(BIO)
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
    Socket clientSocket = serverSocket.accept(); // 阻塞等待连接
    new Thread(() -> {
        // 处理客户端请求(读/写操作均阻塞)
    }).start();
}

-NIO (Non-Blocking IO)同步非阻塞 IO,用户态线程发起请求后直接执行后续操作,而内核态的同步执行 IO 操作,这就是同步非阻塞 IO,用户态程序虽然没有阻塞但是需要通过不停的轮询来检测 IO 的执行情况;NIO 的应用场景不多

  • AIO (Asynchronous IO) 异步非阻塞 IO,用户态发起 IO 请求后直接继续执行后续操作,内核态异步执行 IO 操作,完成之后通过操作系统内核通知用户态线程;这种方式虽然很完美但是支持的版本不多也不是目前最主流的 IO 模型,最主流的是 IO 多路复用模型

IO 多路复用:他是 NIO 模型,不过他将在用户态进行的轮询操作转移到了内核态中,内核态中使用操作系统提供的各种调用函数来实现的轮询;他本质上是通过阻塞一个线程,让其他线程实现类似异步地操作; 目前很多主流的软件都是基于 IO 多路复用实现的,比如 redis、nginx,java 中的 NIO 也是 IO 多路复用的一个具体实现,netty 是对 NIO 的一个具体封装;

如何理解 select、poll、epoll?

select, poll, epoll 都是在 linux 下对 IO 多路复用的实现技术;他们的统一目标就是通过一个或者几个少量的线程进行 IO 监听,当 IO 完成或则异常之后通知对于的用户态线程,是 NIO 模型中的重要实现部分,如果没有他们的话每个线程都只能不断轮询自己的 IO 执行情况;站在服务器的角度看就是通过

select: select 采用的是轮询 +遍历的方式实现的,首先会将客户端和服务器的连接封装到 channel 中,而多个 channel 又会封装到轮询器 Selector 中,底层是通过 set 结构来存储这些 channel fd 信息的,轮询器会遍历 set 查找执行完成的 fd,然后会将所有执行完成的 channel 同时返回给用户态,这个同时返回的过程就是多路复用;select 的缺点是每次轮询的限制是 1024,虽然可以通过修改来让这个上限变大,但是在高并发场景在 FD 数量很大的情况在 select 依然很局限 poll: poll 和 select 基本一致,不同的是底层通过链表的方式来存储 fd,这样就不会有 fd 的限制,但是依然是通过扫描遍历的方式依然局限; epoll:epoll 在处理流程中加入了特有的 epoll_create 方法,方法调用会在内核空间中创建一个红黑树和双向链表,然后调用 epoll_ctl 需要监听的 FD 存放在红黑树中并标记需要监听的事件,然后调用 epoll_wait 去双向链表中查找执行完成的 DF,而红黑数上的 FD 被放到双向链表的过程上由中断来实现的;也就是说 epoll 从传统的轮询扫描的方式转为了事件驱动的方式实现,这样不但减少了用户态到内核态的切换消耗,也提高了降低了轮询器的扫描开销;

IO 多路复用和多线程有什么区别?

IO 多路复用技术是通过一个或者几个少量的线程来管理大量的 IO 连接状态;在没有 IO 多路复用之前的 NIO 模型采用的就是每个线程自己轮询查询自己 IO 状态的方式;这样的方式会大大限制服务器的连接数量,并且在用户态的线程也会处于阻塞状态;

什么是 IO 密集,什么是 CPU 密集?

IO 密集型和 CPU 密集型的描述是这个系统的瓶颈在哪里地方,IO 密集型的意思就是当前系统的瓶颈才 IO,比如主流的互联网应用,抖音、淘宝等系统主要的系统消耗就是网络传输;而 CPU 密集型指的是系统瓶颈在 CPU 计算上面,比如说海量数据的加解密,对象的序列化和反序列化,视频音频的编解码;

什么是 Load(负载)?

负载是在类 unix 系统中的一个重要系统指标;他描述的是在一段时间内,计算机的执行任务量,他由 CPU, 内存和 IO 三方面构成,任意一项负载过高都会让整体负载变大; 查看服务器负载的命令有:

  • uptime:这个命令会返回计算机启动了多长时间,期间由多少账户登录,以及 1,5,15 分钟的平均负载
  • top:top 是 Linux 中最长用的系统性能分析工具,他可以试试查看各个进程资源的运行状态,他一样会返回 1,5,15 分钟的平均负载 load avg
  • w: 也可以查看 load avg

什么是 CPU 利用率?

CPU 利用率指的是在一段时间周期内 CPU 处于运行状态的时长,比如一分钟内 CPU 运行率为 50%那么就说明 30 s 时间 CPU 是处于运行的 30 s 是处于空闲的; 可以查看 CPU 使用率的命令: vmstat n m: 是 n 秒内进行 m 次采样;

什么是Page Cache,他的读写过程是怎么样的?有什么优缺点?

Page Cache 是介于用户态和内核态之间的一个缓存介质,他是内存的一部分;在用户态切换到内核态请求读取数据的时候,会先在页缓存中查看是否存在如果命中则直接使用缓存的数据,如果不存在则从磁盘中将数据读取到页缓存中;在写入时也是一样先在页缓存中对数据进行修改和写入,然后同步到磁盘中;不管是什么系统不管是什么缓存只要设计到了缓存写入那么就一定要面临的是脏页问题,page Cache 解决脏页问题的策略是,当页缓存大小达到一定阈值后进行全部写盘操作;如果系统压力增大时操作系统也可能直接将脏页写入磁盘然后释放内存区域;页缓存的优点就是提高了文件的读写效率,缺点就是占用一定内存空间并且有一定数据丢失的风险;

#java##面试#
全部评论

相关推荐

04-11 19:52
西南大学 后端
查看28道真题和解析
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
3
18
分享

创作者周榜

更多
牛客网
牛客企业服务