面试准备:操作系统常见面试题汇总

1.为什么要有用户态和内核态?内核态和用户态的运作方式?

由于需要限制不同的程序之间的访问能力, 防止他们获取别的程序的内存数据, 或者获取外围设备的数据, 并发送到网络, CPU划分出两个权限等级——用户态和内核态。这两种状态的主要差别在于:进程所能访问的内存空间和资源是否受到限制。

用户态切换到内核态的3种方式:

  1. 系统调用。用户态进程主动要求切换到内核态来申请使用操作系统提供的服务程序完成工作。
  2. 异常。当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常
  3. 外围设备的中断。当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序。

所有用户程序都是运行在用户态的, 但是有时候程序确实需要做一些内核态的事情, 例如从硬盘读取数据, 或者从键盘获取输入等。这时候需要将用户态切换为内核态,这种机制叫系统调用, 在CPU中的实现称之为陷阱指令(Trap Instruction)。系统调用过程如下:

  1. 用户态程序将一些数据放在寄存器或者堆栈(stack frame)里,以此表明需要操作系统提供的服务。
  2. 用户态程序执行陷阱指令,CPU切换到内核态。
  3. 之后,会读取程序放入内存的数据参数, 并执行程序请求的服务。
  4. 系统调用完成后, 操作系统会重置CPU为用户态并返回系统调用的结果。

2.进程间通信方式介绍

进程间通信的概念

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信(IPC,InterProcess Communication)
ref: 进程间通信IPC (InterProcess Communication)

方式:

  • 共享内存
    顾名思义,共享内存就是两个进程同时共享一块内存,然后在这块内存上的数据可以共同修改和读取,达到通信的目的;共享内存是最快的ipc方式;共享内存常与信号量进行配合使用

  • 信号量:
    信号量是一个控制资源访问的标识符 (简单来说就是一个计数器),具有原子性,主要是用来做PV操作,实现进程间同步。

PV操作是一种实现进程互斥与同步的有效方法。PV操作与信号量的处理相关,P表示通过的意思,V表示释放的意思。

  • 无名管道(Pipe):
    无名管道是在具有亲缘关系的进程之间的一种ipc方式,亲缘关系是指fork出的父子进程、兄弟进程这样一个关系,他们创建的时候会同时持有一个无名管道的句柄,句柄存在于内存之中,具有一个读端和写端,进程之间可以通过句柄来进行半双工的通信。

句柄可以简单理解为数据:fd[0],fd[1],也就是两个分别用来读和写的文件描述符。
父子进程:通过fork函数创建的新进程是原进程的子进程。
兄弟进程:同一个进程多次fork产生的子进程之间互为兄弟进程。

  • 命名管道(FIFO):
    命名管道相比于无名管道,虽然也是半双工的通信方式,但是可以在不具有亲缘关系的进程间通信:凡是可以访问到磁盘上对应路径的FIFO文件的进程都可以通信。

FIFO文件是一种特殊设备的文件形式,在文件系统中可以看到。程序中可以查看文件stat结构中st_mode成员的值来判断文件是否是FIFO文件。

  • 消息队列:
    消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息不一定要以先进先出的次序读取,可以实现随机查询。消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。

  • 套接字(Socket):
    套接字是网络编程的api,通过套接字可以不同的机器间的进程进行通信,常用于客户端进程和服务器进程的通信。

线程间通信方式
程通信主要可以分为三种方式,分别为共享内存、消息传递和管道流
线程通信方式

3.Linux查看进程状态、cpu状态、占用端口的进程号的命令?linux top命令VIRT,RES,SHR,DATA的含义?*

查看进程状态ps -ef

查看cpu状态 top
Cpu(s):
0.1%us 用户空间占用CPU百分比
0.0%sy 内核空间占用CPU百分比
0.0%ni 用户进程空间内改变过优先级的进程占用CPU百分比
99.9%id 空闲CPU百分比

查看占用端口的进程号netstat -an|grep or lsof -i: 80
查看内存:free -m


