计算机之八股文

C++

1. 预处理和宏的使用

预处理:#ifdef, #else, #endif

宏定义的使用:

#define SQR(x) (x*x)
int a, b = 3;
a = SQR(b + 2);
printf("a = %d\n", a); // a = b + 2 * b + 2 = 11

#define SQR(x) ((x)*(x))
int a, b = 3;
a = SQR(b + 2);
printf("a = %d\n", a); // a = (b + 2) * (b + 2) = 25
2. 动态分配内存
  • 使用 new 动态分配内存,使用 delete 释放动态申请的内存

    类型名 *指针变量 = new 类型名;

    类型名 *指针变量 = new 类型名[元素个数];

    delete 指针; // 一片空间不能被delete多次

    delete []指针; // 释放动态分配的数组

    eg:int *p = new int[n];

    ​       delete []p;

    ​       p = NULL; // 防止 p 成为野指针

  • 使用 malloc 和 free 动态申请内存

    void malloc(unsigned int size);

    eg:short *p = (short*)malloc(len * sizeof(short));

    ​        free(p);

3. const 关键字

1)定义常量

2)定义常量指针 const int *p = &n;

  • 不可以通过常量指针修改其指向的内容
  • 不能把常量指针赋值给非常量指针,反过来可以
  • 函数参数为常量指针时,可避免函数内部不小心改变参数指针所指地方的内容

3)定义常引用

  • 不能通过常引用修改其引用的变量

const 成员函数的基本定义格式:

  1. 类内定义时

    类型 函数名(参数列表) const {函数体}

  2. 类外定义时

    类内声明:类型 函数名(参数列表) const;

    类外定义:类型 类名:: 函数名(参数列表) const {函数体}

4. 覆盖和重载之间有什么区别?
  • 覆盖是指派生类中重新定义的函数,其函数名、参数列表、返回类型与父类完全相同,只是函数体存在区别;覆盖只发生在类的成员函数中;
  • 重载是指两个函数具有相同的函数名,不同的参数列表,不关心返回值;当调用函数时,根据传递的参数列表来判断调用哪个函数;重载可以是类的成员函数,也可以是普通函数。
5. 请你来说一下C++中struct和class的区别

在C++中,class和struct做类型定义如下区别:

  • 默认继承权限不同,class继承默认是private继承,而struct默认是public继承
  • class还可用于定义模板参数,像typename,但是关键字struct不能用于定义模板参数

C++保留struct关键字,主要有如下原因:

  • 保证与C语言的向下兼容性,C++必须提供一个struct

  • C++中的struct定义必须百分百地保证与C语言中的struct的向下兼容性,把C++中的最基本的对象单元规定为class而不是struct,就是为了避免各种兼容性要求的限制

  • 对struct定义的扩展使C语言的代码能够更容易的被移植到C++中

6. 纯虚函数有什么作用?如何实现?

定义纯虚函数是为了实现一个接口,起到规范的作用,想要继承这个类就必须覆盖该函数。

实现方式是在虚函数声明的结尾加上= 0即可。

7. 虚函数表是针对类的还是针对对象的?同一个类的两个对象的虚函数表是怎么维护的?

虚函数表是针对类的,类的所有对象共享这个类的虚函数表,因为每个对象内部都保存一个指向该类虚函数表的指针vptr,每个对象的vptr的存放地址都不同,但都指向同一虚函数表。

8. 为什么基类的构造函数不能定义为虚函数?

虚函数的调用依赖于虚函数表,而指向虚函数表的指针vptr需要在构造函数中进行初始化,所以无法调用定义为虚函数的构造函数。

9. 为什么基类的析构函数需要定义为虚函数?

为了实现动态绑定,基类指针指向派生类对象,如果析构函数不是虚函数,那么在对象销毁时,就会调用基类的析构函数,只能销毁派生类对象中的部分数据,所以必须将析构函数定义为虚函数,从而在对象销毁时,调用派生类的析构函数,从而销毁派生类对象中的所有数据。

10. 对虚函数和多态的理解

多态的实现主要分为静态多态和动态多态,静态多态主要是重载,在编译的时候就已经确定;动态多态是用虚函数机制实现的,在运行期间动态绑定。举个例子:一个父类类型的指针指向一个子类对象时候,使用父类的指针去调用子类中重写了的父类中的虚函数的时候,会调用子类重写过后的函数,在父类中声明为加了virtual关键字的函数,在子类中重写时候不需要加virtual也是虚函数。

虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

每个包含了虚函数的类都包含一个虚表,虚函数表保存的是虚函数的指针,因此虚表的大小是虚函数个数*4个字节

对于虚表,需要注意的是:

  1. 虚表是一个指针数组,其元素是虚函数的指针,每个元素对应一个虚函数的函数指针。需要指出的是,普通的函数即非虚函数,其调用并不需要经过虚表,所以虚表的元素并不包括普通函数的函数指针。
  2. 虚表内的条目,即虚函数指针的赋值发生在编译器的编译阶段,也就是说在代码的编译阶段,虚表就可以构造出来了。
  3. 虚表是属于类的,而不是属于某个具体的对象,一个类只需要一个虚表即可。同一个类的所有对象都使用同一个虚表。

虚函数表和虚函数指针存放在哪个位置?

  • 虚函数指针一般都是在类的最前边(取决于编译器的实现)。
  • 虚函数表位于只读数据段(.rodata),也就是C++内存模型中的常量区
  • 虚函数位于代码段(.text),也就是C++内存模型中的代码区

虚函数的实现:在有虚函数的类中,类的最开始部分是一个虚函数表的指针,这个指针指向一个虚函数表,表中放了虚函数的地址,实际的虚函数在代码段(.text)中。当子类继承了父类的时候也会继承其虚函数表,当子类重写父类中虚函数时候,会将其继承到的虚函数表中的地址替换为重新写的函数地址。使用了虚函数,会增加访问内存开销,降低效率。

11. 多态的实现有哪几种?

多态分为静态多态和动态多态。其中,静态多态是通过重载和模板技术实现的,在编译期间确定;动态多态是通过虚函数和继承关系实现的,执行动态绑定,在运行期间确定。

12. 动态绑定是如何实现的?

当编译器发现类中有虚函数时,会创建一张虚函数表,把虚函数的函数入口地址放到虚函数表中,并且在对象中增加一个指针vptr,用于指向类的虚函数表。当派生类覆盖基类的虚函数时,会将虚函数表中对应的指针进行替换,从而调用派生类中覆盖后的虚函数,从而实现动态绑定(动态联编)。

13. 动态多态有什么作用?有哪些必要条件?

动态多态的作用:

  • 隐藏实现细节,使代码模块化,提高代码的可复用性;
  • 接口重用,使派生类的功能可以被基类的指针/引用所调用,即向后兼容,提高代码的可扩充性和可维护性。

动态多态的必要条件:

  • 需要有继承;
  • 需要有虚函数覆盖;
  • 需要有基类指针/引用指向子类对象
14. 内联函数

函数调用是有时间开销的。如果函数本身只有几条语句,执行非常快,如果函数被反复执行很多次,相比之下调用函数所产生的这个开销就会显得比较大。

为了减少函数调用的开销,引入了内联函数机制。编译器处理对内联函数的调用语句时,是将整个函数的代码插入到调用语句处,而不会产生调用函数的语句。

  • inline函数,对函数的每⼀个调用都用函数本体替代,调用不承受额外开销,编译器对其执行语境相关最优化。增加目标码大小,额外的换页行为,降低缓存击中率,效率损失。
  • 对虚函数进行inline无意义,虚函数是运行时确定,inline是在编译期替换。
  • 编译器⼀般不对“通过函数指针进行调用”提供inline,是否inline取决于调用的方式。
15. static

1、全局静态变量

在全局变量前加上关键字 static ,全局变量就定义成一个全局静态变量。

