理解 OS 嘛,能说说什么是操作系统?
- 操作系统(Operating System,简称 OS)是治理计算机硬件与软件资源的程序,是计算机的基石。
- 操作系统实质上是一个运行在计算机上的软件程序,用于治理计算机硬件和软件资源。举例:运行在你电脑上的所有应用程序都通过操作系统来调用零碎内存以及磁盘等等硬件。
- 操作系统存在屏蔽了硬件层的复杂性。操作系统就像是硬件应用的负责人,兼顾着各种相干事项。
- 操作系统的内核(Kernel)是操作系统的外围局部,它负责零碎的内存治理,硬件设施的治理,文件系统的治理以及应用程序的治理。
大抵说说 Kernel 的作用
- 操作系统的内核(Kernel)是操作系统的外围局部,它负责零碎的内存治理,硬件设施的治理,文件系统的治理以及应用程序的治理。
- 操作系统的内核是连贯应用程序和硬件的桥梁,决定着操作系统的性能和稳定性。
简略聊聊 CPU
- CPU 是一台计算机的运算外围(Core)+ 管制外围(Control Unit),能够称得上是计算机的大脑。
- CPU 次要包含两个局部:控制器 + 运算器。
- CPU 的根本任务就是执行指令,对计算机来说最终都是一串由“0”和“1”组成的序列。
说说用户态和内核态,还有咱们的零碎调用
哈哈,这个小六六有被问到过哎
首先就是 不论是内核态还是用户态都是基于咱们的过程的
依据过程拜访资源的特点,咱们能够把过程在零碎上的运行分为两个级别:
- 用户态(user mode) : 用户态运行的过程或能够间接读取用户程序的数据。
- 零碎态(kernel mode): 能够简略的了解零碎态运行的过程或程序简直能够拜访计算机的任何资源,不受限制。
咱们运行的程序根本都是运行在用户态,如果咱们调用操作系统提供的零碎态级别的子性能咋办呢?那就须要零碎调用了!
也就是说在咱们运行的用户程序中,但凡与零碎态级别的资源无关的操作(如文件治理、过程管制、内存治理等),都必须通过零碎调用形式向操作系统提出服务申请,并由操作系统代为实现。
这些零碎调用按性能大抵可分为如下几类:
- 设施治理:实现设施的申请或开释,以及设施启动等性能。
- 文件治理:实现文件的读、写、创立及删除等性能。
- 过程管制:实现过程的创立、撤销、阻塞及唤醒等性能。
- 过程通信:实现过程之间的消息传递或信号传递等性能。
- 内存治理:实现内存的调配、回收以及获取作业占用内存区大小及地址等性能。
咱们晓得 Java 线程切换,你晓得线程切换的过程中,操作系统有做的什么操作。
咱们都晓得,Linux 是一个多任务操作系统,它反对远大于 CPU 数量的工作同时运行。当然,这些工作实际上并不是真的在同时运行,而是因为零碎在很短的工夫内,将 CPU 轮流调配给它们,造成多任务同时运行的错觉。
而在每个工作运行前,CPU 都须要晓得工作从哪里加载、又从哪里开始运行,也就是说,须要零碎当时帮它设置好 CPU 寄存器和程序计数器
CPU 寄存器和程序计数器就是 CPU 上下文,因为它们都是 CPU 在运行任何工作前,必须的依赖环境。
- CPU 寄存器是 CPU 内置的容量小、但速度极快的内存。
- 程序计数器则是用来存储 CPU 正在执行的指令地位、或者行将执行的下一条指令地位。
聊聊 Linux 文件系统
在 Linux 操作系统中,所有被操作系统治理的资源,例如网络接口卡、磁盘驱动器、打印机、输入输出设施、一般文件或是目录都被看作是一个文件。也就是说在 Linux 零碎中有一个重要的概念:一切都是文件。
说说 inode
inode 是 linux/unix 文件系统的根底。那么,inode 是什么? 有什么作用呢?
硬盘的最小存储单位是扇区 (Sector),块(block) 由多个扇区组成。文件数据存储在块中。块的最常见的大小是 4kb,约为 8 个间断的扇区组成(每个扇区存储 512 字节)。一个文件可能会占用多个 block,然而一个块只能寄存一个文件。
尽管,咱们将文件存储在了块 (block) 中,然而咱们还须要一个空间来存储文件的 元信息 metadata:如某个文件被分成几块、每一块在的地址、文件拥有者,创立工夫,权限,大小等。这种 存储文件元信息的区域就叫 inode,译为索引节点:i(index)+node。每个文件都有一个 inode,存储文件的元信息。
能够应用 stat 命令能够查看文件的 inode 信息。每个 inode 都有一个号码,Linux/Unix 操作系统不应用文件名来辨别文件,而是应用 inode 号码辨别不同的文件。
简略来说:inode 就是用来保护某个文件被分成几块、每一块在的地址、文件拥有者,创立工夫,权限,大小等信息。
简略总结一下:
- inode:记录文件的属性信息,能够应用 stat 命令查看 inode 信息。
- block:理论文件的内容,如果一个文件大于一个块时候,那么将占用多个 block,然而一个块只能寄存一个文件。(因为数据是由 inode 指向的,如果有两个文件的数据寄存在同一个块中,就会乱套了)
过程有哪几种状态?
- 创立状态(new):过程正在被创立,尚未到就绪状态。
- 就绪状态 (ready):过程已处于筹备运行状态,即过程取得了除了处理器之外的所有所需资源,一旦失去处理器资源(处理器调配的工夫片) 即可运行。
- 运行状态(running):过程正在处理器上上运行(单核 CPU 下任意时刻只有一个过程处于运行状态)。
- 阻塞状态(waiting):又称为期待状态,过程正在期待某一事件而暂停运行如期待某资源为可用或期待 IO 操作实现。即便处理器闲暇,该过程也不能运行。
- 完结状态(terminated):过程正在从零碎中隐没。可能是过程失常完结或其余起因中断退出运行。
过程间的通信形式有哪些,Java 采取的什么形式
- 管道 / 匿名管道(Pipes):用于具备亲缘关系的父子过程间或者兄弟过程之间的通信。
- 有名管道 (Names Pipes) : 匿名管道因为没有名字,只能用于亲缘关系的过程间通信。为了克服这个毛病,提出了有名管道。有名管道严格遵循 先进先出(first in first out)。有名管道以磁盘文件的形式存在,能够实现本机任意两个过程通信。
- 信号(Signal):信号是一种比较复杂的通信形式,用于告诉接管过程某个事件曾经产生;
- 音讯队列 (Message Queuing):音讯队列是音讯的链表, 具备特定的格局, 寄存在内存中并由音讯队列标识符标识。管道和音讯队列的通信数据都是先进先出的准则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于理论的磁盘介质或者文件系统)不同的是音讯队列寄存在内核中,只有在内核重启(即,操作系统重启) 或者显示地删除一个音讯队列时,该音讯队列才会被真正的删除。音讯队列能够实现音讯的随机查问, 音讯不肯定要以先进先出的秩序读取, 也能够按音讯的类型读取. 比 FIFO 更有劣势。音讯队列克服了信号承载信息量少,管道只能承载无格局字 节流以及缓冲区大小受限等缺。
- 信号量(Semaphores):信号量是一个计数器,用于多过程对共享数据的拜访,信号量的用意在于过程间同步。这种通信形式次要用于解决与同步相干的问题并防止竞争条件。
- 共享内存(Shared memory):使得多个过程能够拜访同一块内存空间,不同过程能够及时看到对方过程中对共享内存中数据的更新。这种形式须要依附某种同步操作,如互斥锁和信号量等。能够说这是最有用的过程间通信形式。
- 套接字(Sockets) : 此办法次要用于在客户端和服务器之间通过网络进行通信。套接字是反对 TCP/IP 的网络通信的基本操作单元,能够看做是不同主机之间的过程进行双向通信的端点,简略的说就是通信的两方的一种约定,用套接字中的相干函数来实现通信过程。
CPU 寻址理解吗? 为什么须要虚拟地址空间?
古代处理器应用的是一种称为 虚构寻址(Virtual Addressing) 的寻址形式。应用虚构寻址,CPU 须要将虚拟地址翻译成物理地址,这样能力拜访到实在的物理内存。实际上实现虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为 内存治理单元(Memory Management Unit, MMU)的硬件
如果间接把物理地址裸露进去的话会带来重大问题,比方可能对操作系统造成挫伤以及给同时运行多个程序造成艰难。
通过虚拟地址拜访内存有以下劣势:
- 程序能够应用一系列相邻的虚拟地址来拜访物理内存中不相邻的大内存缓冲区。
- 程序能够应用一系列虚拟地址来拜访大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保留到磁盘文件。数据或代码页会依据须要在物理内存与磁盘之间挪动。
- 不同过程应用的虚拟地址彼此隔离。一个过程中的代码无奈更改正在由另一过程或操作系统应用的物理内存。
聊聊 unix IO 模型
在 linux 中,对于一次 IO 拜访(以 read 举例),数据会先被拷贝到操作系统内核的缓冲区中,而后才会从操作系统内核的缓冲区拷贝到应用程序的地址空间。所以说,当一个 read 操作产生时,它会经验两个阶段:
- 期待数据准备就绪 (Waiting for the data to be ready)
- 将数据从内核拷贝到过程中 (Copying the data from the kernel to the process)
正式因为这两个阶段,linux 零碎产生了上面五种网络模式的计划:
- 阻塞式 IO 模型(blocking IO model)
- 非阻塞式 IO 模型(noblocking IO model)
- IO 复用式 IO 模型(IO multiplexing model)
- 信号驱动式 IO 模型(signal-driven IO model)
- 异步 IO 式 IO 模型(asynchronous IO model)
上面咱们来别离谈一下这些 IO 模型
阻塞式 IO 模型(blocking IO model)
在 linux 中,默认状况下所有的 IO 操作都是 blocking,一个典型的读操作流程大略是这样:
当用户过程调用了 recvfrom 这个零碎调用,kernel 就开始了 IO 的第一个阶段:筹备数据(对于网络 IO 来说,很多时候数据在一开始还没有达到。比方,还没有收到一个残缺的 UDP 包。这个时候 kernel 就要期待足够的数据到来),而数据被拷贝到操作系统内核的缓冲区中是须要一个过程的,这个过程须要期待。而在用户过程这边,整个过程会被阻塞(当然,是过程本人抉择的阻塞)。当 kernel 始终等到数据筹备好了,它就会将数据从 kernel 中拷贝到用户空间的缓冲区当前,而后 kernel 返回后果,用户过程才解除 block 的状态,从新运行起来。
所以:blocking IO 的特点就是在 IO 执行的下两个阶段的时候都被 block 了。
- 期待数据准备就绪 (Waiting for the data to be ready)「阻塞」
- 将数据从内核拷贝到过程中 (Copying the data from the kernel to the process)「阻塞」
非阻塞 I/O(nonblocking IO)
linux 下,能够通过设置 socket 使其变为 non-blocking。通过 java 能够这么操作:
InetAddress host = InetAddress.getByName("localhost");
Selector selector = Selector.open();
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.bind(new InetSocketAddress(hos1234));
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
复制代码
socket 设置为 NONBLOCK(非阻塞)就是通知内核,当所申请的 I / O 操作无奈实现时,不要将过程睡眠,而是返回一个错误码(EWOULDBLOCK),这样申请就不会阻塞。
当用户过程调用了 recvfrom 这个零碎调用,如果 kernel 中的数据还没有筹备好,那么它并不会 block 用户过程,而是立即返回一个 EWOULDBLOCK error。从用户过程角度讲,它发动一个 read 操作后,并不需要期待,而是马上就失去了一个后果。用户过程判断后果是一个 EWOULDBLOCK error 时,它就晓得数据还没有筹备好,于是它能够再次发送 read 操作。一旦 kernel 中的数据筹备好了,并且又再次收到了用户过程的 system call,那么它马上就将数据拷贝到了用户空间缓冲区,而后返回。
能够看到,I/O 操作函数将一直的测试数据是否曾经筹备好,如果没有筹备好,持续轮询,直到数据筹备好为止。整个 I/O 申请的过程中,尽管用户线程每次发动 I/O 申请后能够立刻返回,然而为了等到数据,仍须要一直地轮询、反复申请,耗费了大量的 CPU 的资源。
所以,non blocking IO 的特点是用户过程须要一直的被动询问 kernel 数据好了没有:
- 期待数据准备就绪 (Waiting for the data to be ready)「非阻塞」
- 将数据从内核拷贝到过程中 (Copying the data from the kernel to the process)「阻塞」
个别很少间接应用这种模型,而是在其余 I/O 模型中应用非阻塞 I/O 这一个性。这种形式对单个 I/O 申请意义不大, 但给 I/O 多路复用铺平了路线.
I/O 多路复用(IO multiplexing)
IO multiplexing 就是咱们常说的 select,poll,epoll,有些中央也称这种 IO 形式为 event driven IO。select/epoll 的益处就在于单个 process 就能够同时解决多个网络连接的 IO。它的基本原理就是 select,poll,epoll 这些个 function 会一直的轮询所负责的所有 socket,当某个 socket 有数据达到了,就告诉用户过程。
当用户过程调用了 select,那么整个过程会被 block,而同时,kernel 会“监督”所有 select 负责的 socket,当任何一个 socket 中的数据筹备好了,select 就会返回。这个时候用户过程再调用 read 操作,将数据从 kernel 拷贝到用户过程。
所以,I/O 多路复用的特点是通过一种机制一个过程能同时期待多个文件描述符,而这些文件描述符(套接字描述符)其中的任意一个进入读就绪状态,select()函数就能够返回。
这个图和 blocking IO 的图其实并没有太大的不同,事实上因为 IO 多路复用多了增加监督 socket,以及调用 select 函数的额定操作,效率更差。还更差一些。因为这里须要应用两个 system call (select 和 recvfrom),而 blocking IO 只调用了一个 system call (recvfrom)。然而,然而,应用 select 当前最大的劣势是用户能够在一个线程内同时解决多个 socket 的 I/O 申请。用户能够注册多个 socket,而后一直地调用 select 读取被激活的 socket,即可达到在同一个线程内同时解决多个 I/O 申请的目标。而在同步阻塞模型中,必须通过多线程的形式能力达到这个目标。
所以,如果解决的连接数不是很高的话,应用 select/epoll 的 web server 不肯定比应用 multi-threading + blocking IO 的 web server 性能更好,可能提早还更大。select/epoll 的劣势并不是对于单个连贯能解决得更快,而是在于能解决更多的连贯。)
在 IO multiplexing Model 中,理论中,对于每一个 socket,个别都设置成为 non-blocking,然而,如上图所示,整个用户的 process 其实是始终被 block 的。只不过 process 是被 select 这个函数 block,而不是被 socket IO 给 block。
因而对于 IO 多路复用模型来说:- 期待数据准备就绪 (Waiting for the data to be ready)「阻塞」
- 将数据从内核拷贝到过程中 (Copying the data from the kernel to the process)「阻塞」
异步 I/O(asynchronous IO)
接下来咱们看看 linux 下的 asynchronous IO 的流程:
用户过程发动 aio_read 调用之后,立即就能够开始去做其它的事。而另一方面,从 kernel 的角度,当它发现一个 asynchronous read 之后,首先它会立即返回,所以不会对用户过程产生任何 block。而后,kernel 会期待数据筹备实现,而后将数据拷贝到用户内存,当这所有都实现之后,kernel 会给用户过程发送一个 signal,通知它 read 操作实现了。
异步 I/O 模型应用了 Proactor 设计模式实现了这一机制。
因而对异步 IO 模型来说:
- 期待数据准备就绪 (Waiting for the data to be ready)「非阻塞」
- 将数据从内核拷贝到过程中 (Copying the data from the kernel to the process)「非阻塞」
信号驱动式 IO 模型(signal-driven IO model)
首先咱们容许 socket 进行信号驱动 I/O, 并装置一个信号处理函数,过程持续运行并不阻塞。当数据筹备好时,过程会收到一个 SIGIO 信号,能够在信号处理函数中调用 I/O 操作函数解决数据。
然而这种 IO 模确用的不多,所以我这里也就不具体提它了。
select、poll 和 epoll 之间的区别
select==> 工夫复杂度 O(n) 它仅仅晓得了,有 I/O 事件产生了,却并不知道是哪那几个流(可能有一个,多个,甚至全副),咱们只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以 select 具备 O(n)的无差别轮询复杂度,同时解决的流越多,无差别轮询工夫就越长。
poll==> 工夫复杂度 O(n) poll 实质上和 select 没有区别,它将用户传入的数组拷贝到内核空间,而后查问每个 fd 对应的设施状态,然而它没有最大连接数的限度,起因是它是基于链表来存储的.
epoll==> 工夫复杂度 O(1) epoll 能够了解为 event poll,不同于忙轮询和无差别轮询,epoll 会把哪个流产生了怎么的 I/O 事件告诉咱们。所以咱们说 epoll 实际上是事件驱动(每个事件关联上 fd)的,此时咱们对这些流的操作都是有意义的。(复杂度升高到了 O(1))select,poll,epoll 都是 IO 多路复用的机制。I/O 多路复用就通过一种机制,能够监督多个描述符,一旦某个描述符就绪 个别是读就绪或者写就绪),可能告诉程序进行相应的读写操作。但 select,poll,epoll 实质上都是同步 I/O,因为他们都须要 在读写事件就绪后本人负责进行读写,也就是说这个读写过程是阻塞的,而异步 I/O 则无需本人负责进行读写,异步 I/O 的实现会负责把数据从内核拷贝到用户空间。
结尾
参考:《2020 最新 Java 根底精讲视频教程和学习路线!》
链接:https://juejin.cn/post/694268…