VIRT:virtual memory usage 虚拟内存
1、进程“需要的”虚拟内存大小,包括进程使用的库、代码、数据等
2、假如进程申请100m的内存,但实际只使用了10m,那么它会增长100m,而不是实际的使用量

RES:resident memory usage 常驻内存
1、进程当前使用的内存大小,但不包括swap out
2、包含其他进程的共享
3、如果申请100m的内存,实际使用10m,它只增长10m,与VIRT相反
4、关于库占用内存的情况,它只统计加载的库文件所占内存大小

SHR:shared memory 共享内存
1、除了自身进程的共享内存,也包括其他进程的共享内存
2、虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小
3、计算某个进程所占的物理内存大小公式:RES – SHR
4、swap out后,它将会降下来

DATA
1、数据占用的内存。如果top没有显示,按f键可以显示出来。
2、真正的该程序要求的数据空间,是真正在运行中要使用的。

4.什么是Swap?

从功能上讲,交换分区主要是在内存不够用的时候,将部分内存上的数据交换到swap空间上,以便让系统不会因内存不够用而导致oom或者更致命的情况出现。swap空间是磁盘上的一块区域,可以是一个分区,也可以是一个文件,或者是他们的组合。

在Linux上可以使用swapon -s命令查看当前系统上正在使用的交换空间有哪些。

5.什么是上下文切换?*

对于单核单线程CPU而言,在某一时刻只能执行一条CPU指令。

上下文切换(Context Switch)是一种将CPU资源从一个线程分配给另一个线程的机制。从用户角度看,计算机能够并行运行多个线程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。

6.为什么进程上下文切换比线程上下文切换代价高?

进程切换分两步:

1.切换页目录以使用新的地址空间。(每一个进程拥有自己独立的内存空间,而线程共享进程的内存空间。)

每个进程有自己的页目录和页表,所以每个进程的地址空间映射的物理内存是不一样的。

2.切换内核栈和硬件上下文

内核在创建进程的时候,会为进程创建相应的堆栈。每一个进程都有两个栈,一个用户栈,存在于用户空间;一个内核栈,存在于内核空间。当进程在用户空间运行时,CPU堆栈指针寄存器里面的内容都是用户栈地址,使用用户栈;当进程在内核空间时,CPU堆栈指针寄存器里面的内容是内核栈空间地址,使用内核栈。
当进程因为中断或者系统调用陷入到内核态时,进程所使用的堆栈也要从用户栈转到内核栈。进程陷入到内核态后,先把用户态堆栈的地址保存在内核栈之中,然后设置堆栈指针寄存器的内容为内核栈的地址,这样就完成了用户栈向内核栈的转换当进程从内核态恢复到用户态之后时,在内核态之后的最后将保存在内核栈里面的用户栈的地址恢复到堆栈指针寄存器即可。

尽管每个进程可以拥有属于自己的地址空间,但是所有进程必须共享CPU寄存器,因此,在恢复一个进程的执行前,内核必须确保每个寄存器装入了挂起进程时的值。
进程恢复执行前必须装入寄存器的那一组数据成为硬件上下文。

对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。

备注:

进程切换与线程切换的最主要区别:进程切换涉及到虚拟地址空间的切换而线程切换则不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。把虚拟地址转换为物理地址需要查找页表,页表查找是一个很慢的过程,因此通常使用TLB(Translation Lookaside Buffer)来缓存页地址,用来加速页表查找。当进程切换后页表也要进行切换,页表切换后TLB就失效了,那么虚拟地址转换为物理地址就会变慢,表现出来的就是程序运行会变慢,而线程切换则不会导致TLB失效,因为线程线程无需切换地址空间,因此我们通常说线程切换要比较进程切换块,原因就在这里。
通过页表理解进程切换和线程切换开销上的区别
——

7.进程调度

  • 调度种类

高级调度:(High-Level Scheduling)又称为作业调度,它决定把外存中的后备作业调入内存的就绪队列中等待运行;

低级调度:(Low-Level Scheduling)又称为进程调度,它决定把就绪队列的某进程获得CPU;