静态存储区,在整个程序运行期间一直存在。

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:全局静态变量在声明它的文件之外是不可见的,准确地说是从定义之处开始,到文件结尾。

2、局部静态变量

在局部变量之前加上关键字 static ,局部变量就成为一个局部静态变量。

内存中的位置:静态存储区

初始化:未经初始化的全局静态变量会被自动初始化为0(自动对象的值是任意的,除非他被显式初始化)。

作用域:作用域仍为局部作用域,当定义它的函数或者语句块结束的时候,作用域结束。但是当局部静态变量离开作用域后,并没有销毁,而是仍然驻留在内存当中,只不过我们不能再对它进行访问,直到该函数再次被调用,并且值不变。

3、静态函数

在函数返回类型前加 static ,函数就定义为静态函数。函数的定义和声明在默认情况下都是 extern 的,但静态函数只是在声明他的文件当中可见,不能被其他文件所用。

函数的实现使用 static 修饰,那么这个函数只可在本 cpp 内使用,不会同其他 cpp 中的同名函数引起冲突。

warning:不要在头文件中声明 static 的全局函数,不要在 cpp 内声明非 static 的全局函教,如果你要在多个 cpp 中复用该函数,就把它的声明提到头文件里去,否则 cpp 内部声明需加上 static 修饰。

4、类的静态成员

在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。对多个对象来说,静态数据成员只存储一处,供所有对象共用。

5、类的静态函数

静态成员函数和静态数据成员一样,它们都属于类的静态成员,它们都不是对象成员。因此,对静态成员的引用不需要用对象名。

在静态成员函数的实现中不能直接引用类中说明的非静态成员,可以引用类中说明的静态成员(这点非常重要)。如果静态成员函数中要引用非静态成员时,可通过对象来引用。从中可看出,调用静态成员函数使用如下格式:<类名>::< 静态成员函数名>(<参数表>)

作用:实现多个对象之间的数据共享 + 隐藏,并且使用静态成员还不会破坏隐藏原则;默认初始化为0

普通成员变量每个对象有各自的一份,而静态成员变量一共就一份,为所有对象共享。

普通成员函数必须具体作用于某个对象,而静态成员函数并不具体作用于某个对象。

静态成员变量本质上是全局变量,哪怕一个都不存在,类的静态成员变量也存在。

静态成员函数本质上是全局函数。

设置静态成员这种机制的目的是将和某些类紧密相关的全局变量和函数写到类里面,看上去像一个整体,易于维护和理解。

sizeof 运算符不会计算静态成员变量。

16. 四种cast转换

cast出现的意义

  • C++继承并扩展C语言的传统类型转换方式,提供了功能更加强大的转型机制(检查与风险)
  • 更好地定位转型的地方(ctrl+F cast)

C++中四种类型转换是:static_cast, dynamic_cast, const_cast, reinterpret_cast

1、reinterpret_cast

  • reinterpret_cast是四种强制转换中功能最为强大的(最暴力,最底层,最不安全)。它的本质是编译器的指令。
  • 作用:可以把一个指针转换成一个整数,也可以把一个整数转换成一个指针。或者不同类型的指针的相互替换

2、const_cast

  • 有两个功能,去掉const和加上const

3、static_cast

  • 作用:1.基本类型之间的转换;2.void指针转换为任意基本类型的指针;3.用于有继承关系的子类与父类之间的指针或引用的转换

4、dynamic_cast

用于动态类型转换。只能用于含有虚函数的类,用于类层次间的向上和向下转化。只能转指针或引用。向下转化时,如果是非法的对于指针返回NULL,对于引用抛异常。要深入了解内部转换的原理。

  • 向上转换:指的是子类向基类的转换
  • 向下转换:指的是基类向子类的转换

它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。

杂谈

override:覆盖(重写)

overload:重载

int argc,char*argv[ ]:argv[]为保存命令行参数的字符串指针,其中第0个参数是程序的全名,以后的参数为命令行后面跟的用户输入的参数,argv参数是字符串指针数组,其各元素值为命令行中各字符串(参数均按字符串处理)的首地址。 指针数组的长度即为参数个数argc。数组元素初值由系统自动赋予。

操作系统

1. 什么是操作系统?

操作系统就是负责管理协调计算机硬件与软件资源工作的一种系统软件。计算机安装操作系统,主要就是为了帮助我们屏蔽硬件层的复杂性,给上层的应用软件或用户提供易用的服务。

2. 线程和进程?

举个简单例子,我们电脑打开了QQ、word,其实就是开了不同的几个进程;而我用QQ时,可以一边发消息、一边视频聊天,这就是在进程下开了多个线程处理任务。

进程组成:进程总的来说包括程序段 + 数据段 + PCB三部分。

  • 程序段就是程序的代码;

  • 数据段就是程序运行时产生的数据(比如全局变量、局部变量等);

  • PCB中包含操作系统对其进行管理的各种信息(如进程标识符PID,进程当前的状态,进程优先级)。

进程和线程区别

  • 1)调度方面:进程是系统进行资源分配的基本单位,而线程则是任务调度和执行的基本单位;
  • 2)内存方面:进程拥有独立的地址空间与资源,同个进程下的线程共享进程的地址空间与资源;
  • 3)开销方面:线程间切换开销小,进程间切换开销大(线程共享进程资源,拥有独立的栈与程序计数器,进程切换要切换上下文环境);
  • 4)并发性:一个进程内多个线程可以并发(最好和CPU核数相等);多个进程可以并发
  • 5)进程间不会相互影响,线程间一个线程挂掉将导致整个进程挂掉。当然还有很多其他区别,比如通信、切换、健壮性等。

进程切换开销比较大:线程切换不需要更换页表,而进程切换需要。页表切换就会导致缓存失效,性能就会变低。进程切换上下文环境比线程上下文环境大,保存现场,切换,到恢复现场又更耗时。

3. 假如电脑内存只有一个G,为什么还能运行几个G的游戏?

这得益于操作系统的虚拟内存技术。虚拟内存使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间),使得进程逻辑上有很大的连续内存地址空间。实际上一部分对应物理内存上的块,还有一部分没加载进内存的对应在外部磁盘,在需要时进行数据交换。这样就可以让程序拥有超过系统物理内存大小的可用的内存空间。

4. 操作系统是怎样进行内存管理的?

它有很多的管理方式,比如块式管理、页式管理、段式管理、段页式管理。简单来说,块式就是将内存分成一个个块,每个块包含一个进程。同理,段式就是将内存分成多个段,按段存储……。

5. 页面置换算法有哪些?

当访问一个内存中不存在的页,并且内存已满,则需要从内存中调出一个页或将数据送至磁盘对换区,替换一个页,这种现象叫做缺页置换。

(1)先进先出置换算法FIFO:置换最早进入内存的页面

(2)最佳置换算法: 置换最不可能再次被使用的页面,无法实现

(3)最近最久未使用置换算法:置换最近一段时间以来最长时间未访问过的页面

(4)时钟置换算法:

(5)改进型时钟置换算法

6. 进程的调度算法有哪些?

常用的调度算法有:先来先服务调度算法、时间片轮转调度法、短作业优先调度算法、最短剩余时间优先、高响应比优先调度算法、优先级调度算法等等。

7. 页表是什么?

可以理解成一个映射,存储页面与页框的对应关系,通过页表对应逻辑地址与物理地址。

8. 进程间的通信方式

管道

  1. 它是半双工的,具有固定的读端和写端;

  2. 它只能用于父子进程或者兄弟进程之间的进程的通信;

  3. 它可以看成是一种特殊的文件,对于它的读写也可以使用普通的 read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。

命名管道

  1. FIFO 可以在无关的进程之间交换数据,与无名管道不同;
  2. FIFO 有路径名与之相关联,它以一种特殊设备文件形式存在于文件系统中。

消息队列

  1. 消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符 ID 来标识;
  2. 消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级;
  3. 消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除;
  4. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取。

