Skip to content

Latest commit

 

History

History
171 lines (134 loc) · 15 KB

计算机基础.md

File metadata and controls

171 lines (134 loc) · 15 KB

计算机基础

Linux 启动流程

介绍地址

  1. 加载内核:首先读入 /boot 目录下的内核文件
  2. 启动初始化进程:内核文件加载以后,就开始运行第一个程序 /sbin/init,它的作用是初始化系统环境
  3. 确定运行级别
  4. 加载开机启动程序
  5. 用户登录
  6. 进入 login shell
  7. 打开 non-login shell

Linux 常用命令

find、grep、ps、cp、move、tar、head、tail、netstat、lsof、tree、wget、curl、ping、ssh、echo、free、top

文件句柄 vs 文件描述符

  • 文件句柄也称为文件指针(FILE *)
    • C 语言中使用文件指针做为 I/O 的句柄。文件指针指向进程用户区中的一个被称为 FILE 结构的数据结构。FILE 结构包括一个缓冲区和一个文件描述符。
  • 文件描述符
    • 对于 linux 而言,所有对设备和文件的操作都使用文件描述符来进行的。文件描述符是一个非负的整数,它是一个索引值,指向内核中每个进程打开文件的记录表。当打开一个现存文件或创建一个新文件时,内核就向进程返回一个文件描述符;当需要读写文件时,也需要把文件描述符作为参数传递给相应的函数
    • 文件描述符是文件描述符表的一个索引,因此从某种意义上说文件指针就是句柄的句柄(在 Windows 系统上,文件描述符被称作文件句柄)

Linux CPU 内核态和用户态

  • java 的线程是映射到操作系统原生线程之上的,如果要阻塞或唤醒一个线程就需要操作系统介入,需要在户态与内核态之间切换,这种切换会消耗大量的系统资源,因为用户态与内核态都有各自专用的内存空间,专用的寄存器等,用户态切换至内核态需要传递给许多变量、参数给内核,内核也需要保护好用户态在切换时的一些寄存器值、变量等,以便内核态调用结束后切换回用户态继续工作
  • 当用户程序向内核发起系统调用时,CPU 将用户进程从用户态切换到内核态;当系统调用返回时,CPU 将用户进程从内核态切换回用户态
  • 内核态:CPU 可以访问内存所有数据,包括外围设备,例如硬盘、网卡,CPU 也可以将自己从一个程序切换到另一个程序
  • 用户态:只能受限的访问内存,且不允许访问外围设备,占用 CPU 的能力被剥夺,CPU 资源可以被其他程序获取

ASCII 码

ASCII 码对照表

ASCII ((American Standard Code for Information Interchange): 美国信息交换标准代码)是基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言

ASCII 码对照表地址:https://tool.oschina.net/commons?type=4

ASCII 码大写字母对照表:

65=A	66=B	67=C	68=D	69=E	70=F	71=G
72=H	73=I	74=J	75=K	76=L	77=M	78=N
79=O	80=P	81=Q	82=R	83=S	84=T	85=U
86=V	87=W	88=X	89=Y	90=Z

ASCII 码小写字母对照表:

a=97	b=98	c=99	d=100	e=101	f=102	g=103
h=104	i=105	j=106	k=107	l=108	m=109	n=110
o=111	p=112	q=113	r=114	s=115	t=116	u=117
v=118	w=119	x=120	y=121	z=122

bit、byte、word

  • bit (位) bit 电脑记忆体中最小的单位,在二进位电脑系统中,每一 bit 可以代表 0 或 1 * 的数位讯号。所以它能表示的数字范围就是 0 ~ 1(2^1 个数字取值范围)
  • byte (字节) 一个 byte 由 8 bit 组成,所以理论上一个 byte 能表示的数据范围是 0 ~ 255(2^6 个数字取值范围)
  • word (字) 一个 word 由 2 byte 组成,所以理论上一个 word 能表示的数据范围是 0 ~ 65535(2^6 个数字取值范围)

CPU32 位和 64 位的区别

  1. 运算速度不同。64 位 CPU GPRs(General-Purpose Registers,通用寄存器)的数据宽度为 64 位,64 位指令集可以运行 64 位数据指令,也就是说处理器一次可提取 64 位数据(只要两个指令,一次提取 8 个字节的数据),比 32 位(需要四个指令,一次提取 4 个字节的数据)提高了一倍,理论上性能会相应提升 1 倍
  2. 寻址能力不同。64 位处理器的优势还体现在系统对内存的控制上。由于地址使用的是特殊的整数,因此一个 ALU(算术逻辑运算器)和寄存器可以处理更大的整数,也就是更大的地址。比如,Windows Vista x64 Edition 支持多达 128 GB 的内存和多达 16 TB 的虚拟内存,而 32 位 CPU 和操作系统最大只可支持 4G 内存