中级调度:(Intermediate-Level Scheduling)又称为在虚拟存储器中引入,在内、外存对换区进行进程对换(将内存中暂不运行的进程移到外存避免占用资源,当内存有空闲时调入具备条件的进程至内存中,以提高内存利用率和系统吞吐率)。

  • 非抢占式调度与抢占式调度

非抢占式:分派程序一旦把处理机分配给某进程后便让它一直运行下去,直到进程完成或发生进程调度进程调度某事件而阻塞时,才把处理机分配给另一个进程。

抢占式:操作系统将正在运行的进程强行暂停,由调度程序将CPU分配给其他就绪进程的调度方式。

  • 调度策略的设计

响应时间: 从用户输入到产生反应的时间

周转时间: 从任务开始到任务结束的时间

CPU任务可以分为交互式任务和批处理任务,调度最终的目标是合理的使用CPU,使得交互式任务的响应时间尽可能短,用户不至于感到延迟,同时使得批处理任务的周转时间尽可能短,减少用户等待的时间。

  • 调度算法:

    1. FIFO或First Come, First Served (FCFS)先来先服务
      调度的顺序就是任务到达就绪队列的顺序。
      公平、简单(FIFO队列)、非抢占、不适合交互式。
      未考虑任务特性,平均等待时间可以缩短。

    2. 优先权调度
      每个任务关联一个优先权,调度优先权最高的任务。
      注意:优先权太低的任务一直就绪,得不到运行,出现“饥饿”现象。

    3. Shortest Job First (SJF)
      最短的作业(CPU区间长度最小)最先调度。
      SJF可以保证最小的平均等待时间。

    4. Shortest Remaining Job First (SRJF)
      SJF的可抢占版本,比SJF更有优势。

    5. Round-Robin(RR)时间片轮转
      设置一个时间片,按时间片来轮转调度。
      优点: 定时有响应,等待时间较短;缺点: 上下文切换次数较多;
      时间片太大,响应时间太长;吞吐量变小,周转时间变长;当时间片过长时,退化为FCFS。

    6. 多级队列调度
      按照一定的规则建立多个进程队列
      不同的队列有固定的优先级(高优先级有抢占权)
      不同的队列可以给不同的时间片和采用不同的调度方法
      存在问题1:没法区分I/O bound和CPU bound;
      存在问题2:也存在一定程度的“饥饿”现象;

    7. 多级反馈队列
      在多级队列的基础上,任务可以在队列之间移动,更细致的区分任务。
      可以根据“享用”CPU时间多少来移动队列,阻止“饥饿”。
      最通用的调度算法,多数OS都使用该方法或其变形,如UNIX、Windows等。

8.逻辑地址 Vs 物理地址 Vs 虚拟内存

所谓的逻辑地址,是指计算机用户(例如程序开发者),看到的地址。
例如,当创建一个长度为100的整型数组时,操作系统返回一个逻辑上的连续空间指针指向数组第一个元素的内存地址。由于整型元素的大小为4个字节,故第二个元素的地址时起始地址加4,以此类推。事实上,逻辑地址并不一定是元素存储的真实地址,即数组元素的物理地址(在内存条中所处的位置),并非是连续的,只是操作系统通过地址映射,将逻辑地址映射成连续的,这样更符合人们的直观思维。

虚拟内存 使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),而实际上,它通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换。

9.内部碎片与外部碎片

在内存管理中,内部碎片是已经被分配出去的的内存空间大于请求所需的内存空间。

外部碎片是指还没有分配出去,但是由于大小太小而无法分配给申请空间的新进程的内存空间空闲块。

页式虚拟存储系统存在内部碎片;段式虚拟存储系统,存在外部碎片

为了有效的利用内存,使内存产生更少的碎片,要对内存分页,内存以页为单位来使用,最后一页往往装不满,于是形成了内部碎片。

为了共享要分段,在段的换入换出时形成外部碎片,比如5K的段换出后,有一个4k的段进来放到原来5k的地方,于是形成1k的外部碎片。