共享内存

  1. 共享内存(Shared Memory),指两个或多个进程共享一个给定的存储区;
  2. 共享内存是最快的一种 IPC,因为进程是直接对内存进行存取。

信号量

  1. 信号量(semaphore)是一个计数器。用于实现进程间的互斥与同步,而不是用于存储进程间通信数据;
  2. 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存;
  3. 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作;
  4. 每次对信号量的 PV 操作不仅限于对信号量值加 1 或减 1,而且可以加减任意正整数;
  5. 支持信号量组。

Socket

上面我们说的管道、消息队列、共享内存、信号量,他们都是多个进程在一台主机之间的通信,那两个相隔几千里的进程能够进行通信吗?

答是必须的,这个时候 Socket 这家伙就派上用场了,例如我们平时通过浏览器发起一个 http 请求,然后服务器给你返回对应的数据,这种就是采用 Socket 的通信方式了。

9. 线程间通信的方式

(1)临界区:

通过多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问

(2)互斥量:

采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被对多个线程同时访问

(3)信号量:

为控制具有有限数量的用户资源而设计的,它允许多个线程在同一时刻去访问同一个资源,但一般需要限制同一时刻访问此资源的最大线程数目

(4)事件(信号):

通过通知操作的方式来保持多线程同步,还可以方便地实现多线程优先级的比较操作

10. 协程
11. 死锁检测与避免

死锁是指多个进程循环等待他方占有的资源而无限期地僵持下去的局面。计算机系统产生死锁的根本原因就是资源有限且操作不当。

(1)产生死锁的原因

  • 竞争资源引起的死锁

  • 由于进程推进顺序不合适引发的死锁

(2)产生死锁的四个必要条件是

  • 互斥条件:进程访问的是临界资源,即在一段时间内某资源只有一个进程占用。如果此时还有其他进程请求该资源,则请求者只能等待,直至占有该资源的进程用完释放

  • 请求和保持条件:一个进程在请求新的资源时,保持对已分配资源的占有

  • 不可剥夺条件:指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放

  • 环路等待条件:指在发生死锁时,必然存在一个进程–资源的环形链

如果在计算机系统中同时具备这四个必要条件时,那么会发生死锁。

(3)处理死锁的基本方法

一般地,解决死锁的方法分为死锁的预防、避免、检测与恢复三种。

  • 预防死锁:是一种较简单和直观的事先预防方法。该方法是通过设置某些限制条件,去破坏产生死锁的四个必要条件的一个或几个,来预防发生死锁。预防死锁是一种较易实现的方法,已被广泛使用。但由于所施加的限制条件往往太严格,可能会导致系统资源利用率和系统吞吐量降低。

  • 避免死锁:该方法同样是属于事先预防的策略,这种方法不是预先加上各种限制条件以预防产生死锁的可能性,而是用某种方法去防止系统进入不安全状态,使死锁不致于最终发生。这种方法只须事先加以较弱的限制条件,便可获得较高的资源利用率及系统吞吐量,但在实现上有一定的难度。目前在较完善的系统中,常用此方法来避免发生死锁。

  • 检测死锁:这种方法并不须事先采取任何限制性措施,也不必检查系统是否已经进入不安全区。此方法允许系统在运行过程中发生死锁,但可通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源;然后采取适当的措施,从系统中将已发生的死锁清除掉。

  • 解除死锁:是与死锁检测相配套的一种措施。当检测到系统中已发生死锁时,须将进程从死锁状态中解脱出来。

    常用的实施方法是撤销或挂起一些进程,以便回收一些资源,再将这些资源分配给已处于阻塞状态的进程,使之转为就绪状态,以继续运行

    死锁的检测与解除措施,有可能使系统获得较好的资源利用率和吞吐量,但在实现上难度也最大。

12. 并发经典的问题(哲学家就餐问题)
  1. 必须同时拿起左右两根筷子
  2. 只有在两个邻居都没有进餐的情况下才允许进餐
13. 内存为什么分段分页
14. 内存交换中,被换出的进程保存在哪里?

保存在磁盘中,也就是外存中。具有对换功能的操作系统中,通常把磁盘分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换区的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出的速度,因此通常对换区采连续分配方式。总之,对换区的I/O速度比文件区的更快。

15. 用户态和内核态

用户态和内核态是操作系统的两种运行级别。两者最大的区别就是特权级不同。用户态拥有最低的特权级,内核态拥有较高的特权级。运行在用户态的程序不能直接访问操作系统内核数据结构和程序。内核态和用户态之间的转换方式主要包括:系统调用,异常和中断。

内核态:处于内核态的 CPU 可以访问任意的数据,包括外围设备,比如网卡、硬盘等,处于内核态的 CPU 可以从一个程序切换到另一个程序,并且占有 CPU 不会发生抢占情况,一般处于特权级 0 的状态称之为内核态

用户态:处于用户态的 CPU 只能受限地访问内存,并且不允许访问外围设备,用户态下的 CPU 不允许独占,也就是说 CPU 能够被其他程序获取。

1、系统调用:这是用户进程主动要求切换到内核态的一种方式,用户进程通过系统调用申请操作系统提供的服务程序完成工作。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如 Linux 的 ine 80h 中断。

2、异常:当 CPU 在执行运行在用户态的程序时,发现了某些事件不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就到了内核态,比如缺页异常。

3、外围设备的中断:当外围设备完成用户请求的操作之后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条将要执行的指令,转而去执行中断信号的处理程序,如果先执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

一般用户态 -> 内核态的转换我们都称之为 trap 进内核,也被称之为 陷阱指令(trap instruction)

16. 计算机有哪些中断?

(1)外部中断:

  • 可屏蔽中断:通过INTR线向CPU请求的中断,主要来自外部设备如硬盘,打印机,网卡等。此类中断并不会影响系统运行,可随时处理,甚至不处理,所以名为可屏蔽中断。

  • 不可屏蔽中断:通过NMI线向CPU请求的中断,如电源掉电,硬件线路故障等。这里不可屏蔽的意思不是不可以屏蔽,不建议屏蔽,而是问题太大,屏蔽不了,不能屏蔽的意思。

    注:INTR和NMI都是CPU的引脚

(2)内部中断:

  • 陷阱:是一种有意的,预先安排的异常事件,一般是在编写程序时故意设下的陷阱指令,而后执行到陷阱指令后,CPU将会调用特定程序进行相应的处理,处理结束后返回到陷阱指令的下一条指令。如系统调用,程序调试功能等。

  • 故障:故障是在引起故障的指令被执行,但还没有执行结束时,CPU检测到的一类的意外事件。出错时交由故障处理程序处理,如果能处理修正这个错误,就将控制返回到引起故障的指令即CPU重新执这条指令。如果不能处理就报错。

    常见的故障为缺页,当CPU引用的虚拟地址对应的物理页不存在时就会发生故障。缺页异常是能够修正的,有着专门的缺页处理程序,它会将缺失的物理页从磁盘中重新调进主存。而后再次执行引起故障的指令时便能够顺利执行了。

  • 终止:执行指令的过程中发生了致命错误,不可修复,程序无法继续运行,只能终止,通常是一些硬件的错误。终止处理程序不将控制返回给原程序,而是直接终止原程序

https://mp.weixin.qq.com/s/c9kfirfsdD6pWuiizC5E6g

17. 虚拟内存的好处与代价?

虚拟内存的好处:

  1. 扩大地址空间
  2. 内存保护:每个进程运行在各自的虚拟内存地址空间,互相不能干扰对方。虚拟内存还对特定的内存地址提供写保护,可以防止代码被数据恶意篡改
  3. 公平内存分配:采用了虚存之后,每个进程都相当于有同样大小的虚存空间
  4. 当进程通信时,可采用虚存共享的方式实现
  5. 当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存
  6. 虚拟内存很适合在多道程序设计系统中使用,许多程度的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把 CPU 交给另一个进程使用。在内存中可以保留多个进程,系统并发度提高
  7. 在程序需要分配连续内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片

