备战面试基础问题
文章目录
基础问题
计算机网络
计算机网络每层都有什么协议
TCP和UDP
三次握手、四次挥手
三次握手
1.客户端发送 SYN(SEQ=x)报文给服务器端,进入 SYN_SEND 状态。
2.服务器端收到 SYN 报文,回应一个 SYN (SEQ=y)ACK(ACK=x+1)报文,进入 SYN_RECV 状态。
3.客户端收到服务器端的 SYN 报文,回应一个 ACK(ACK=y+1)报文,进入 Established 状态。
四次挥手
1.某个应用进程首先调用 close,称该端执行“主动关闭”(active close)。该端的 TCP 于是发送一个 FIN 分节,表示数据发送完毕。
2.接收到这个 FIN 的对端执行 “被动关闭”(passive close),这个 FIN 由 TCP 确认。
注意:FIN
的接收也作为一个文件结束符(end-of-file)传递给接收端应用进程,放在已排队等候该应用进程接收的任何其他数据之后,因为,FIN
的接收意味着接收端应用进程在相应连接上再无额外数据可接收。
3.一段时间后,接收到这个文件结束符的应用进程将调用 close 关闭它的套接字。这导致它的 TCP 也发送一个 FIN。
4.接收这个最终FIN的原发送端 TCP(即执行主动关闭的那一端)确认这个 FIN。
既然每个方向都需要一个 FIN 和一个 ACK,因此通常需要4个分节。
TCP拥塞控制
四种方法
- 慢开始
假设当前发送方拥塞窗口cwnd的值为1,而发送窗口swnd等于拥塞窗口cwnd,因此发送方当前只能发送一个数据报文段(拥塞窗口cwnd的值是几,就能发送几个数据报文段),接收方收到该数据报文段后,给发送方回复一个确认报文段,发送方收到该确认报文后,将拥塞窗口的值变为2,当前的拥塞窗口cwnd的值已经等于慢开始门限值,之后改用拥塞避免算法。 - 拥塞避免
也就是每个传输轮次,拥塞窗口cwnd只能线性加一,而不是像慢开始算法时,每个传输轮次,拥塞窗口cwnd按指数增长。同理,16+1……直至到达24,假设24个报文段在传输过程中丢失4个,接收方只收到20个报文段,给发送方依次回复20个确认报文段,一段时间后,丢失的4个报文段的重传计时器超时了,发送发判断可能出现拥塞,更改cwnd和ssthresh.并重新开始慢开始算法 - 快重传
发送方发送1号数据报文段,接收方收到1号报文段后给发送方发回对1号报文段的确认,在1号报文段到达发送方之前,发送方还可以将发送窗口内的2号数据报文段发送出去,接收方收到2号报文段后给发送方发回对2号报文段的确认,在2号报文段到达发送方之前,发送方还可以将发送窗口内的3号数据报文段发送出去,此时,发送方收到了累计3个连续的针对2号报文段的重复确认,立即重传3号报文段,接收方收到后,给发送方发回针对6号报文的确认,表明,序号到6为至的报文都收到了,这样就不会造成发送方对3号报文的超时重传,而是提早收到了重传。 - 快恢复
当发送方连续收到三个重复确认,执行乘法减小,ssthresh减半
操作系统
进程与线程
进程间通讯方式
- 共享内存
共享内存是最快的进程间通讯的方式
原因:相对于其他几种方式,共享内存直接在进程的虚拟地址空间进行操作,不再通过执行进入内核的系统调用来传递彼此的数据 - 信号
信号量和以前的IPC通信方式不同,信号量的本质是计数器,用于多进程对共享数据对象的访问。
在进程访问临界资源之前,需要测试信号量,如果为正数,则信号量-1并且进程可以进入临界区,若为非正数,则进程挂起放入等待队列,直至有进程退出临界区,释放资源并+1信号量,此时唤醒等待队列的进程。
信号量本身就是临界资源,所以必须是原子操作。 - 管道
单向,一端输入,另一端输出,先进先出FIFO。管道也是文件。管道大小4096字节。
特点:管道满时,写阻塞;空时,读阻塞。
分类:普通管道(仅父子进程间通信)位于内存;命名管道位于文件系统,没有亲缘关系管道只要知道管道名也可以通讯。
管道是由内核管理的一个缓冲区(buffer),相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失 - 消息队列
消息队列是消息的链表,包括Posix消息队列和system v消息队列(Posix常用于线程,system常用于进程),有权限的进程可以向消息队列中添加消息,有读权限的进程可以读走消息队列的消息。
消息队列克服了信号承载信息量少,管道只能承载无格式字节流及缓冲区大小受限等缺陷。
死锁的必要条件
- 什么是死锁
死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。 - 死锁产生的原因
- 竞争不可抢占资源引起死锁
- 竞争可消耗资源引起死锁
- 进程推进顺序不当引起死锁
- 产生死锁的必要条件
- 互斥条件
进程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。 - 不可剥夺条件
进程所获得的资源在未使用完毕之前,不能被其他进程强行夺走,即只能由获得该资源的进程自己来释放(只能是主动释放)。 - 请求与保持条件
进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。 - 循环等待条件
存在一种进程资源的循环等待链,链中每一个进程已获得的资源同时被 链中下一个进程所请求。
- 互斥条件
数据结构
map和hashmap的区别
Map集合的特点:
1、Map集合一次存储两个对象,一个键对象,一个值对象
2、键对象在集合中是唯一的,可以通过键来查找值
HashMap特点:
1、使用哈希算法对键去重复,效率高,但无序
2、HashMap是Map接口的主要实现类
hashset和dictionary的区别
1.HashTable
哈希表(HashTable)表示键/值对的集合。在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key-value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。
在哈希表中添加一个key/键值对:HashtableObject.Add(key,);
在哈希表中去除某个key/键值对:HashtableObject.Remove(key);
从哈希表中移除所有元素: HashtableObject.Clear();
判断哈希表是否包含特定键key: HashtableObject.Contains(key);
2.HashSet
HashSet类主要是设计用来做高性能集运算的,例如对两个集合求交集、并集、差集等。集合中包含一组不重复出现且无特性顺序的元素,HashSet拒绝接受重复的对象。
HashSet的一些特性如下:
a. HashSet中的值不能重复且没有顺序。
b. HashSet的容量会按需自动添加。
3.Dictionary
Dictionary表示键和值的集合。
Dictionary<string, string>是一个泛型
他本身有集合的功能有时候可以把它看成数组
他的结构是这样的:Dictionary<[key], [value]>
他的特点是存入对象是需要与[key]值一一对应的存入该泛型
通过某一个一定的[key]去找到对应的值
4.HashTable和Dictionary的区别:
(1).HashTable不支持泛型,而Dictionary支持泛型。
(2). Hashtable 的元素属于 Object 类型,所以在存储或检索值类型时通常发生装箱和拆箱的操作,所以你可能需要进行一些类型转换的操作,而且对于int,float这些值类型还需要进行装箱等操作,非常耗时。
(3).单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分。多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减。
(4)在通过代码测试的时候发现key是整数型Dictionary的效率比Hashtable快,如果key是字符串型,Dictionary的效率没有Hashtable快。
C#
GC——垃圾回收
.Net类型分为两大类,一个就是值类型,另一个就是引用类型。前者是分配在栈上,并不需要GC回收;后者是分配在堆上,因此它的内存释放和回收需要通过GC来完成。
只要判定此对象或者其包含的子对象没有任何引用是有效的,那么系统就认为它是垃圾。
对于内存中的垃圾分为两种,一种是需要调用对象的析构函数,另一种是不需要调用的。GC对于前者的回收需要通过两步完成,第一步是调用对象的析构函数,第二步是回收内存,但是要注意这两步不是在GC一次轮循完成,即需要两次轮循;相对于后者,则只是回收内存而已。
如何使用协程?协程和线程区别?
-
如何使用协程
-
开启协程的两种方式
1、StartCoroutine(string methodName)
注意:
(1)、参数是方法名(字符串类型),此方法可以包含一个参数
(2)、形参方法可以有返回值
2、StartCoroutine(IEnumerator method)
注意:
(1)、参数是方法名(TestMethod()),方法中可以包含多个参数
(2)、IEnumrator 类型的方法不能含有ref或者out 类型的参数,但可以含有被传递的引用
(3)、必须有有返回值,且返回值类型为IEnumrator,返回值使用(yield retuen +表达式或者值,或者 yield break)语句
终止协程的两种方式:
StopCoroutine (string methodName),只能终止指定的协程
StopAllCoroutine(),终止所有协程
常量 静态字段 实例字段
用修饰符static声明的字段为静态字段。不管包含该静态字段的类生成多少个对象或根本无对象,该字段都只有一个实例,静态字段不能被撤销。
如果类中定义的字段不使用修饰符static,该字段为实例字段,每创建该类的一个对象,在对象内创建一个该字段实例,创建它的对象被撤销,该字段对象也被撤销
用const修饰符声明的字段为常量,常量只能在声明中初始化,以后不能再修改。
接口和抽象类的区别
-
抽象类
1抽象类
(1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法
(2) 抽象类不能被实例化
(3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法,就一定要把这个类声明为抽象类
(4) 具体派生类必须覆盖基类的抽象方法
(5) 抽象派生类可以覆盖基类的抽象方法,也可以不覆盖。如果不覆盖,则其具体派生类必须覆盖它们。 -
接口
(1) 接口不能被实例化
(2) 接口只能包含方法声明
(3) 接口的成员包括方法、属性、索引器、事件
(4) 接口中不能包含常量、字段(域)、构造函数、析构函数、静态成员。
(5) 接口中的所有成员默认为public,因此接口中不能有private修饰符
(6) 派生类必须实现接口的所有成员
(7) 一个类可以直接实现多个接口,接口之间用逗号隔开
(8) 一个接口可以有多个父接口,实现该接口的类必须实现所有父接口中的所有成员 -
抽象类和接口
相同点:
(1) 都可以被继承
(2) 都不能被实例化
(3) 都可以包含方法声明
(4) 派生类必须实现未实现的方法
区别:
(1) 抽象基类可以定义字段、属性、方法实现。接口只能定义属性、索引器、事件、和方法声明,不能包含字段。
(2) 抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。微软的自定义接口总是后带able字段,证明其是表述一类“我能做。。。”
(3) 接口可以被多重实现,抽象类只能被单一继承
(4) 抽象类更多的是定义在一系列紧密相关的类间,而接口大多数是关系疏松但都实现某一功能的类中
(5) 抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
(6) 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法
(7) 接口可以用于支持回调,而继承并不具备这个特点
(8) 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的
(9) 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法