进制

二进制、八进制、十进制、十六进制简介

  • 二进制:由 0,1 组成,运算规律是逢二进一,计算机只能识别二进制表示的数据
  • 八进制:由 0、1、2、3、4、5、6、7 组成,运算规律是逢八进一
  • 十进制:由 0,1,2、3、4、5、6、7、8、9 组成,运算规律是逢十进一
  • 十六进制:由数字 0 ~ 9 以及字母 A,B,C,D,E,F 组成,运算规律是逢十六进一,A,B,C,D,E,F 代表 10-15

二进制、八进制、十进制、十六进相互转换

  • 十进制转二进制(以 13 为例):13%2=1,6%2=0,3%2=1,1%2=1,反向取余得到 1101,即 13 的二进制为 1101
  • 十进制转八进制(以 13 为例):13%8=5,1%8=1,反向取余得到 15,即 13 的八进制为 15
  • 十六进制转十进制(以 47 为例):2F=2*(16^1) + 15*(16^0)=32+15=47
  • 二进制转十进制(以 13 为例):1101=1*(2^3)+1*(2^2)+0*(2^1)+1*(2^0)=8+4+0+1=13
  • 八进制转十进制(以 13 为例):15=1*(8^1)+5*(8^0)=8+5=13
  • 十进制转十六进制(以 47 为例):47%16=15(F),2%16=2,反向取余得到 2F,即 47 的十六进制为 2F
  • 二进制和八进制及十六进制之间转换:先转换为十进制,再转换为二进制或者八进制或者十六进制

原码、反码、补码

  • 符号位:数据的正号“+”或负号“-”,在计算机里就用一位二进制的 0 或 1 来区别,通常放在最高位,成为符号位,最高位为 0 表示正数,为 1 表示负数
  • 原码:正数是其二进制本身;负数是符号位为 1,数值部分取 X 绝对值的二进制
  • 反码:正数的反码和原码相同;负数是符号位为 1,其它位是原码取反
  • 补码:正数的补码和原码,反码相同;负数是符号位为 1,其它位是原码取反,未位加 1。(或者说负数的补码是其绝对值反码未位加 1)
  • 计算机使用的是补码

案例:

  • 5 的原码:0000 0000 0000 0101
  • -3 的反码:1111 1111 1111 1100
  • -3 的补码: 1111 1111 1111 1101
  • -3 的原码:1000 0000 0000 0011

Linux 的 IO 模型

  • 介绍地址
  • 阻塞 IO 模型
    • 所有的文件操作都是阻塞。在用户进程进行系统调用,进程会阻塞一直到应用进程收到数据或出错才会返回
  • 非阻塞 IO 模型
    • 通过在进行读写操作前,先检查读写是否准备就绪来减少读写的阻塞。这种模式需要不停的轮询操作系统,看操作是否就绪,频繁的轮询对效率上也会带来较大损耗
  • IO 复用模型
    • 由于系统调用是一个较重的操作,我们期望每次系统调用能帮我们检查更多的文件准备就绪的事件,这个模式就是把多个要检查的文件及事件通过系统调用一次性传递给操作系统进行检查,如果有任何一个文件的事件准备就绪就会返回,否则阻塞当前进程直到超时或有文件的事件达到就绪状态
  • 信号驱动 IO 模型
    • 此模式的核心是预先通过系统调用设置信号的信号处理程序。而后在设备驱动程序中,如果某种事件准备就绪,就产生一个信号,并把信号放到进程的内存结构中,当进程返回用户态时会执行这个信号对应的信号处理程序
  • 异步 IO 模型
    • 信号驱动 IO 模型,在被操作系统回调时,还需要通过 read 系统调用获取数据,而异步 IO 模型会在回调前就把数据从内核空间 copy 到了用户空间,回调函数可以直接处理数据,不需要再进行系统调用

IO 复用模型原理

  • 介绍地址
  • NIO 中的非阻塞实现,其实是实现了 IO 模型中的第三种模型 IO 复用模型
  • 操作系统实现 IO 复用模型,一般通过 3 个系统调用 poll,select 和 epoll,其中 poll 和 select 实现相似,epoll 实现略有差别
  • epoll
    • Epoll 是 linux2.6 内核的一个新的系统调用,Epoll 在设计之初,就是为了替代 select,Epoll 线性复杂度的模型,epoll 的时间复杂度为 O(1), 也就意味着,Epoll 在高并发场景,随着文件描述符的增长,有良好的可扩展性
    • epoll 高效的本质在于:
      • 减少了用户态和内核态的文件句柄拷贝
      • 减少了对可读可写文件句柄的遍历
      • mmap 加速了内核与用户空间的信息传递,epoll 是通过内核与用户 mmap 同一块内存,避免了无谓的内存拷贝
      • IO 性能不会随着监听的文件描述的数量增长而下降
      • 使用红黑树存储 fd,以及对应的回调函数,其插入,查找,删除的性能不错,相比于 hash,不必预先分配很多的空间