虚拟内存的代价:

  1. 虚存的管理需要建立很多数据结构,这些数据结构主要占用额外的内存
  2. 虚拟地址到物理地址的转换,增加了指令的执行时间
  3. 页面的换入换出需要磁盘 I/O,这是很耗时的
  4. 如果一页中只有一部分数据,会浪费内存
18. 分页与分段的区别?
  1. 段是信息的逻辑单位,它是根据用户的需要划分的,因此段对用户是可见的;页是信息的物理单位,是为了管理主存的方便而划分的,对用户是透明的

  2. 段的大小不固定,由它所完成的功能决定;页的大小固定,由系统决定

  3. 段向用户提供二维地址空间;页向用户提供的是一维地址空间

  4. 段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制

    分段共享:在实现程序和数据的共享时,常常以信息的逻辑单位为基础,而分页系统中的每一页只是存放信息的物理单位,其本身没有完整的意义,因而不便于实现信息的共享,而段却是信息的逻辑单位,有利于信息的共享。分段保护:信息保护是对相对完整意义的逻辑单位(段)进行保护

19. TTL

IP包中的Time To Live,生存周期。在网络中,由于某些路径出现故障,而导致数据包在其中几个路由器中形成环路循环转发,永远无法发送结束。这就需要进行干预,即规定了一个途径的最大路由器数量(TTL),如果转发途径路由器大于这个数量则将此数据包直接丢弃。

20. 外中断和异常有什么区别?

外中断是指由 CPU 执行指令以外的事件引起,如 I/O 完成中断,表示设备输入/输出处理已经完成,处理器能够发送下一个输入/输出请求。此外还有时钟中断、控制台中断等。

而异常是由 CPU 执行指令的内部事件引起,如非法操作码、地址越界、算术溢出等。

21. 一个程序从开始运行到结束的完整过程,你能说出来多少?

(1)预编译 主要处理源代码文件中的以“#”开头的预编译指令。

(2)编译 把预编译之后生成的xxx.i或xxx.ii文件,进行一系列词法分析、语法分析、语义分析及优化后,生成相应的汇编代码文件。

(3)汇编

将汇编代码转变成机器可以执行的指令(机器码文件)。经汇编之后,产生目标文件(与可执行文件格式几乎一样)xxx.o(Linux下)、xxx.obj(Windows下)。

(4)链接

将不同的源文件产生的目标文件进行链接,从而形成一个可以执行的程序。

22. 守护进程、僵尸进程和孤儿进程

守护进程:指在后台运行的,没有控制终端与之相连的进程。它独立于控制终端,周期性地执行某种任务。Linux的大多数服务器就是用守护进程的方式实现的,如web服务器进程http等

孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。(注:任何一个进程都必须有父进程)

僵尸进程:如果子进程先退出,父进程还没退出,那么子进程必须等到父进程捕获到了子进程的退出状态才真正结束,否则这个时候子进程就成为僵尸进程。

23. 内存交换中,被换出的进程保存在哪里?

保存在磁盘中,也就是外存中。具有对换功能的操作系统中,通常把磁盘空间分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的小部分,被换出的进程数据就存放在对换区。由于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式。总之,对换区的I/O速度比文件区的更快。

24. 抖动你知道是什么吗?它也叫颠簸现象

刚刚换出的页面马上又要换入内存,刚刚换入的页面马上又要换出外存,这种频繁的页面调度行为称为抖动,或颠簸。产生抖动的主要原因是进程频繁访问的页面数目高于可用的物理块数(分配给进程的物理块不够)

为进程分配的物理块太少,会使进程发生抖动现象。为进程分配的物理块太多,又会降低系统整体的并发度,降低某些资源的利用率 为了研究为应该为每个进程分配多少个物理块,Denning 提出了进程工作集” 的概念

杂谈

(句柄)文件描述符->指针->地址

CPU 寄存器和程序计数是 CPU 在运行任何任务前,所必须依赖的环境,这些环境就叫做 CPU 上下文

计算机网络

1. 三次握手与四次挥手

过程

在这里插入图片描述

ESTABLISHED:表示连接已经建立。三次握手完成。

FIN_WAIT_2:主动关闭链接的一方,发出FIN收到ACK以后进入该状态。称之为半连接或半关闭状态。该状态下的socket只能接收数据,不能发。

TIME_WAIT:表示收到了对方的FIN报文,并发送出了ACK报文,等2MSL后即可回到CLOSED可用状态。等待2MSL(Maximum Segment Life)的时间,确保最后一个ACK(主动关闭连接端发送的)能顺利到达对端。

MSL:Maximum Segment Life

TCP为什么是三次握手四次挥手?

TCP是一个双工协议,为了让双方都保证发送和接收能力正常,建立连接的时候,连接双方都需要向对方发送 SYC(同步请求)和 ACK(响应)。握手阶段双方都没有繁琐的工作,因此一方向另一方发起同步之后,另一方可以将自己的 ACK-SYN 打包作为一条消息回复。因此是三次握手,需要三次数据传输。

在LINUX操作系统中允许TCP采用半关闭。

到了挥手阶段,双方都可能有未完成的工作,收到挥手请求的一方必须马上响应,ACK 表示接收到了挥手请求。最后等所有工作结束再发送请求中断连接 FIN,因此是四次挥手。

TCP四次挥手过程中,为什么需要等待2MSL,才进入CLOSED关闭状态?

  • 1. 为了保证客户端发送的最后一个ACK报文段能够到达服务端。 这个ACK报文段有可能丢失,因而使处在LAST-ACK状态的服务端就收不到对已发送的FIN + ACK报文段的确认。服务端会超时重传这个FIN+ACK 报文段,而客户端就能在 2MSL 时间内(超时 + 1MSL 传输)收到这个重传的 FIN+ACK 报文段。接着客户端重传一次确认,重新启动2MSL计时器。最后,客户端和服务器都正常进入到CLOSED状态。
  • 2. 防止已失效的连接请求报文段出现在本连接中。客户端在发送完最后一个ACK报文段后,再经过时间2MSL,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。
2. 为什么需要TIME_WAIT状态

(1)防止旧链接的数据包

TIME_WAIT状态持续时间为2MSL,经过2MSL时间,旧连接的所有报文都在网络中消失,防止其对新连接的建立及传输过程造成影响。

(2)保证连接正常关闭

当服务端发送终止连接 FIN 报文时,进⼊LAST_ACK 状态,等待客户端的确认报文。如果FIN报文未正常到达客户端,或者客户端的确认报文未正常传输到服务端,此时服务端无法正常关闭连接。因此需要对 FIN 报文重传,假如客户端没有TIME_WAIT状态,直接进入closed 状态,那么服务端永远无法收到客户端确认报文,因此无法正常关闭。

3. TCP的拆包和粘包

TCP是面向流,没有界限的一串数据。TCP底层并不了解上层业务数据的具体含义,它会根据TCP缓冲区的实际情况进行包的划分,所以在业务上认为, 一个完整的包可能会被TCP拆分成多个包进行发送也有可能把多个小的包封装成一个大的数据包发送,这就是所谓的TCP粘包和拆包问题。

为什么会产生粘包和拆包呢?

  • 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据一次发送出去,将会发生粘包;
  • 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包;
  • 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包;
  • 待发送数据大于MSS(最大报文长度),TCP在传输前将进行拆包。即TCP报文长度-TCP头部长度>MSS。

解决方案:

  • 发送端将每个数据包封装为固定长度
  • 在数据尾部增加特殊字符进行分割
  • 将数据分为两部分,一部分是头部,一部分是内容体;其中头部结构大小固定,且有一个字段声明内容体的大小。
4. 滑动窗口和流速控制是怎么回事?