10.同步和互斥的区别

同步,是指散步在不同进程之间的若干程序片断,它们的运行必须严格按照规定的某种先后次序来运行,这种先后次序依赖于要完成的特定的任务。如果用对资源的访问来定义的话,同步是指在互斥的基础上(大多数情况),通过其它机制实现访问者对资源的有序访问。在大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。

所谓互斥,是指散布在不同进程之间的若干程序片断,当某个进程运行其中一个程序片段时,其它进程就不能运行它们之中的任一程序片段,只能等到该进程运行完这个程序片段后才可以运行。如果用对资源的访问来定义的话,互斥某一资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的。

11.CPU缓存?

What:
CPU缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小的多但是交换速度却比内存要快得多。
Why:
缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾。当CPU调用大量数据时,就可避开内存直接从缓存中调用,从而加快读取速度。
How:
命中:
缓存的工作原理是当CPU要读取一个数据时,首先从缓存中查找,如果找到就立即读取并送给CPU处理;如果没有找到,就用相对慢的速度从内存中读取并送给CPU处理,同时把这个数据所在的数据块调入缓存中,可以使得以后对整块数据的读取都从缓存中进行,不必再调用内存。
分级:
按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存,二级缓存,部分高端CPU还具有三级缓存,每一级缓存中所储存的全部数据都是下一级缓存的一部分,这三种缓存的技术难度和制造成本是相对递减的,所以其容量也是相对递增的。

12. 为什么要使用多线程呢?

  1. 避免阻塞(异步调用)
    线程在运行之中,比如遇到磁盘IO、RPC等这些比CPU处理慢得多的操作,就需要阻塞、自旋,这一段时间可以用来运行其它线程的任务,提高效率。
  2. 多核处理器
    现在的处理器几乎都是多核的,一个单线程程序在运行时只能使用一个处理器核心。

13. 什么是操作系统OS?

操作系统本质上是在硬件上运行的软件系统,用于管理计算机硬件与软件资源的程序。
它可以的简单的分为两个部分,外壳和内核。外壳负责与用户进行交互,内核负责与硬件进行交互。

14. 什么是系统调用?*

我们运行的程序基本都是运行在用户态,如果我们调用操作系统提供的系统态级别的子功能咋办呢?那就需要系统调用了!