Linux 零拷贝(Zero-copy)

  • 零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间
  • 它的作用是在数据包从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现 CPU 的零参与,彻底消除 CPU 在这方面的负载
    • 零拷贝机制可以减少数据在内核缓冲区和用户进程缓冲区之间反复的 I/O 拷贝操作
    • 零拷贝机制可以减少用户进程地址空间和内核地址空间之间因为上下文切换而带来的 CPU 开销
  • 实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术
    • DMA 的全称叫直接内存存取(Direct Memory Access),是一种允许外围设备(硬件子系统)直接访问系统主内存的机制
  • 零拷贝方式
    • 用户态直接 I/O:应用程序可以直接访问硬件存储,操作系统内核只是辅助数据传输。这种方式依旧存在用户空间和内核空间的上下文切换,硬件上的数据直接拷贝至了用户空间,不经过内核空间。因此,直接 I/O 不存在内核空间缓冲区和用户空间缓冲区之间的数据拷贝
    • 减少数据拷贝次数:在数据传输过程中,避免数据在用户空间缓冲区和系统内核空间缓冲区之间的 CPU 拷贝,以及数据在系统内核空间内的 CPU 拷贝,这也是当前主流零拷贝技术的实现思路
      • mmap + write:使用 mmap + write 代替原来的 read + write 方式,减少了 1 次 CPU 拷贝操作。mmap 是 Linux 提供的一种内存映射文件方法,即将一个进程的地址空间中的一段虚拟地址映射到磁盘文件地址(RocketMQ 的方式),适用于小块文件传输,频繁调用时,效率很高
      • sendfile:Linux 内核版本 2.1 中引入,通过 sendfile 系统调用,数据可以直接在内核空间内部进行 I/O 传输,从而省去了数据在用户空间和内核空间之间的来回拷贝(Kafka 的方式),可以利用 DMA 方式,消耗 CPU 较少,大块文件传输效率高
      • sendfile + DMA gather copy:Linux 2.4 版本的内核对 sendfile 系统调用进行修改,为 DMA 拷贝引入了 gather 操作。它将内核空间(kernel space)的读缓冲区(read buffer)中对应的数据描述信息(内存地址、地址偏移量)记录到相应的网络缓冲区( socket buffer)中,由 DMA 根据内存地址、地址偏移量将数据批量地从读缓冲区(read buffer)拷贝到网卡设备中,这样就省去了内核空间中仅剩的 1 次 CPU 拷贝操作
      • splice:Linux 在 2.6.17 版本引入 splice 系统调用,不仅不需要硬件支持,还实现了两个文件描述符之间的数据零拷贝,splice 系统调用可以在内核空间的读缓冲区(read buffer)和网络缓冲区(socket buffer)之间建立管道(pipeline),从而避免了两者之间的 CPU 拷贝操作
    • 写时复制技术:写时复制指的是当多个进程共享同一块数据时,如果其中一个进程需要对这份数据进行修改,那么将其拷贝到自己的进程地址空间中,如果只是数据读取操作则不需要进行拷贝操作
  • Java NIO 零拷贝实现
    • 在 Java NIO 中的通道(Channel)就相当于操作系统的内核空间(kernel space)的缓冲区,而缓冲区(Buffer)对应的相当于操作系统的用户空间(user space)中的用户缓冲区(user buffer)
    • 通道(Channel)是全双工的(双向传输),它既可能是读缓冲区(read buffer),也可能是网络缓冲区(socket buffer)
    • 缓冲区(Buffer)分为堆内存(HeapBuffer)和堆外内存(DirectBuffer),这是通过 malloc() 分配出来的用户态内存。堆外内存(DirectBuffer)在使用后需要应用程序手动回收,而堆内存(HeapBuffer)的数据在 GC 时可能会被自动回收
    • 在使用 HeapBuffer 读写数据时,为了避免缓冲区数据因为 GC 而丢失,NIO 会先把 HeapBuffer 内部的数据拷贝到一个临时的 DirectBuffer 中的本地内存(native memory),这个拷贝涉及到 sun.misc.Unsafe.copyMemory() 的调用,背后的实现原理与 memcpy() 类似。 最后,将临时生成的 DirectBuffer 内部的数据的内存地址传给 I/O 调用函数,这样就避免了再去访问 Java 对象处理 I/O 读写