滑动窗口是 TCP 协议控制可靠性的核心,发送方将数据拆包,变成多个分组,然后将数据放入一个拥有滑动窗口的数组,依次发出,仍然遵循先入先出(FIFO)的顺序,但是窗口中的分组会一次性发送。窗口中序号最小的分组如果收到 ACK,窗口就会发生滑动。如果最小序号的分组长时间没有收到 ACK,就会触发整个窗口的数据重新发送。

滑动窗口的作用:

TCP 利用滑动窗口实现流量控制的机制。早期的网络通信中,通信双方不会考虑网络的拥挤情况直接发送数据。由于大家不知道网络拥塞状况,同时发送数据,导致中间节点阻塞掉包,谁也发不了数据,所以就有了滑动窗口机制来解决此问题。

TCP 中采用滑动窗口来进行传输控制,滑动窗口的大小意味着接收方还有多大的缓冲区可以用于接收数据。发送方可以通过滑动窗口的大小来确定应该发送多少字节的数据。当滑动窗口为 0 时,发送方一般不能再发送数据报,但有两种情况除外,一种情况是可以发送紧急数据,例如,允许用户终止在远端机上的运行进程。另一种情况是发送方可以发送一个 1 字节的数据报来通知接收方重新声明它希望接收的下一字节及发送方的滑动窗口大小。

如果接收方的接收窗口告诉发送方已经没有缓存位置了,这时发送方就不会再发送数据,假设接收方又有100个缓存位置,然后发送报文给发送方,但是报文丢失,这样双方都在等待,就形成了死锁,怎么解决(我的回答是发送方发送探测报文)?

发送方看到window为0的包,会启动一个定时器,隔一段时间发一个包试探

5. TCP的拥塞控制
6. TCP和UDP的区别?

TCP:面向连接的可靠数据包传输

UDP:无连接的不可靠报文传递

在这里插入图片描述

7. 路由器和交换机的区别?
所属网络模型的层级功能
路由器网络层识别IP地址并根据IP地址转发数据包,维护数据表并基于数据表进行最佳路径选择
交换机数据链路层识别MAC地址并根据MAC地址转发数据帧
8. Tunnel 技术是什么?

Tunnel 就是隧道,两个网络用隧道连接,位于两个网络中的设备通信,都可以使用这个隧道。如果两个 IPv6 网络被 IPv4 分隔开,那么两个 IPv6 网络的出口处就可以用程序实现一个隧道。

9. ICMP

网络控制报文协议,可以报告错误信息或者异常情况,ICMP报文封装在IP数据报当中。

ICMP协议的应用:

  • Ping:网络故障的排查
  • Traceroute:可以探测IP数据包在网络中走过的路径
10. TTL

IP包中的Time To Live,生存周期。在网络中,由于某些路径出现故障,而导致数据包在其中几个路由器中形成环路循环转发,永远无法发送结束。这就需要进行干预,即规定了一个途径的最大路由器数量(TTL),如果转发途径路由器大于这个数量则将此数据包直接丢弃。

11. ARQ 协议(自动重传请求)

停等 ARQ 协议

停止等待协议中超时重传是指只要超过一段时间仍然没有收到确认,就重传前面发送过的分组(认为刚才发送过的分组丢失了)。因此每发送完一个分组需要设置一个超时计时器,其重传时间应比数据在分组传输的平均往返时间更长一些。这种自动重传方式常称为自动重传请求 ARQ。

连续 ARQ 协议

连续 ARQ 协议可提高信道利用率。发送方维持一个发送窗口,凡位于发送窗口内的分组可以连续发送出去,而不需要等待对方确认。接收方一般采用累计确认,对按序到达的最后一个分组发送确认,表明到这个分组为止的所有分组都已经正确收到了。

12. HTTP 哪些常用的状态码及使用场景?

状态码分类

1xx:表示目前是协议的中间状态,还需要后续请求

2xx:表示请求成功(服务器成功处理了客户端的请求)

3xx:表示重定向状态,需要重新请求(客户端请求的资源发生了变动,需要客户端用新的 URL 重新发送请求获取资源,也就是重定向。)

4xx:表示请求报文错误(表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义)

5xx:服务器端错误(表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码)

常用状态码

101 切换请求协议,从 HTTP 切换到 WebSocket

200 请求成功,有响应体

301 永久重定向:会缓存(说明请求的资源已经不存在了,需改用新的 URL 再次访问)【搬家】

302 临时重定向:不会缓存(说明请求的资源还在,但暂时需要用另一个 URL 来访问)【走亲戚】

304 协商缓存命中

403 服务器禁止访问

404 资源未找到

400 请求错误

500 服务器端错误

503 服务器繁忙

13. POST 和 GET 区别?
  • 作用:GET用于获取资源,POST用于传输实体主体

    Post 向指定资源提交数据进行处理请求。数据被包含在请求体中(报文body)。Post请求可能会导致新的资源的建立和/或已有资源的修改

  • 参数位置:GET的参数放在URL中,POST的参数存储在实体主体中,并且GET方法提交的请求的URL中的数据最多是2048字节,POST请求没有大小限制。

  • 安全性:GET方法因为参数放在URL中,安全性相对于POST较差一些

  • 幂等性:GET方法是具有幂等性的,而POST方法不具有幂等性。

    • 在 HTTP 协议里,所谓的「安全」是指请求方法不会「破坏」服务器上的资源。
    • 所谓的「幂等」,意思是多次执行相同的操作,结果都是「相同」的。

GET 和 POST 方法都是安全和幂等的吗?

如果从 RFC 规范定义的语义来看:

  • GET 方法是安全且幂等的,因为它是「只读」操作,无论操作多少次,服务器上的数据都是安全的,且每次的结果都是相同的。所以,可以对 GET 请求的数据做缓存,这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),而且在浏览器中 GET 请求可以保存为书签。
  • POST 因为是「新增或提交数据」的操作,会修改服务器上的资源,所以是不安全的,且多次提交数据就会创建多个资源,所以不是幂等的。所以,浏览器一般不会缓存 POST 请求,也不能把 POST 请求保存为书签。
14. HTTP 缓存技术

强制缓存:指的是只要浏览器判断缓存没有过期,则直接使用浏览器的本地缓存,决定是否使用缓存的主动性在于浏览器这边。

强缓存是利用下面这两个 HTTP 响应头部(Response Header)字段实现的,它们都用来表示资源在客户端缓存的有效期:

  • Cache-Control, 是一个相对时间;
  • Expires,是一个绝对时间;

具体的实现流程如下:

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 Cache-Control,Cache-Control 中设置了过期时间大小;
  • 浏览器再次请求访问服务器中的该资源时,会先通过请求资源的时间与 Cache-Control 中设置的过期时间大小,来计算出该资源是否过期,如果没有,则使用该缓存,否则重新请求服务器;
  • 服务器再次收到请求后,会再次更新 Response 头部的 Cache-Control。

协商缓存:就是与服务端协商之后,通过协商结果来判断是否使用本地缓存。

使用 ETag 字段实现的协商缓存的过程如下;

  • 当浏览器第一次请求访问服务器资源时,服务器会在返回这个资源的同时,在 Response 头部加上 ETag 唯一标识,这个唯一标识的值是根据当前请求的资源生成的;

  • 当浏览器再次请求访问服务器中的该资源时,首先会先检查强制缓存是否过期,如果没有过期,则直接使用本地缓存;如果缓存过期了,会在 Request 头部加上 If-None-Match 字段,该字段的值就是 ETag 唯一标识;

  • 服务器再次收到请求后,

    会根据请求中的 If-None-Match 值与当前请求的资源生成的唯一标识进行比较:

    • 如果值相等,则返回 304 Not Modified,不会返回资源
    • 如果不相等,则返回 200 状态码和返回资源,并在 Response 头部加上新的 ETag 唯一标识;
  • 如果浏览器收到 304 的请求响应状态码,则会从本地缓存中加载资源,否则更新资源。

15. HTTPS 和 HTTP 的区别