也就是说在我们运行的用户程序中,凡是与系统态级别的资源有关的操作(如文件管理、进程控制、内存管理等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。

这些系统调用按功能大致可分为如下几类:

设备管理。完成设备的请求或释放,以及设备启动等功能。
文件管理。完成文件的读、写、创建及删除等功能。
进程控制。完成进程的创建、撤销、阻塞及唤醒等功能。
进程通信。完成进程之间的消息传递或信号传递等功能。
内存管理。完成内存的分配、回收以及获取作业占用内存区大小及地址等功能。

15. 线程间的同步的方式?

  • 共享内存:
    1. 互斥量(Mutex):采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问。比如 Java 中的 synchronized 关键词和各种 Lock 都是这种机制。
    2. 信号量(Semphares) :它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量
    3. 事件(Event) :Wait/Notify:通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较

互斥量、读写锁、自旋锁、线程信号、条件变量

  • 消息队列:
    1. 管道(Pipe):Java提供管道功能,实现管道通信的类有两组:PipedInputStream和PipedOutputStream或者是PipedReader和PipedWriter。

16. 常见的几种内存管理机制?*

简单分为连续分配管理方式非连续分配管理方式这两种。连续分配管理方式是指为一个用户程序分配一个连续的内存空间,常见的如 块式管理 。同样地,非连续分配管理方式允许一个程序使用的内存分布在离散或者说不相邻的内存中,常见的如页式管理段式管理

  • 块式管理 : 远古时代的计算机操系统的内存管理方式。将内存分为几个固定大小的块,每个块中只包含一个进程。如果程序运行需要内存的话,操作系统就分配给它一块,如果程序运行只需要很小的空间的话,分配的这块内存很大一部分几乎被浪费了。这些在每个块中未被利用的空间,我们称之为碎片。
  • 页式管理 :把主存分为大小相等且固定的一页一页的形式,页较小,相对相比于块式管理的划分力度更大,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址。
  • 段式管理 : 页式管理虽然提高了内存利用率,但是页式管理其中的页实际并无任何实际意义。 段式管理把主存分为一段段的,每一段的空间又要比一页的空间小很多 。但是,最重要的是段是有实际意义的每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址。
  • 段页式管理:段页式管理机制结合了段式管理和页式管理的优点。简单来说段页式管理机制就是把主存先分成若干段,每个段又分成若干页,也就是说 段页式管理机制 中段与段之间以及段的内部的都是离散的。

分页机制和分段机制的共同点和区别?

  • 共同点 :
    1. 分页机制和分段机制都是为了提高内存利用率,较少内存碎片。
    2. 页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
  • 区别 :
    1. 页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
    2. 分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。

17. 简单描述快表和多级页表?

在分页内存管理中,很重要的两点是:

  1. 虚拟地址(逻辑地址)到物理地址的转换要快。
  2. 解决虚拟地址空间大,页表也会很大的问题。

为了提高内存的空间性能,提出了多级页表的概念;但是提到空间性能是以浪费时间性能为基础的,因此为了补充损失的时间性能,提出了快表(即 TLB)的概念。

快表(TLB):
也叫旁路转换缓冲或者页表缓冲,可以把快表理解为一种特殊的高速缓冲存储器(Cache),其中的内容是页表的一部分或者全部内容。作为页表的 Cache,它的作用与页表相似。如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。

多级页表:
引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间,特别是那些根本就不需要的页表就不需要保留在内存中。给进程分配的内存空间很大的话,对应页表也很大。于是就要建立多级页表,把高层页表(相对级别比较高的)放在内存,靠这个高层页表找底层页表,再在底层页表里找到对应的实页号。部分底层页表在内存,另外的一些就被塞在磁盘,被高层页表点名之后,才调入内存

18. CPU 寻址了解吗?

CPU 需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为 内存管理单元(Memory Management Unit, MMU) 的硬件

CPU要寻址的时候,就会将虚拟地址告知给MMU,然后MMU在快表TLB中查询真实的物理地址,如果没有命中,还要去到磁盘中寻找,如下图所示:
在这里插入图片描述

19. 为什么要有虚拟(逻辑)地址空间呢?

  1. 避免多个程序之间的物理地址冲突:主要是因为,如果程序直接写入物理地址的话,就需要使用得到该物理地址,而如果该物理地址已经被占用了,就会发生冲突。所以使用虚拟地址映射到物理地址,可以避免物理地址的冲突。
  2. 屏蔽底层内存访问:用户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者无意)破坏操作系统,造成操作系统崩溃。
  3. 程序可以使用一系列相邻的虚拟地址来访问内存中不相邻的物理地址

20. 局部性原理?

局部性原理是虚拟内存技术的基础,正是因为程序运行具有局部性原理,才可以只装入部分程序到内存就开始运行。

局部性原理的主要内容就是:

  1. 时间局部性 :如果程序中的某条指令一旦执行,不久以后该指令可能再次执行;如果某数据被访问过,不久以后该数据可能再次被访问。产生时间局部性的典型原因,是由于在程序中存在着大量的循环操作。
  2. 空间局部性 :一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也将被访问,即程序在一段时间内所访问的地址,可能集中在一定的范围之内,这是因为指令通常是顺序存放、顺序执行的,数据也一般是以向量、数组、表等形式簇聚存储的。

21.页面置换算法?

What:

进程运行时,如果需要访问的数据不在内存,需要将该页调入内存,若内存空间已满,则要按照一定策略调出页面。

How:

  • OPT 页面置换算法(最佳页面置换算法)

最佳(Optimal, OPT)置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若千页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。一般作为衡量其他置换算法的方法。

  • FIFO(First In First Out) 先进先出页面置换算法 :

总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。

  • LRU (Least Currently Used)最近最久未使用页面置换算法:

LRU算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T,当须淘汰一个页面时,选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。

  • LFU (Least Frequently Used) 最少使用页面置换算法

最少使用页面置换算法 : 该置换算法选择在之前时期使用最少的页面作为淘汰页。

22. 了解一下Linux文件系统*

参考:Linux文件系统概要

23. Unicode,ASCII,UTF-8的区别?*

  • ASCII编码

ASCII是单字节编码,使用7 位二进制数(剩下的1位二进制为0)来表示所有的大写和小写字母,数字0 到9、标点符号, 以及在美式英语中使用的特殊控制字符,其中最后一位用于奇偶校验。

无法用来表示中文(中文编码至少需要2个字节),所以,中国制定了GB2312编码,用来把中文编进去。但世界上有许多不同的语言,所以需要一种统一的编码。

  • Unicode

Unicode把所有语言都统一到一套编码里,这样就不会再有乱码问题了。Unicode 最常用的 是用两个字节表示一个字符(如果要用到非常偏僻的字符,就需要4个字节)。现代操作系统和大多数编程语言都直接支持Unicode。

  • Unicode和ASCII的区别

ASCII编码是1个字节,而Unicode编码通常是2个字节。
字母A用ASCII编码是十进制的65,二进制的01000001;而在Unicode中,只需要在前面补0,即为:00000000 01000001。

新的问题:如果统一成Unicode编码,乱码问题从此消失了。但是,如果你写的文本基本上全部是英文的话,用Unicode编码比ASCII编码需要多一倍的存储空间,在存储和传输上就十分不划算。

  • UTF8

出现了把Unicode编码转化为“可变长编码”的UTF-8编码。
UTF-8编码把一个Unicode字符根据不同的数字大小编码成1-6个字节,常用的英文字母被编码成1个字节,汉字通常是3个字节,只有很生僻的字符才会被编码成4-6个字节。 如果你要传输的文本包含大量英文字符,用UTF-8编码就能节省空间。
从上面的表格还可以发现,UTF-8编码有一个额外的好处,就是ASCII编码实际上可以被看成是UTF-8编码的一部分,所以,大量只支持ASCII编码的历史遗留软件可以在UTF-8编码下继续工作。

24. 原子操作的实现原理?*

CAS、锁升级 等原理解答。

25. 动态链接和静态链接?*

首先大概介绍一下C++编译。
编译分为3步,首先对源文件进行预处理,这个过程主要是处理一些#号定义的命令或语句(如宏、#include、预编译指令#ifdef等),生成*.i文件;然后进行编译,这个过程主要是进行词法分析、语法分析和语义分析等,生成*.s的汇编文件;最后进行汇编,这个过程比较简单,就是将对应的汇编指令翻译成机器指令,生成可重定位的二进制目标文件。

静态链接和动态链接两者最大的区别就在于链接的时机不一样,静态链接是在形成可执行程序前,而动态链接的进行则是在程序执行时。

  • 为什么要进行静态链接
    在我们的实际开发中,不可能将所有代码放在一个源文件中,所以会出现多个源文件,而且多个源文件之间不是独立的,而会存在多种依赖关系,如一个源文件可能要调用另一个源文件中定义的函数,但是每个源文件都是独立编译的,为了满足前面说的依赖关系,则需要将这些源文件产生的目标文件进行链接,从而形成一个可以执行的程序。这个链接的过程就是静态链接。

静态链接的优点就是,在可执行程序中已经具备了所有执行程序所需要的任何东西,在执行的时候运行速度快。
静态链接的缺点很明显,一是浪费空间,因为每个可执行程序中对所有需要的目标文件都要有一份副本,所以如果多个程序对同一个目标文件都有依赖,同一个目标文件都在内存存在多个副本。一方面就是更新比较困难,因为每当库函数的代码修改了,这个时候就需要重新进行编译链接形成可执行程序。

  • 为什么会出现动态链接
    动态链接出现的原因就是为了解决静态链接中提到的两个问题,一方面是空间浪费,另外一方面是更新困难。