1、端口不同:HTTP 的端口号是 80,HTTPS 的端口号是 443。

2、资源消耗:HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。

3、开销:HTTPS 协议需要向 CA(证书权威机构)申请数字证书,一般免费证书很少,需要交费

4、安全性:HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。
在这里插入图片描述

16. HTTP1.0,1.1,2.0 的版本区别

思路: 这道题主要考察的知识点是HTTP几个版本的区别,我们记住HTTP/1.0默认是短连接,可以强制开启,HTTP/1.1默认长连接,HTTP/2.0采用多路复用就差不多啦。

HTTP/1.0

  • 默认使用短连接,每次请求都需要建立一个TCP连接。它可以设置Connection: keep-alive 这个字段,强制开启长连接。

HTTP/1.1

  • 引入了持久连接,即TCP连接默认不关闭,可以被多个请求复用。
  • 分块传输编码,即服务端没产生一块数据,就发送一块,用”流模式”取代”缓存模式”。
  • 管道机制,即在同一个TCP连接里面,客户端可以同时发送多个请求。

HTTP/2.0

  • 二进制协议,1.1版本的头信息是文本(ASCII编码),数据体可以是文本或者二进制;2.0中,头信息和数据体都是二进制。
  • 完全多路复用,在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应。
  • 报头压缩,HTTP协议不带有状态,每次请求都必须附上所有信息。Http/2.0引入了头信息压缩机制,使用gzip或compress压缩后再发送。
  • 服务端推送,允许服务器未经请求,主动向客户端发送资源。

HTTP/1.0:为了提高系统的效率,HTTP/1.0规定浏览器与服务器只保持短连接,浏览器的每次请求都需要与服务器建立一个TCP连接,服务器完成请求处理后立即断开TCP连接,服务器不跟踪每个客户也不记录过去的请求。

HTTP/1.1:为了解决HTTP/1.0存在的缺陷,HTTP/1.1最主要的改进就是引入了持久连接。所谓的持久连接即TCP连接默认不关闭,可以被多个请求复用。

HTTP/1.1版还引入了管道机制(pipelining),即在同一个TCP连接里面,客户端可以同时发送多个请求。这样就进一步改进了HTTP协议的效率。

HTTP/2 :有了持久连接和管道,大大提升了HTTP的效率。但是服务端还是顺序执行的,效率还有提升的空间。HTTP/2 是 HTTP 协议自 1999 年 HTTP 1.1 发布后的首个更新,主要基于 SPDY 协议。

HTTP/2 为了解决HTTP/1.1中仍然存在的效率问题,HTTP/2 采用了多路复用。即在一个连接里,客户端和浏览器都可以同时发送多个请求或回应,而且不用按照顺序一一对应。能这样做有一个前提,就是HTTP/2进行了二进制分帧,即 HTTP/2 会将所有传输的信息分割为更小的消息和帧(frame),并对它们采用二进制格式的编码。

除此之外,还有一些其他的优化,比如做Header压缩、服务端推送等。

17 . Https流程是怎样的?

思路: 这道题实际上考察的知识点是HTTPS的工作流程,大家需要回答这几个要点,公私钥、数字证书、加密、对称加密、非对称加密

  • HTTPS = HTTP + SSL/TLS,也就是用SSL/TLS对数据进行加密和解密,Http进行传输。
  • SSL,即Secure Sockets Layer(安全套接层协议),是网络通信提供安全及数据完整性的一种安全协议。
  • TLS,即Transport Layer Security(安全传输层协议),它是SSL3.0的后续版本。

在这里插入图片描述

Https工作流程

  1. 客户端发起Https请求,连接到服务器的443端口。
  2. 服务器必须要有一套数字证书(证书内容有公钥、证书颁发机构、失效日期等)。
  3. 服务器将自己的数字证书发送给客户端(公钥在证书里面,私钥由服务器持有)。
  4. 客户端收到数字证书之后,会验证证书的合法性。如果证书验证通过,就会生成一个随机的对称密钥,用证书的公钥加密。
  5. 客户端将公钥加密后的密钥发送到服务器。
  6. 服务器接收到客户端发来的密文密钥之后,用自己之前保留的私钥对其进行非对称解密,解密之后就得到客户端的密钥,然后用客户端密钥对返回数据进行对称加密,酱紫传输的数据都是密文啦。
  7. 服务器将加密后的密文返回到客户端。
  8. 客户端收到后,用自己的密钥对其进行对称解密,得到服务器返回的数据。
18. 说说什么是数字签名?什么是数字证书?

思路: 这道题考查的知识点,不仅仅是数字签名,数字证书,很可能面试官也会问你https的原理的,因为https原理跟数字证书有关的哈,大家需要掌握https原理哦。

数字证书是指在互联网通讯中标志通讯各方身份信息的一个数字认证,人们可以在网上用它来识别对方的身份。它的出现,是为了避免身份被篡改冒充的。比如Https的数字证书,就是为了避免公钥被中间人冒充篡改。

数字证书构成

  • 公钥和个人等信息,经过Hash摘要算法加密,形成消息摘要;将消息摘要拿到拥有公信力的认证中心(CA),用它的私钥对消息摘要加密,形成数字签名
  • 公钥和个人信息、数字签名共同构成数字证书
19. IP分片技术

如果 IP 层有一个数据报要传,而且网络包的长度比链路层的 MTU 还大,那么 IP 层就需要进行分片,即把数据报分成若干片,这样每一片就都小于 MTU。分片可能发生在发送端,也可能发送在中转路由器上,而且可能在传输过程中被多次分片,但只有在最终的目标机器上,这些分片才会被内核中的IP模块重新组装。
IP的分片和重组需要的信息由头部的三个字段提供:16位数据报标识、3位标志、13位偏移。一个IP数据报的每个分片都具有自己的IP头部,他们具有相同的标识值,但具有不同的片偏移。并且除了最后一个分片外,其他分片都将设置MF标志。此外,每个分片的IP头部的总长度字段将被设置为该分片的长度。
以太网帧的MTU是1500字节,因此他携带的IP数据报的数据部分最多是1480字节(因为头部占了20字节)。

20. RST报文

TCP的异常终止是相对于正常释放TCP连接的过程而言的,正常情况下是通过四次挥手来关闭TCP连接的,但是有些情况下,TCP在交互的过程中会出现一些意想不到的情况,导致TCP无法按照正常的四次挥手来释放连接,如果此时不通过其他的方式来释放TCP连接的话,这个TCP连接将会一直存在,占用系统的部分资源。

RST表示复位,用来异常的关闭连接,发送RST包关闭连接时,不必等缓冲区的包都发出去(不像上面的FIN包),直接就丢弃缓存区的包发送RST包。而接收端收到RST包后,也不必发送ACK包来确认。

异常终止常见情况:

(1)未对外提供服务的端口

客户端尝试与服务器未对外提供服务的端口建立TCP连接,服务器将会直接向客户端发送reset报文。

(2)程序崩溃

客户端和服务器的某一方在交互的过程中发生异常(如程序崩溃等),该方系统将向对端发送TCP reset报文,告之对方释放相关的TCP连接。

(3)不在其已建立的TCP连接列表

接收端收到TCP报文,但是发现该TCP的报文,并不在其已建立的TCP连接列表内,则其直接向对端发送reset报文。

(4)长期未收到来自对方的确认报文

在交互的双方中的某一方长期未收到来自对方的确认报文,则其在超出一定的重传次数或时间后,会主动向对端发送reset报文释放该TCP连接。

(5)利用reset报文快速释放

有些应用开发者在设计应用系统时,会利用reset报文快速释放已经完成数据交互的TCP连接,以提高业务交互的效率。