动态链接的基本思想是把程序按照模块拆分成各个相对独立部分,在程序运行时才将它们链接在一起形成一个完整的程序,而不是像静态链接一样把所有程序模块都链接成一个单独的可执行文件。JVM的虚拟机运行时数据区中,虚拟机栈的栈帧中含动态链接,每一个栈帧都包含指向运行时常量池中该栈帧所属方法的引用,持有这个引用是为了支持方法调用过程中的动态连接。

在这里插入图片描述

26. Free命令*

[root@application ~]# free
              total        used        free      shared  buff/cache   available
Mem:        3733780     1402580      132948      130244     2198252     1919164
Swap:             0           0           0
  • 第一列

Mem 内存的使用信息
Swap 交换空间的使用信息


  • 第一行

total:系统从加电开始到引导完成,BIOS等要保留一些内存,内核要保留一些内存,最后剩下可供系统支配的内存就是total。这个值在系统运行期间一般是固定不变的。
used: 已被使用的物理内存大小。
free: 还有多少物理内存可用。
available:应用程序可用内存数。系统中有些内存虽然已被使用但是可以回收的,比如cache/buffer、slab都有一部分可以回收,所以free不能代表全部可用的内存,这部分可回收的内存加上free才是系统可用的内存,即:available free+buff+cache。

available与free的关键区别点在于,free是说的系统层面,available是说的应用程序层面

shared: 被共享使用的物理内存大小。
buff/cache: 被 buffer 和 cache 使用的物理内存大小。

buffer与cache的区别
举例说明,每秒要写100次硬盘,对系统冲击很大,浪费了大量时间在忙着处理开始写和结束写这两件事嘛。用buffer暂存起来,变成每10秒写一次硬盘,对系统的冲击就很小,写入效率高了;
cache 是为了弥补高速设备和低速设备的鸿沟而引入的中间层,最终起到加快取速度的作用。比如你一个很复杂的计算做完了,下次还要用结果,就把结果放手边一个好拿的地方存着,下次不用再算了,加快了数据取用的速度。

27. HugePage是什么,有什么作用?*

“大内存页”有助于 Linux 系统进行虚拟内存管理。顾名思义,除了标准的 4KB 大小的页面外,它们还能帮助管理内存中的巨大的页面。使用“大内存页”,你最大可以定义 1GB 的页面大小。

在系统启动期间,你能用“大内存页”为应用程序预留一部分内存。这部分内存,即被“大内存页”占用的这些存储器永远不会被交换出内存。它会一直保留其中,除非你修改了配置。这会极大地提高像 Oracle 数据库这样的需要海量内存的应用程序的性能。

在虚拟内存管理中,内核维护一个将虚拟内存地址映射到物理地址的表,对于每个页面操作,内核都需要加载相关的映射。如果你的内存页很小,那么你需要加载的页就会很多,导致内核会加载更多的映射表。而这会降低性能

使用“大内存页”,意味着所需要的页变少了。从而大大减少由内核加载的映射表的数量。这提高了内核级别的性能最终有利于应用程序的性能。

28. 内存管理算法Buddy和Slab?*

参考:Linux内核内存管理算法Buddy和Slab

29. 匿名映射是什么?有什么作用?*

要考虑匿名映射和有名映射,需要先知道:

内存是外存的缓存,而linux一切外存都用 “文件 fd” 作为驱动程序的读写操作的抽象模型。