21. TCP/IP四层模型
  • 应用层:对应于OSI参考模型的(应用层、表示层、会话层)。
  • 传输层:对应OSI的传输层,为应用层实体提供端到端的通信功能,保证了数据包的顺序传送及数据的完整性。
  • 网际层:对应于OSI参考模型的网络层,主要解决主机到主机的通信问题。
  • 网络接口层:与OSI参考模型的数据链路层、物理层对应。
22. 从浏览器地址栏输入url到显示主页的过程

思路: 这道题主要考察的知识点是HTTP的请求过程,DNS解析,TCP三次握手,四次挥手这几个要点,我们都可以讲下。

  1. DNS解析,查找域名对应的IP地址。
  2. 与服务器通过三次握手,建立TCP连接
  3. 向服务器发送HTTP请求
  4. 服务器处理请求,返回网页内容
  5. 浏览器解析并渲染页面
  6. TCP四次挥手,连接结束
23. 说说DNS的解析过程?

假设你要查询www.baidu.com的IP地址:

  • 首先会查找浏览器的缓存,看看是否能找到www.baidu.com对应的IP地址,找到就直接返回;否则进行下一步。
  • 将请求发往给本地DNS服务器,如果查找到也直接返回,否则继续进行下一步;
  • 本地DNS服务器向根域名服务器发送请求,根域名服务器返回负责.com的顶级域名服务器的IP地址的列表。
  • 本地DNS服务器再向其中一个负责.com的顶级域名服务器发送一个请求,返回负责.baidu的权威域名服务器的IP地址列表。
  • 本地DNS服务器再向其中一个权威域名服务器发送一个请求,返回www.baidu.com所对应的IP地址。
24. 说说 WebSocket与socket的区别

思路: 这是一个比较基础的知识点,经常有小伙伴会搞混。

  • Socket其实就是等于IP地址 + 端口 + 协议

★ 具体来说,Socket是一套标准,它完成了对TCP/IP的高度封装,屏蔽网络细节,以方便开发者更好地进行网络编程。

  • WebSocket是一个持久化的协议,它是伴随H5而出的协议,用来解决http不支持持久化连接的问题。
  • Socket一个是网编编程的标准接口,而WebSocket则是应用层通信协议。
25. forward和redirect的区别?
  • 直接转发方式(Forward):客户端和浏览器只发出一次请求,Servlet、HTML、JSP或其它信息资源,由第二个信息资源响应该请求,在请求对象request中,保存的对象对于每个信息资源是共享的。

  • 间接转发方式(Redirect):实际是两次HTTP请求,服务器端在响应第一次请求的时候,让浏览器再向另外一个URL发出请求,从而达到转发的目的。

Redirect 的工作原理:

在这里插入图片描述

forward 的工作原理:

在这里插入图片描述

  • 直接转发就相当于:“A找B借钱,B说没有,B去找C借,借到借不到都会把消息传递给A”;
  • 间接转发就相当于:“A找B借钱,B说没有,让A去找C借”。
26. ARP协议的工作过程

ARP 协议协议,Address Resolution Protocol,地址解析协议,它是用于实现IP地址到MAC地址的映射。

  1. 首先,每台主机都会在自己的ARP缓冲区中建立一个ARP列表,以表示IP地址和MAC地址的对应关系。
  2. 当源主机需要将一个数据包要发送到目的主机时,会首先检查自己的ARP列表,是否存在该IP地址对应的MAC地址;如果有﹐就直接将数据包发送到这个MAC地址;如果没有,就向本地网段发起一个ARP请求的广播包,查询此目的主机对应的MAC地址。此ARP请求的数据包里,包括源主机的IP地址、硬件地址、以及目的主机的IP地址。
  3. 网络中所有的主机收到这个ARP请求后,会检查数据包中的目的IP是否和自己的IP地址一致。如果不相同,就会忽略此数据包;如果相同,该主机首先将发送端的MAC地址和IP地址添加到自己的ARP列表中,如果ARP表中已经存在该IP的信息,则将其覆盖,然后给源主机发送一个 ARP响应数据包,告诉对方自己是它需要查找的MAC地址。
  4. 源主机收到这个ARP响应数据包后,将得到的目的主机的IP地址和MAC地址添加到自己的ARP列表中,并利用此信息开始数据的传输。如果源主机一直没有收到ARP响应数据包,表示ARP查询失败。
杂谈
字节:

服务端如果只bind了ip和端口,但是没有调用listen让这个socket监听连接,这时候如果客户端朝这个服务端socket发数据,会发生啥?

如果没有listen,说明未完成三次握手来建立连接,则客户端发送的报文就不在服务端已建立的TCP连接列表中,那么服务端会直接回复RST报文。

网络编程

1. 什么是零拷贝

零拷贝是指计算机执行IO操作时,CPU不需要将数据从一个存储区域复制到另一个存储区域,从而可以减少上下文切换以及CPU的拷贝时间。它是一种I/O操作优化技术。

零拷贝的好处

  • 减少或避免不必要的CPU数据拷贝,从而释放CPU去执行其他任务
  • 零拷贝机制能减少用户空间和操作系统内核空间的上下文切换
  • 减少内存的占用

零拷贝并不是没有拷贝数据,而是减少用户态/内核态的切换次数以及CPU拷贝的次数。零拷贝实现有多种方式,分别是

  • mmap+write
  • sendfile
  • 带有DMA收集拷贝功能的sendfile

https://mp.weixin.qq.com/s/SzK_AiVQo09R65GXBLrchg

2. IO模型

计算机角度的IO:

涉及计算机核心与其他设备间数据迁移的过程,就是IO。如磁盘IO,就是从磁盘读取数据到内存,这算一次输入,对应的,将内存中的数据写入磁盘,就算输出。这就是IO的本质。

操作系统的IO:

我们应用程序是跑在用户空间的,它不存在实质的IO过程,真正的IO是在操作系统执行的。即应用程序的IO操作分为两种动作:IO调用和IO执行。IO调用是由进程(应用程序的运行态)发起,而IO执行是操作系统内核的工作。此时所说的IO是应用程序对操作系统IO功能的一次触发,即IO调用。

操作系统的一次IO过程:

  • 应用程序进程向操作系统发起IO调用请求
  • 操作系统准备数据,把IO外部设备的数据,加载到内核缓冲区
  • 操作系统拷贝数据,即将内核缓冲区的数据,拷贝到用户进程缓冲区

阻塞IO模型

假设应用程序的进程发起IO调用,但是如果内核的数据还没准备好的话,那应用程序进程就一直在阻塞等待,一直等到内核数据准备好了,从内核拷贝到用户空间,才返回成功提示,此次IO操作,称之为阻塞IO。

  • 阻塞IO比较经典的应用就是阻塞socket、Java BIO。
  • 阻塞IO的缺点就是:如果内核数据一直没准备好,那用户进程将一直阻塞,浪费性能,可以使用非阻塞IO优化。

非阻塞IO模型

如果内核数据还没准备好,可以先返回错误信息给用户进程,让它不需要等待,而是通过轮询的方式再来请求。这就是非阻塞IO。

非阻塞IO模型,简称 NIO,Non-Blocking IO。它相对于阻塞IO,虽然大幅提升了性能,但是它依然存在性能问题,即频繁的轮询,导致频繁的系统调用,同样会消耗大量的CPU资源。可以考虑IO复用模型,去解决这个问题。

IO多路复用模型

既然NIO无效的轮询会导致CPU资源消耗,我们等到内核数据准备好了,主动通知应用进程再去进行系统调用,那不就好了嘛?

IO复用模型核心思路:系统给我们提供一类函数(如我们耳濡目染的select、poll、epoll函数),它们可以同时监控多个fd的操作,任何一个返回内核数据就绪,应用进程再发起recvfrom系统调用。

IO多路复用之select

应用进程通过调用 select 函数,可以同时监控多个fd,在select函数监控的fd中,只要有任何一个数据状态准备就绪了,select函数就会返回可读状态,这时应用进程再发起recvfrom请求去读取数据。