文件时Linux操作的基础,而如果使用匿名映射,表示不存在fd这么个真实的文件,匿名映射只要在 flags 参数中增加 MAP_ANONYMOUS,即为匿名映射。此时,会忽略参数 fd,映射区不与任何文件关联,而且映射区域无法和其他进程共享(但是父子进程能够共享)。

  • 匿名映射(anonymous mapping)– 其实就是进程的堆栈等普通内存,它对应的是直接从物理内存分配内存然后建立的映射,没有文件或设备作为后备存储,这种映射的内存区域的内容会被初始化为0。由于匿名映射没有关联的后备存储,因此回收匿名内存时只能把它们的存储到磁盘交换区,这也是区分匿名内存的一个重要标志。

  • 文件映射:映射和实际文件相关联,通常是把文件的内容映射到进程地址空间,这样应用程序就可以像操作进程地址空间一样读写文件。

  • 有名映射(memory mapped) – 页表要映射的不是物理内存,而是由某个文件或设备提供的,比如页被映射到一个文件或MMIO内存。此种情况下,需要驱动程序去决定如何建立映射,提供vm_operations_struct结构,即实现vma->vm_ops函数集合。一般情况,这些VMA区域都是用户空间调用mmap()函数以MAP_SHARED映射某个文件时建立的。

mmap是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现了文件磁盘地址和进程虚拟地址的映射关系。实现映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。

匿名内存映射适用于具有亲属关系的进程之间;由于父子进程之间的这种特殊的父子关系,在父进程中先调用mmap(),然后调用fork(),那么,在调用fork() 之后,子进程继承了父进程的所有资源,当然也包括匿名映射后的地址空间和mmap()返回的地址,这样父子进程就可以通过映射区域进行通信了

30. Linux 相关命令

磁盘管理

  • ls (list files)

-l:以长格式显示目录下的内容列表。输出的信息从左到右依次包括文件名,文件类型、权限模式、硬连接数、所有者、组、文件大小和文件的最后修改时间等ls -l输出格式

  • cd (change directory):切换当前工作目录
  • du (disk usage):展示磁盘使用量的统计信息
#计算当前文件夹的总磁盘占用量, -s选项表示计算总和, -h选项表示以恰当的K/M/G单位展示
du -sh .
51M
  • df (disk free):显示目前在 Linux 系统上的文件系统磁盘使用情况统计
df -h
Filesystem      Size   Used  Avail Capacity iused      ifree %iused  Mounted on
/dev/disk1s5   233Gi   11Gi   57Gi    16%  488440 2447612880    0%   /
devfs          349Ki  349Ki    0Bi   100%    1208          0  100%   /dev
/dev/disk1s1   233Gi  161Gi   57Gi    75% 2467129 2445634191    0%   /System/Volumes/Data
/dev/disk1s4   233Gi  4.0Gi   57Gi     7%       5 2448101315    0%   /private/var/vm
map auto_home    0Bi    0Bi    0Bi   100%       0          0  100%   /System/Volumes/Data/home
/dev/disk1s3   233Gi  1.0Gi   57Gi     2%      67 2448101253    0%   /Volumes/Recovery
  • export: 查看与设置Shell环境变量

文档编辑

  • grep:用于查找文件里符合条件的字符串
    单个文件中字符串出现位置:
    查看单个文件中字符串出现
    输出单个文件中字符串出现次数:
[xx@xx] grep Error laravel-2021-07-26.log -c
24
  • ps
#列出所有进程 or ps -a
ps -ef 
  • top 【见3】

31. 死锁的条件?

面试准备-Java(二)
-75

32. NIO,BIO

面试准备-Java(二)
-50

33. 哈希算法的作用?哈希碰撞解决方案?

  1. 校验
    比如 计算md5,做视频完整性的匹配。
  2. 加密
    如果password加密。
  3. 唯一标识
    比如说, 现在有十万个文件, 给你一个文件, 要你在这十万个文件中查找是否存在。
  4. 哈希表
  5. 负载均衡
    路由。

  1. 开放地址法
    线性探测,向后查找。
  2. 再Hash
    再做一遍哈希。
  3. 链地址法
    比如Java HashMap的实现方案。
  4. 一致性Hash

34 什么是协程?

协程,英文Coroutines,是一种比线程更加轻量级的存在。正如一个进程可以拥有多个线程一样,一个线程也可以拥有多个协程。
最重要的是,协程不是被操作系统内核所管理,而完全是由程序所控制(也就是在用户态执行)。
这样带来的好处就是性能得到了很大的提升,不会像线程切换那样消耗资源。
ref: 漫画:什么是协程?

  • 2
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值