非阻塞IO模型(NIO)中,需要N(N>=1)次轮询系统调用,然而借助select的IO多路复用模型,只需要发起一次询问就够了,大大优化了性能。

但是呢,select有几个缺点:

  • 监听的IO最大连接数有限,在Linux系统上一般为1024。
  • select函数返回后,是通过遍历fdset,找到就绪的描述符fd。(仅知道有I/O事件发生,却不知是哪几个流,所以遍历所有流)

因为存在连接数限制,所以后来又提出了poll。与select相比,poll解决了连接数限制问题。但是呢,select和poll一样,还是需要通过遍历文件描述符来获取已经就绪的socket。如果同时连接的大量客户端,在一时刻可能只有极少处于就绪状态,伴随着监视的描述符数量的增长,效率也会线性下降

因此经典的多路复用模型epoll诞生。

IO多路复用之epoll

epoll 先通过epoll_ctl()来注册一个fd,一旦基于某个fd就绪时,内核会采用回调机制,迅速激活这个fd,当进程调用epoll_wait()时便得到通知。这里去掉了遍历文件描述符的坑爹操作,而是采用监听事件回调的机制。这就是 epoll的亮点。

selectpollepoll
底层数据结构数组链表红黑树和双链表
获取就绪的fd遍历遍历事件回调
事件复杂度O(n)O(n)O(1)
最大连接数1024无限制无限制
fd数据拷贝每次调用select,需要将fd数据从用户空间拷贝到内核空间每次调用poll,需要将fd数据从用户空间拷贝到内核空间使用内存映射(mmap),不需要从用户空间频繁拷贝fd数据到内核空间

epoll明显优化了IO的执行效率,但在进程调用epoll_wait()时,仍然可能被阻塞。能不能酱紫:不用我老是去问你数据是否准备就绪,等我发出请求后,你数据准备好了通知我就行了,这就诞生了信号驱动IO模型

IO模型之信号驱动模型

信号驱动IO不再用主动询问的方式去确认数据是否就绪,而是向内核发送一个信号(调用sigaction的时候建立一个SIGIO的信号),然后应用用户进程可以去做别的事,不用阻塞。当内核数据准备好后,再通过SIGIO信号通知应用进程,数据准备好后的可读状态。应用用户进程收到信号之后,立即调用recvfrom,去读取数据。

信号驱动IO模型,在应用进程发出信号后,是立即返回的,不会阻塞进程。它已经有异步操作的感觉了。但是你细看上面的流程图,发现数据复制到应用缓冲的时候,应用进程还是阻塞的。回过头来看下,不管是BIO,还是NIO,还是信号驱动,在数据从内核复制到应用缓冲的时候,都是阻塞的。还有没有优化方案呢?AIO(真正的异步IO)!

IO 模型之异步IO(AIO)

前面讲的BIO,NIO和信号驱动,在数据从内核复制到应用缓冲的时候,都是阻塞的,因此都不算是真正的异步。AIO实现了IO全流程的非阻塞,就是应用进程发出系统调用后,是立即返回的,但是立即返回的不是处理结果,而是表示提交成功类似的意思。等内核数据准备好,将数据拷贝到用户进程缓冲区,发送信号通知用户进程IO操作执行完毕。

异步IO的优化思路很简单,只需要向内核发送一次请求,就可以完成数据状态询问和数据拷贝的所有操作,并且不用阻塞等待结果。

https://mp.weixin.qq.com/s/bb7C6VNbq7REP9u8PsreSg

3. 系统级IO

https://mp.weixin.qq.com/s/GFca4uEq3Efwih-oCGU2_A

共享文件

内核使用三个相关的数据结构来表示打开的文件:文件描述符,文件表,v-node表。

通常一个进程中一个文件描述符指向一个文件表项,然后对应一个v-node表项。不同的文件描述符可以对应同一个文件,这样的文件就是共享文件,被两个文件描述符共享,当然还可以被两个进程共享。

算法

1. 有了二叉查找树、平衡树为啥还需要红黑树?

二叉查找树

二叉查找树的特点就是 左子树的节点值比父亲节点小,而右子树的节点值比父亲节点大,基于二叉查找树的这种特点,在查找某个节点的时候,可以采取类似于二分查找的思想,快速找到某个节点。n 个节点的二叉查找树,正常的情况下,查找的时间复杂度为 O(logn)。然而,当二叉查找树近似退化为一条链表,这样的二叉查找树的查找时间复杂度顿时变成了 O(n),可想而知,我们必须不能让这种情况发生,为了解决这个问题,于是我们引申出了平衡二叉树

平衡二叉树

平衡二叉树就是为了解决二叉查找树退化成一颗链表而诞生了,平衡树具有如下特点:

1、具有二叉查找树的全部特性。

2、每个节点的左子树和右子树的高度差至多等于1。

通过平衡树,我们解决了二叉查找树的缺点。对于有 n 个节点的平衡树,最坏的查找时间复杂度也为 O(logn)。

红黑树

虽然平衡树解决了二叉查找树退化为近似链表的缺点,能够把查找时间控制在 O(logn),不过却不是最佳的,因为平衡树要求每个节点的左子树和右子树的高度差至多等于1,这个要求实在是太严了,导致每次进行插入/删除节点的时候,几乎都会破坏平衡树的第二个规则,进而我们都需要通过左旋右旋来进行调整,使之再次成为一颗符合要求的平衡树。

显然,如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣,为了解决这个问题,于是有了红黑树

在最坏情况下,红黑树也能在 O(logn) 的时间复杂度查找到某个节点。

总结:平衡树是为了解决二叉查找树退化为链表的情况,而红黑树是为了解决平衡树在插入、删除等操作需要频繁调整的情况。

2. 假如给你20亿个非负数的int型整数,然后再给你一个非负数的int型整数 t ,让你判断t是否存在于这20亿数中,你会怎么做呢?

位图:bitmap

4Byte * 20亿 = 80亿个字节,大概需要8G的内存空间

用单个bit(两种状态1/0)来表示某个数是否存在,一个字节可以表示8个数的存在与否,共需内存20亿 / 8 = 250e6字节≈250M。故只需250M的内存空间即可。

在这里插入图片描述

  • 11
    点赞
  • 66
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
计算机网络八股文 计算机网络是现代信息技术的核心基础之一,它涉及了计算机通信和数据传输技术等多个方面。下面将从网络体系结构、网络协议、网络安全和网络性能等方面对计算机网络进行阐述。 首先,计算机网络的体系结构包括了物理层、数据链路层、网络层、传输层和应用层。物理层负责传输比特流,数据链路层提供可靠的点对点数据传输,网络层负责数据包的路由与转发,传输层提供端到端的可靠或不可靠的数据传输服务,而应用层则为用户提供网络应用服务。 其次,网络协议是计算机网络中的重要组成部分。常见的网络协议包括TCP/IP协议、HTTP协议、FTP协议等。TCP/IP协议是互联网的核心协议,它包括了IP地址分配、路由选择和数据传输等功能。HTTP协议用于在Web浏览器与Web服务器之间传输超文本数据,FTP协议用于文件传输。 此外,网络安全是计算机网络中不可忽视的问题。常见的网络安全措施包括防火墙、入侵检测系统和加密技术等。防火墙可以过滤网络流量,保护内部网络免受外部攻击;入侵检测系统可以检测和阻止恶意行为;加密技术可以保护数据的机密性和完整性。 最后,网络性能是衡量计算机网络好坏的重要指标之一。网络性能包括带宽、时延、吞吐量和丢包率等。带宽是指网络传输数据的能力,时延是数据从发送到接收所需的时间,吞吐量是单位时间内传输的数据量,丢包率是指在传输过程中丢失的数据包比例。 综上所述,计算机网络是一门涉及广泛的学科,它的体系结构、协议、安全和性能等方面都需要深入研究和理解。通过不断提高网络技术和加强网络安全措施,我们可以更好地应对计算机网络发展带来的挑战。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值