秋招面经总结

文章目录

  • 数据结构
    • 二叉树
      • 二叉树定义
      • 几种重要的二叉树
      • 二叉树的用途
    • 红黑树的定义 特点 应用场景
    • 常见的排序及复杂度
    • 知道哪些数据结构和算法
    • 了解哪些STL,举例说一下应用场景
    • 队列和栈的区别
    • 堆、栈、队列
  • 计算机网络
    • OSI参考模型有几层
    • 网络7层,每一层的作用
    • TCP和UDP的区别
    • TCP协议如何保证可靠传输
    • ping命令的作用和原理
    • TCP拥塞控制
    • ip地址分成5类
    • ipv6相对于ipv4的优势
    • TCP沾包,如何避免沾包
    • 说一下三次握手,四次挥手
      • 三次握手
      • 四次挥手
    • 为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?
    • 为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?
    • 为什么不能用两次握手进行连接?
    • 如果已经建立了连接,但是客户端突然出现故障了怎么办?
    • TCP报文格式
    • HTTP常见状态码
    • HTTP请求报文和响应报文的格式
    • http和https
    • ARP和RARP
    • 五层协议中的攻击
    • MAC和IP
    • 在浏览器中输入www.baidu.com后执行的全部过程?
    • 以太网帧结构
    • DNS流程
    • ICMP数据包格式
  • 语言基础
    • Volatile关键字
    • 编译需要那几步,那几步的作用
    • C语言的malloc怎么使用
    • Malloc与calloc,realloc的区别
    • 宏定义与内联函数的区别
    • 内联函数
    • strlen 和 sizeof 区别
    • 什么是虚函数,什么是纯虚函数,什么是抽象类?
    • 虚函数的实现条件与原理
    • 析构函数没有声明为虚析构函数的后果
    • 内存泄漏的原因
    • 深拷贝和浅拷贝
    • 结构体对齐的原则
    • 为什么要内存对齐
    • 堆和栈的区别
    • New和malloc区别?
    • C和C++的内存分区:
    • static,static局部变量?生命周期?static关键字(全局,局部,成员变量,成员函数)
    • 重载 重写区别
    • 指针和引用区别
    • 智能指针
    • 悬挂指针和野指针
    • 指针函数 函数指针
    • 堆栈溢出一般是由什么原因引起的
  • 操作系统
    • 进程与线程
    • 线程之间共享哪些资源
    • 进程间通信方式(详细)
    • 线程间通信方式的简要介绍
    • 谈一下锁机制
    • 内存的三种分配方式
    • 死锁的四个条件
    • 死锁的两个实例
    • Linux文件类型
    • 用户态和内核态
    • 进程状态
    • ps aux 和top
    • 驱动是如何加载的?
    • 虚拟内存机制
    • 孤儿进程、僵尸进程
    • linux中遇到文件无法删除怎么解决
    • linux基本命令
    • 查找文件中语句所在的那一行
    • Linux中断的响应流程
    • Linux系统启动流程
    • 编译需要哪几步,那几步的作用
    • GCC 编译参数
    • GDB调试
    • GDB多线程调试
    • Select 、poll、epoll 区别
  • 多线程与多进程
    • 多进程并发服务器的设计思路
    • 多线程并发服务器的设计思路
    • 子线程,父线程分辨
    • 多个客户端和一个服务端连接的情况
    • 具体讲一讲socket编程的步骤
  • 嵌入式相关
    • 不能用 sizeof()函数,如何判断操作系统是16位,还是32位
    • 大小端模式
    • 为什么有大小端之分
  • 判断链表有环

数据结构

二叉树

二叉树定义

二叉树是每个结点最多有两个子树的树结构,,其结点有左右子树之分

几种重要的二叉树

完全二叉树——若设二叉树的高度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数,第h层有叶子节点,并且叶子结点都是从左到右依次排布,这就是完全二叉树。
满二叉树——除了叶结点外每一个结点都有左右子叶且叶子结点都处在最底层的二叉树。
平衡二叉树——平衡二叉树又被称为AVL树(区别于AVL算法),它是一棵二叉排序树,且具有以下性质:它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。
二叉搜索树——根节点左子树全部严格小于根节点,右子树大于等于根节点,并且,左右子树都是二叉搜索树。

二叉树的用途

在处理大批量的动态的数据是比较有用,主要为提升排序和检索的效率,文件系统和数据库系统一般都采用树的数据结构

红黑树的定义 特点 应用场景

定义
红黑树是一种近似的平衡二叉查找树
特点
1.根节点是黑的
2.每个节点要么是红的,要么是黑的
3.每个叶子结点都是空的黑色节点
4.两个红色节点不能相邻
5.对于每个节点,从该节点到叶子节点走的所有路径,每条路径都包含相同数量的黑色节点。
应用场景
大量的查找,少量的插入和删除(map, set)

平衡二叉树:具有二叉查找树的全部特性。每个节点的左子树和右子树的高度差至多等于1。如果在那种插入、删除很频繁的场景中,平衡树需要频繁着进行调整,这会使平衡树的性能大打折扣
二叉查找树:左子树的节点值比父亲节点小,而右子树的节点值比父亲节点大,极端情况可能会退化成链表
近似的平衡二叉查找树:而红黑树是为了解决平衡树在插入、删除等操作需要频繁调整的情况。

常见的排序及复杂度

秋招面经总结_第1张图片
插入排序
插入排序是一种最简单的排序方法,它的基本思想是将一个数插入到已经排好序的数列中,从而一个新的、数量增 1 的有序数列。在其实现过程使用双层循环,外层循环对除了第一个元素之外的所有元素,内层循环对当前元素前面有序表进行待插入位置查找,并进行移动。

归并排序
1.将序列中待排序的数字分为若干组,每个数字为一组
2.将若干个数字两两合并,保证合并后的组是有序的,(两个序列头部比较,较小的放在新序列的头部,如果某个序列为空,将另一个序列中剩下的全部元素一次移到新序列中)
3.重复上一步操作直至只剩下一组

希尔排序
找到一个间隔数(通常为总长度的一半),每隔这个间隔数的数分在一组,每组组内排序,排序方法是插入排序
间隔减半,继续分组排序
继续减半,进行分组排序,直到间隔为1,说明所有的数都在一组。

知道哪些数据结构和算法

数据结构:
表,栈,队列,串,树,图
算法:常用的排序算法
查找算法

了解哪些STL,举例说一下应用场景

vector 经常尾插,几乎不头插的情况,比如添加和查看历史记录
stack 后进先出,比如记录上一次的操作
queue 先入先出,比如说排队买票
list 频繁地插入和删除
set 对某些数据不重复地记录
map key-value ,key是不可以重复的,按ID号存储了大量数据,想要快速通过ID查找对应的数,效率会很高

队列和栈的区别

1、队列先进先出,栈先进后出。
2、对插入和删除操作的"限定"不同。
栈是限定只能在表的一端进行插入和删除操作的线性表。
队列是限定只能在表的一端进行插入和在另一端进行删除操作的线性表。
3、遍历数据速度不同。

栈只能从头部取数据,也就最先放入的需要遍历整个栈最后才能取出来,而且在遍历数据的时候还得为数据开辟临时空间,保持数据在遍历前的一致性。

队列则不同,它基于地址指针进行遍历,而且可以从头或尾部开始遍历,但不能同时遍历,无需开辟临时空间,因为在遍历的过程中不影像数据结构,速度要快的多

堆、栈、队列

:堆(heap)是一种优先队列,在堆底插入元素,在堆顶取出元素。插入可以随便插入,取出需要按某种特定的规则。堆是二叉树的衍生,有最小堆最大堆的两个概念,将根节点最大的堆叫做大根堆,根节点最小的堆叫做小根堆。
:栈(Stack)又名堆栈,作为一个 先进后出 的数据结构。
仅允许在一端进行插入和删除运算。这一端被称为栈顶,相对地,把另一端称为栈底。向一个栈插入新元素又称作进栈、入栈或压栈,它是把新元素放到栈顶元素的上面,使之成为新的栈顶元素;从一个栈删除元素又称作出栈或退栈,它是把栈顶元素删除掉,使其相邻的元素成为新的栈顶元素。
队列:只允许在前端(front)进行删除操作,而在表的后端(rear)进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
队列采用 先进先出 FIFO(first in first out),新元素(等待进入队列的元素)总是被插入到链表的尾部,而读取的时候总是从链表的头部开始读取。每次读取一个元素,释放一个元素。所谓的动态创建,动态释放。因而也不存在溢出等问题。由于链表由结构体间接而成,遍历也方便。

计算机网络

OSI参考模型有几层

秋招面经总结_第2张图片

TCP/IP是一套用于网络通信的协议集合或者系统。TCP/IP协议模型就有OSI模型分为7层。但其实一般我们所谈到的都是四层的TCP/IP协议栈。
秋招面经总结_第3张图片

网络7层,每一层的作用

应用层:为应用程序提供交互服务。在互联网中的应用层协议很多,如域名系统DNS,支持万维网应用的HTTP协议,支持电子邮件的SMTP协议等。

表示层:主要负责数据格式的转换,如加密解密、转换翻译、压缩解压缩等。

会话层:负责在网络中的两节点之间建立、维持和终止通信,如服务器验证用户登录便是由会话层
完成的。

运输层:有时也译为传输层,向主机进程提供通用的数据传输服务。该层主要有以下两种协议:
TCP:提供面向连接的、可靠的数据传输服务;
UDP:提供无连接的、尽最大努力的数据传输服务,但不保证数据传输的可靠性。

网络层:选择合适的路由和交换结点,确保数据及时传送。主要包括IP协议。

数据链路层:数据链路层通常简称为链路层。将网络层传下来的IP数据包组装成帧,并再相邻节点的链路上传送帧。

**物理层 **:实现相邻节点间比特流的透明传输,尽可能屏蔽传输介质和通信手段的差异。

TCP和UDP的区别

秋招面经总结_第4张图片

1.基于连接与无连接
2.TCP要求系统资源较多,UDP较少;
3.UDP程序结构较简单
4.流模式(TCP)与数据报模式(UDP);
5.TCP保证数据正确性,UDP可能丢包
6.TCP保证数据顺序,UDP不保证

TCP协议如何保证可靠传输

1.检验和:通过检验和的方式,接收端可以检测出来数据是否有差错和异常,假如有差错就会直接丢弃TCP段,重新发送。

2.序列号/确认应答:序列号的作用不仅仅是应答的作用,有了序列号能够将接收到的数据根据序列号排序,并且去掉重复序列号的数据。TCP传输的过程中,每次接收方收到数据后,都会对传输方进行确认应答。也就是发送ACK报文,这个ACK报文当中带有对应的确认序列号,告诉发送方,接收到了哪些数据,下一次的数据从哪里发。

3.超时重传:超时重传是指发送出去的数据包到接收到确认包之间的时间,如果超过了这个时间会被认为是丢包了,需要重传。最大超时时间是动态计算的。

4.拥塞控制:在数据传输过程中,可能由于网络状态的问题,造成网络拥堵,此时引入拥塞控制机制,在保证TCP可靠性的同时,提高性能。

5.超时重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。如果不能及时收到一个确认,将重发这个报文。

6.流量控制:如果主机A一直向主机B发送数据,不考虑主机B的接受能力,则可能导致主机B的接受缓冲区满了而无法再接受数据,从而会导致大量的数据丢包,引发重传机制。而在重传的过程中,若主机B的接收缓冲区情况仍未好转,则会将大量的时间浪费在重传数据上,降低传送数据的效率。所以引入流量控制机制,主机B通过告诉主机A自己接收缓冲区的大小,来使主机A控制发送的数据量。流量控制与TCP协议报头中的窗口大小有关。

ping命令的作用和原理

简单来说,「ping」是用来探测本机与网络中另一主机之间是否可达的命令,如果两台主机之间ping不通,则表明这两台主机不能建立起连接。ping是定位网络通不通的一个重要手段。
ping 命令是基于 ICMP 协议来工作的,「 ICMP 」全称为 Internet 控制报文协议( Internet Control Message Protocol)。ping 命令会发送一份ICMP回显请求报文给目标主机,并等待目标主机返回ICMP回显应答。因为ICMP协议会要求目标主机在收到消息之后,必须返回ICMP应答消息给源主机,如果源主机在一定时间内收到了目标主机的应答,则表明两台主机之间网络是可达的。

TCP拥塞控制

1.慢开始:刚开始发送方的拥塞窗口值为1,发送窗口大小就等于拥塞窗口,因此刚开始发送方发送1,接收方收到确认报文后,发送方拥塞窗口值变2,之后变4,变8,直到到达上限,开始进入拥塞控制算法(加法增大)

2.拥塞避免:发送超时重传,判断网络可能出现拥塞:采取两个措施1.将慢开始门限更新为发生拥塞时的一半;2.将发送包的值减少为1(从一个包开始发),并重新开始执行慢开始算法。

3.快重传:使发送方尽快进行重传,而不是等到计数器超时再重传。
当某个数据包在网络中丢失,接收方会不停地针对这个丢失的数据包发送重复确认,当发送三个重复确认时,发送方立即重新发送该数据包

4.快恢复:当发送方接受到三个重复确认时,执行快恢复算法,拥塞窗口变为当前一半,并开始拥塞避免算法。

ip地址分成5类

A类:1.0.0.0到127.255.255.255(政府机构)
B类:128.0.0.0-191.255.255.255(中等规模的公司)
C类:192.0.0.0-223.255.255.255(分配给任何需要的人)
D类:224.0.0.0—239.255.255.255(组播)
E类:240.0.0.0—255.255.255.255(用于实验)

总结如下
秋招面经总结_第5张图片

ipv6相对于ipv4的优势

  • 扩展的寻址能力
      IPv6将IP地址长度从32位扩展到128位,支持更多级别的地址层次、更多的可寻址节点数以及更简单的地址自动配置。通过在组播地址中增加一个“范围”域提高了多点传送路由的可扩展性。还定义了一种新的地址类型,称为“任意播地址”,用于发送包给一组节点中的任意一个;
  • 简化的报头格式
      一些IPv4报头字段被删除或变为了可选项,以减少包处理中例行处理的消耗并限制IPv6报头消耗的带宽;
  • 对扩展报头和选项支持的改进
      IP报头选项编码方式的改变可以提高转发效率,使得对选项长度的限制更宽松,且提供了将来引入新的选项的更大的灵活性;
  • 标识流的能力
      增加了一种新的能力,使得标识属于发送方要求特别处理(如非默认的服务质量获“实时”服务)的特定通信“流”的包成为可能;
  • 认证和加密能力
      IPv6中指定了支持认证、数据完整性和(可选的)数据机密性的扩展功能。

TCP沾包,如何避免沾包

什么是TCP粘包问题?
TCP粘包就是指发送方发送的若干包数据到达接收方时粘成了一包,从接收缓冲区来看,后一包数据的头紧接着前一包数据的尾

造成粘包的原因:
(1)发送方原因
发送方引起的粘包是由TCP协议本身造成的,TCP为提高传输效率,发送方往往要收集到足够多的数据后才发送一包数据。若连续几次发送的数据都很少,通常TCP会根据优化算法把这些数据合成一包后一次发送出去,这样接收方就收到了粘包数据。
(2)接收方原因
TCP接收到数据包时,并不会马上交到应用层进行处理,或者说应用层并不会立即处理。实际上,TCP将接收到的数据包保存在接收缓存里,然后应用程序主动从缓存读取收到的分组。这样一来,如果TCP接收数据包到缓存的速度大于应用程序从缓存中读取数据包的速度,多个包就会被缓存,应用程序就有可能读取到多个首尾相接粘到一起的包。

粘包的解决方案:
1.使用标准的应用层协议(如http,https)来封装要传输的不定长的数据包。
2.在每条数据的尾部添加特殊字符,如果遇到特殊字符,代表这条数据接收完毕
有缺陷,效率低,需要一个字节一个自己地接收并判断
3.在发送数据块之前,在数据块前边添加一个固定大小的数据头,这时候数据由两部分组成:数据头+数据块。
数据头存储当前数据包的总字节数,接收端先接收数据头,然后在根据数据头接收对应大小的字节数
数据块:当前数据包的内容

说一下三次握手,四次挥手

三次握手

秋招面经总结_第6张图片
第一次握手:客户端请求建立连接,向服务端发送一个同步报文(SYN=1),同时选择一个随机数 seq = x 作为初始序列号,并进入SYN_SENT状态,等待服务器确认。
第二次握手:服务端收到连接请求报文后,如果同意建立连接,则向客户端发送同步确认报文(SYN=1,ACK=1),确认号为 ack = x + 1,同时选择一个随机数 seq = y 作为初始序列号,此时服务器进入SYN_RECV状态。
第三次握手:客户端收到服务端的确认后,向服务端发送一个确认报文(ACK=1),确认号为 ack= y + 1,序列号为 seq = x + 1,客户端和服务器进入ESTABLISHED状态,完成三次握手。

四次挥手

秋招面经总结_第7张图片
第一次挥手:客户端向服务端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待服务端的确认。
序列号 seq = u,即客户端上次发送的报文的最后一个字节的序号 + 1
确认号 ack = k, 即服务端上次发送的报文的最后一个字节的序号 + 1

第二次挥手:服务端收到连接释放报文后,立即发出确认报文(ACK=1),序列号 seq = k,确认号 ack = u + 1。这时 TCP 连接处于半关闭状态,即客户端到服务端的连接已经释放了,但是服务端到客户端的连接还未释放。这表示客户端已经没有数据发送了,但是服务端可能还要给客户端发送数据。

第三次挥手:服务端向客户端发送连接释放报文(FIN=1,ACK=1),主动关闭连接,同时等待 A的确认。
序列号 seq = w,即服务端上次发送的报文的最后一个字节的序号 + 1。
确认号 ack = u + 1,与第二次挥手相同,因为这段时间客户端没有发送数据

第四次挥手:客户端收到服务端的连接释放报文后,立即发出确认报文(ACK=1),序列号 seq =
u + 1,确认号为 ack = w + 1。此时,客户端就进入了 TIME-WAIT 状态。注意此时客户端到 TCP 连接还没有释放,必须经过2*MSL(最长报文段寿命)的时间后,才进入 CLOSED 状态。而服务端只要收到客户端发出的确认,就立即进入 CLOSED 状态。可以看到,服务端结束 TCP 连接的时间要比客户端早一些。

秋招面经总结_第8张图片

为什么建立连接协议是三次握手,而关闭连接却是四次握手呢?

服务器在收到客户端的 FIN 报文段后,可能还有一些数据要传输,所以不能马上关闭连接,但是会做出应答,返回 ACK 报文段.
接下来可能会继续发送数据,在数据发送完后,服务器会向客户单发送 FIN 报文,表示数据已经发送完毕,请求关闭连接。服务器的ACK和FIN一般都会分开发送,从而导致多了一次,因此一共需要四次挥手。

为什么TIME_WAIT状态还需要等2MSL后才能返回到CLOSED状态?

主要有两个原因:

  1. 确保 ACK 报文能够到达服务端,从而使服务端正常关闭连接。
    第四次挥手时,客户端第四次挥手的 ACK 报文不一定会到达服务端。服务端会超时重传 FIN/ACK报文,此时如果客户端已经断开了连接,那么就无法响应服务端的二次请求,这样服务端迟迟收不到 FIN/ACK 报文的确认,就无法正常断开连接。MSL 是报文段在网络上存活的最长时间。客户端等待 2MSL 时间,即「客户端 ACK 报文 1MSL 超时 + 服务端 FIN 报文 1MSL 传输」,就能够收到服务端重传的 FIN/ACK 报文,然后客户端重传一次 ACK 报文,并重新启动 2MSL 计时器。如此保证服务端能够正常关闭。如果服务端重发的 FIN 没有成功地在 2MSL 时间里传给客户端,服务端则会继续超时重试直到断开连接。

  2. 防止已失效的连接请求报文段出现在之后的连接中。TCP 要求在 2MSL 内不使用相同的序列号。客户端在发送完最后一个 ACK 报文段后,再经过时间2MSL,就可以保证本连接持续的时间内产生的所有报文段都从网络中消失。这样就可以使下一个连接中不会出现这种旧的连接请求报文段。或者即使收到这些过时的报文,也可以不处理它。

为什么不能用两次握手进行连接?

我们可以假设这样一种场景,如果是两次握手进行连接。客户端向服务器发送一次连接请求,这个请求并没有丢失,而是在网络中停留了很长时间,服务器迟迟没有发出确认应答,客户端重新发出连接请求,服务器应答并建立连接,传输数据后关闭。然后停留在网络中的连接请求又发送给服务器,服务器应答并建立连接,这就会导致不必要的错误和资源浪费。如果是三次握手进行连接,就算第一次滞留在网络中的连接请求发送给服务器,服务器应答,但是客户端并不会再次发送确认,所以不会发生错误。

如果已经建立了连接,但是客户端突然出现故障了怎么办?

TCP有一个保活计时器,每次收到客户端的请求,就会把这个计数器置零,如果等待时间超过一定时间,一般是两个小时,服务器就会开始发送探测报文段,75s一次,连续发送10个报文都没反应,服务器会关闭连接。

TCP报文格式

秋招面经总结_第9张图片

HTTP常见状态码

200:服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。
301 : (永久移动) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。
302:(临时移动) 服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求。
400 :客户端请求有语法错误,不能被服务器所理解。
403 :服务器收到请求,但是拒绝提供服务。
404 :(未找到) 服务器找不到请求的网页。
500: (服务器内部错误) 服务器遇到错误,无法完成请求。

HTTP请求报文和响应报文的格式

请求报文格式

  1. 请求行(请求方法+URI协议+版本)
  2. 请求头部
  3. 空行
  4. 请求主体
    秋招面经总结_第10张图片
    响应报文
  5. 状态行(版本+状态码+原因短语)
  6. 响应首部
  7. 空行
  8. 响应主体

秋招面经总结_第11张图片

请求体:
秋招面经总结_第12张图片

响应体:

响应体是服务器回写给客户端的页面正文,浏览器将正文加载到内存,然后解析渲染,显示页面内容

http和https

秋招面经总结_第13张图片优点

  • 安全性:
    1.使用HTTPS协议可认证用户和服务器,确保数据发送到正确的客户机和服务器;
    2.HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,要比http协议安全,可防止数据在传输过程中不被窃取、改变,确保数据的完整性。
    3.HTTPS是现行架构下最安全的解决方案,虽然不是绝对安全,但它大幅增加了中间人攻击的成本。
  • SEO方面:谷歌曾在2014年8月份调整搜索引擎算法,并称“比起同等HTTP网站,采用HTTPS加密
    的网站在搜索结果中的排名将会更高”。

缺点:

  • 在相同网络环境中,HTTPS 相比 HTTP 无论是响应时间还是耗电量都有大幅度上升。
  • HTTPS 的安全是有范围的,在黑客攻击、服务器劫持等情况下几乎起不到作用。
  • 在现有的证书机制下,中间人攻击依然有可能发生。
  • HTTPS 需要更多的服务器资源,也会导致成本的升高。

ARP和RARP

1.ARP:工作在网络层
A向B发送IP包,在报头需要填写B的IP为目的地址,但这个IP包在以太网上传输的时候,还需要进行一次以太包的封装,在这个以太包中,目的地址就是B的MAC地址
工作原理:
在A不知道B的MAC地址的情况下,A就广播一个ARP请求包,请求包中填写B的IP,以太网中的所有计算机都会接受这个请求,而正常的情况下只有B会给出ARP应答包,包中就填充上了B的MAC地址,并回复给A。
A得到ARP应答后,将B的MAC地址放入本机缓存中,便于下次使用。
本机MAC缓存是有生存期的,生存期结束后,将再次重复上面的过程。

2.RARP:允许局域网的物理机器从网关服务器的ARP表或者缓存上请求其IP地址
工作原理:
1.网络上的每台设备都有一个独一无二的硬件地址,通常是设备厂商分配的MAC地址,主机从网卡上读取MAC地址,然后在网络上发送一个RARP请求的广播数据包,请求RARP服务器回复该主机的IP地址。
2.RARP服务器收到了RARP请求数据包,为其分配IP地址,并将RARP回应发给主机
3.主机收到RARP回应后,就使用得到的IP地址进行通信。

五层协议中的攻击

ICMP fiood:不断地给目标发送ICMP数据包(伪造IP)
UDP flood:不断地给目标发送UDP数据包(伪造IP)

反射攻击:
发件人:攻击目标的IP
收件人:互联网上大量的第三方机器
把大量数据发到网上,回复数据就会涌入攻击目标

放大反射攻击:
攻击者不断地对DNS服务器发起查询请求,并把源地址伪造为攻击目标的IP,反射到攻击目标的数据量被放大到几十倍。

SYN flood:
在三次握手的过程中,服务器发送第二个包后,如果未收到客户端的ACK,则会不断地重发。SYN攻击就是通过伪造不存在的ip地址,向服务器不停地发SYN包,客户端回复SYN/ACK包,并等待客户端的确认,因为源地址不存在,所以服务器要不停地重发直至超时,这些伪造的SYN包将长时间地占用未连接队列,影响了正常的SYN,导致目标系统运行缓慢甚至瘫痪。

只发送SYN,不进行后续操作,对方不断重传(伪造IP)(杀敌1,自损10)
所以一般反射攻击。

防护

  • 通过防火墙、路由器等过滤网关防护。
  • 通过加固 TCP/IP 协议栈防范,如增加最大半连接数,缩短超时时间。

MAC和IP

  • 一、地址长度的不同

    1、MAC地址的长度为48位(6个字节),通常表示为12个16进制数,每2个16进制数之间用冒号隔开,如:00:50:29:5A:8H:1E就是一个MAC地址。

    2、IP地址为32位,由用点分隔开的4个8八位组构成,如192.168.0.1就是一个IP地址,这种写法叫点分十进制格式。

  • 二、所在寻址协议层上的区别

    1、MAC地址应用在OSI第二层,即数据链路层。数据链路层协议可以使数据从一个节点传递到相同链路的另一个节点上(通过MAC地址)。

    2、IP地址应用于OSI第三层,即网络层。网络层协议使数据可以从一个网络传递到另一个网络上(ARP根据目的IP地址,找到中间节点的MAC地址,通过中间节点传送,从而最终到达目的网络)。

  • 三、 分配依据不同。

    1、MAC地址的分配是基于制造商。

    MAC地址由网络设备制造商生产时写在硬件内部。这个地址与网络无关,也即无论将带有这个地址的硬件(如集线器、网卡、路由器等)接入到网络的何处,它都有相同的MAC地址,是不可变的。

    2、IP地址的分配是基于网络拓朴。

    IP地址由网络地址和主机地址两部分组成,分配给这两部分的位数随地址类(A类、B类、C类等)的不同而不同。

MAC地址是烧录在网卡或者接口上的物理地址,具有二层意义和全球唯一性,一般不能被改变。IP地址是网络中的主机或者三层接口在网络中的逻辑地址,在同一个网络内具有唯一性。

在浏览器中输入www.baidu.com后执行的全部过程?

  1. 域名解析(域名 www.baidu.com 变为 ip 地址)
    浏览器搜索自己的DNS缓存(维护一张域名与IP的对应表);若没有,则搜索操作系统的DNS缓存(维护一张域名与IP的对应表);若没有,则搜索操作系统的hosts文件(维护一张域名与IP的对应表)。
    若都没有,则找 tcp/ip 参数中设置的首选 dns 服务器,即本地 dns 服务器(递归查询),本地域名服务器查询自己的dns缓存,如果没有,则进行迭代查询。将本地dns服务器将IP返回给操作系统,同时缓存IP。
  2. 发起 tcp 的三次握手,建立 tcp 连接。浏览器会以一个随机端口(1024-5535)向服务端的 web程序 80 端口发起 tcp 的连接。
  3. 建立 tcp 连接后发起 http 请求。
  4. 服务器响应 http 请求,客户端得到 html 代码。服务器 web 应用程序收到 http 请求后,就开始处理请求,处理之后就返回给浏览器 html 文件。
  5. 浏览器解析 html 代码,并请求 html 中的资源。
  6. 浏览器对页面进行渲染,并呈现给用户。

以太网帧结构

秋招面经总结_第14张图片

  • 前同步码:用来使接收端的适配器在接收 MAC 帧时能够迅速调整时钟频率,使它和发送端的频率相同。前同步码为 7 个字节,1 和 0 交替。
  • 帧开始定界符:帧的起始符,为 1 个字节。前 6 位 1 和 0 交替,最后的两个连续的 1 表示告诉接收端适配器:“帧信息要来了,准备接收”。
  • 目的地址:接收帧的网络适配器的物理地址(MAC 地址),为 6 个字节(48 比特)。作用是当网卡接收到一个数据帧时,首先会检查该帧的目的地址,是否与当前适配器的物理地址相同,如果相同,就会进一步处理;如果不同,则直接丢弃。
  • 源地址:发送帧的网络适配器的物理地址(MAC 地址),为 6 个字节(48 比特)。
  • 类型:上层协议的类型。由于上层协议众多,所以在处理数据的时候必须设置该字段,标识数据交付哪个协议处理。例如,字段为 0x0800 时,表示将数据交付给 IP 协议。
  • 数据:也称为效载荷,表示交付给上层的数据。以太网帧数据长度最小为 46 字节,最大为 1500 字节。如果不足 46 字节时,会填充到最小长度。最大值也叫最大传输单元(MTU)。
  • 帧检验序列 FCS:检测该帧是否出现差错,占 4 个字节(32 比特)。发送方计算帧的循环冗余码校验(CRC)值,把这个值写到帧里。接收方计算机重新计算 CRC,与 FCS 字段的值进行比较。如果两个值不相同,则表示传输过程中发生了数据丢失或改变。这时,就需要重新传输这一帧。

DNS流程

  • 三级域名
  • 根服务器
  • 完整流程
    比如说在学校里有很多PC在上网,学校里有一个DNS服务器,学校里某一台PC访问www.baidu.com,这时先访问学校的DNS服务器,DNS服务器向最近的一台根服务器发送请求,根服务器返回.com服务器的地址,然后DNS服务器再向.com服务器发送请求(三级域名),.com服务器把百度的ip地址返回给DNS服务器,此时在DNS的高速缓存中记录IP地址,等下次访问PC直接从DNS服务器中查,省去了很多时间(默认保存2天);另外还可以手动设置DNS服务器(8.8.8.8 谷歌) 或者由路由器自动分配。

ICMP数据包格式

秋招面经总结_第15张图片

ping创建ICMP请求报文。ICMP报文被封装进IP数据包中,然后IP数据包被封装进以太网数据帧中

语言基础

Volatile关键字

加上 volatile 就会从内存那里读取内容,防止编译优化。还可以保障变量间不会乱序。

编译需要那几步,那几步的作用

预处理,编译,汇编,链接

预处理:处理头文件和编译指令(如define,ifndef)
编译:把预编译好的文件逐条转化为汇编语言.c文件
汇编:将.c文件编译成.o的二进制文件
链接:把代码和库函数进行连接,分为静态连接和动态链接
~ 静态连接:函数的代码从静态链接库中被拷贝到最终的可执行程序中。
~动态连接:链接程序此时所作的只是在最终的可执行程序中记录少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

C语言的malloc怎么使用

C语言中malloc是动态分配内存的函数,参数是分配的字节数,分配失败返回空指针NULL,返回成功返回指向被分配内存的指针,数据类型是void* 可以指向任何类型的数据,因此在必要时需要进行类型转换。在申请完空间之后要使用free对内存进行释放。

Malloc与calloc,realloc的区别

void* realloc(void* ptr, unsigned newsize);
void* malloc(unsigned size);
void* calloc(size_t numElements, size_t sizeOfElement);
malloc是申请一块大小为N的内存,返回首地址
calloc是申请n块长度为“size”字节的连续区域,返回首地址
realloc将ptr的内存大小增大到size

宏定义与内联函数的区别

宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。

内联函数

inline 对编译器来说只是个建议,取决于编译器。有点像宏但是比宏更安全,会做安全检查或自动类型转换。
相当于把内联函数里面的内容写在调用内联函数处。
没有函数调用的开销,但是会造成膨胀,占内存。

strlen 和 sizeof 区别

1.sizeof 是运算符,并不是函数,结果在编译时得到而非运行中获得;strlen 是字符处理的库函数。
2.sizeof 参数可以是任何数据的类型或者数据(sizeof 参数不退化);strlen 的参数只能是字符指针且结尾是’\0’的字符串。
3.因为 sizeof 值在编译时确定,所以不能用来得到动态分配(运行时分配)存储空间的大小。
Strlen是计算字符串的长度,不包括\0,sizeof是计算空间大小。

什么是虚函数,什么是纯虚函数,什么是抽象类?

虚函数是指一个类中希望重载的成员函数,当用基类指针或引用指向一个继承类对象时,调用一个虚函数,实际调用的是继承类的版本。

纯虚函数是一个在基类中说明的虚函数,它在基类里没有定义,要求任何派生类型都定义自己的版本。纯虚函数为各派生类提供一个公共接口,从基类继承来的纯虚函数,在派生类中仍是虚函数。

在C++中,含有纯虚函数的类称为抽象类,它不能生成对象。抽象类是不完整的,它只能用作基类。

虚函数的实现条件与原理

条件:
1.要有虚函数
2.虚函数的实现是依靠父类指针指向一个子类实例,然后通过父类指针调用子类的成员函数。
原理:
虚函数表,如果子类中有虚函数重写了父类的虚函数,对于派生类的实例,他的虚函数表结构如下: 重写虚函数,父类虚函数,子类虚函数,当我们通过父类指针调用重写虚函数时,父类指针的虚函数表中指向的虚函数的地址已被重写后的函数地址替换,所以实现了多态。

析构函数没有声明为虚析构函数的后果

当派生类对象经由一个基类指针被删除,而该基类带着一个non-virtual析构函数(即非虚析构函数),其结果是未有定义的,实际在执行时通常会发生的是对象的派生成分没有被销毁(即派生类的析构函数没有执行),这样就造成基类成分被销毁了,但是派生类成分没有被销毁,可能会形成资源泄漏、败坏之数据结构,在调试器上浪费很多时间进行调试。

内存泄漏的原因

内存泄露的原因

1.在类的构造函数和析构函数中没有匹配的调用new和delete函数

2.没有正确地清除嵌套的对象指针

3.在释放对象数组时在delete中没有使用方括号
方括号是告诉编译器这个指针指向的是一个对象数组,同时也告诉编译器正确的对象地址值并调用对象的析构函数,如果没有方括号,那么这个指针就被默认为只指向一个对象,对象数组中的其他对象的析构函数就不会被调用,结果造成了内存泄露。如果在方括号中间放了一个比对象数组大小还大的数字,那么编译器就会调用无效对象(内存溢出)的析构函数,会造成堆的奔溃。如果方括号中间的数字值比对象数组的大小小的话,编译器就不能调用足够多个析构函数,结果会造成内存泄露。
释放单个对象、单个基本数据类型的变量或者是基本数据类型的数组不需要大小参数,释放定义了析构函数的对象数组才需要大小参数。

4.指向对象的指针数组不等同于对象数组
对象数组是指:数组中存放的是对象,只需要delete []p,即可调用对象数组中的每个对象的析构函数释放空间
指向对象的指针数组是指:数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的空间,delete []p只是释放了每个指针,但是并没有释放对象的空间,正确的做法,是通过一个循环,将每个对象释放了,然后再把指针释放了。

5.缺少拷贝构造函数

6.没有将基类的析构函数定义为虚函数

深拷贝和浅拷贝

浅拷贝只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针

结构体对齐的原则

1:结构体成员的起始位置都必须要从第一个成员的大小的整数倍位置开始
2:结构体的大小即sizeof得到的结果必须是结构体最大成员的整数倍
3:如果结构体的成员中还包含结构体成员,那么成员结构体的每一个成员的起始地址都要从该成员结构体内部最大成员的整数倍位置开始
4:#pragma pack()中的值与该数据类型的sizeof()索取得的值两个要取最小值作为其偏移值

为什么要内存对齐

1.平台原因:
不是所有的硬件平台都能访问任意地址上的任意数据的
2.性能原因
数据结构应该尽可能地在自然边界上对齐
为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存仅需要一次访问

堆和栈的区别

1)堆和栈中的存储内容:栈存局部变量、函数参数等。堆存储使用new、malloc申请的变量等;

2)申请方式:栈内存由系统分配,堆内存由自己申请;

3)申请后系统的响应:栈——只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。
堆——首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表 中删除,并将该结点的空间分配给程序;

4)申请大小的限制:Windows下栈的大小一般是2M,堆的容量较大;

5)申请效率的比较:栈由系统自动分配,速度较快。堆使用new、malloc等分配,较慢;

总结:栈区优势在处理效率,堆区优势在于灵活;

New和malloc区别?

1.malloc 和 free 都是函数。 new 和 delete 是C++ 的运算符!

2.malloc 用 分配内存不会自动调用构造函数, new 就会。

3.malloc 分配的空间的大小必须指定, new会自动分配。

4.malloc 和 new 分配的内存都在堆上面。

5.malloc 分配空间失败会返回 空指针NULL 而 new 分配失败了会抛出std::bad_alloc异常。

6.不管是new 或者 malloc 分配的空间之后, 都应该记得释放分配的内存。 因为系统不会自动释放你申请的空间, 空间在堆上面。

C和C++的内存分区:

1)、栈区(stack)— 由编译器自动分配释放 ,存放函数的参数值,局部变量的值等。其
操作方式类似于数据结构中的栈。
2)、堆区(heap) — 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收 。注意它与数据结构中的堆是两回事,分配方式倒是类似于链表。
3)、静态存储区(static)存放全局变量和静态变量的存储区,初始化的变量放在初始化区,未初始化的变量放在未初始化区。在程序结束后释放这块空间
一块区域。 - 程序结束后由系统释放。
4)、文字常量区 —常量字符串就是放在这里的。 程序结束后由系统释放。
5)、程序代码区—存放源程序的二进制代码。

static,static局部变量?生命周期?static关键字(全局,局部,成员变量,成员函数)

1.局部静态变量
修改变量的存储区域和生命周期,使变量存储在静态区,在main函数运行前就分配了空间,如果有初始值就用初始值初始化它,如果没有初始值系统用默认值初始化它。在每次调用时,其值为上一次调用后改变的值,调用结束后不释放空间。

2. 全局静态变量
全局静态变量也是储存在静态区,但是它的作用域仅为本文件。
对于全局变量使用static,可以多个文件中使用相同名字的全局变量而不会发生名字冲突。
3. 静态函数
在函数定义前面加上static,限定该函数只在当前文件中可见。
这样不同的文件可以使用相同名字的函数而不会发生混淆。
4.静态数据成员
修饰成员变量,该类的所有的对象只保存一个该变量,而且不需要生成对象就可以访问该成员。
5.静态成员函数
修饰类中的成员函数,不需要生成对象就可以访问该函数,但是在static函数内不能访问非静态成员。

重载 重写区别

重载 函数名相同,函数参数,函数参数个数,函数参数顺序至少有一个不同。函数的返回值类型可以相同也可以不同,发生在一个类的内部,不能跨作用域。
重写,一般发生在子类和父类继承关系之间。子类重新定义父类中有相同名称和参数的虚函数。

指针和引用区别

(1)当引用被创建时,它必须被初始化。而指针则可以在任何时候被初始化。
(2)一旦一个引用被初始化为指向一个对象,它就不能被改变为对另一个对象的引用。而指针则可以在任何时候指向另一个对象。
(3)不可能有NULL引用。必须确保引用是和一块合法的存储单元关联。

智能指针

auto_ptr 管理权的转移 ->不建议使用
unique_ptr 防拷贝 ->简单粗暴
shared_ptr 利用引用计数,每有一个指针指向相同的一片内存时,引用计数+1,每有一个指针取消指向一片内存时,引用计数-1.为0时释放内存
week_ptr 弱指针 ->辅助shared_ptr解决循环引用的问题。

悬挂指针和野指针

野指针指申请了没有初试化的指针。
悬挂指针指曾经初始化过的指针,但是delete了指针指向的内容,该指针未置空。

指针函数 函数指针

指针函数: 顾名思义,它的本质是一个函数,不过它的返回值是一个指针。
函数指针 的本质是一个指针,该指针的地址指向了一个函数,所以它是指向函数的指针。
int *fun(int x,int y); 指针函数
int (*fun) (int x, int y) 函数指针

区别就是括号的优先级
定义一个f
Int * f(int x, inty); -----------1
Int (*f)(int x, int y); -----------2
1中的f是一个函数,所以f是一个返回值为指针的函数。
2中得f是一个指针,所以f是一个指向函数的指针。

堆栈溢出一般是由什么原因引起的

1.函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回。再有,当函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出。
2.动态申请空间使用之后没有释放。由于C语言中没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。申请的动态空间使用的是堆空间,动态空间使用不会造成堆溢出。
3.数组访问越界。C语言没有提供数组下标越界检查,如果在程序中出现数组下标访问超出数组范围,在运行过程中可能会内存访问错误。
4.指针非法访问。指针保存了一个非法的地址,通过这样的指针访问所指向的地址时会产生内存访问错误。

操作系统

进程与线程

调度:进程是资源管理的基本单位,线程是程序执行的基本单位。
切换:线程上下文切换比进程上下文切换要快得多。
拥有资源: 进程是拥有资源的一个独立单位,线程不拥有系统资源,但是可以访问隶属于进程的资
源。
系统开销: 创建或撤销进程时,系统都要为之分配或回收系统资源,如内存空间,I/O设备等,OS
所付出的开销显著大于在创建或撤销线程时的开销,进程切换的开销也远大于线程切换的开销。
健壮性:一个线程崩溃可能会引起整个进程崩溃,而一个进程崩溃不会影响其他进程。

线程之间共享哪些资源

线程共享的环境包括 进程代码段 进程的公有数据(利用这些共享的数据 线程很容易的实现相互之间的通讯) 进程打开的文件描述符 信号的处理器 进程的当前目录和进程用户ID与进程组ID

进程间通信方式(详细)

(1)管道(pipe)及有名管道(named pipe):管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。
(2)信号(signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一致的。
(3)消息队列(message queue):消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息。
(4)共享内存(shared memory):可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。
(5)信号量(semaphore):主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段。
(6)套接字(socket):这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛

线程间通信方式的简要介绍

信号量:初始化值为 N 的互斥量。 N值,表示可以同时访问共享数据区的线程数
sem_wait()函数相当于加锁 一次调用,做一次-- 操作, 当信号量的值为 0 时,再次 – 就会阻塞。
sem_post()函数相当于解锁 一次调用,做一次++ 操作. 当信号量的值为 N 时, 再次 ++ 就会阻塞。

读写锁:读时共享,写时独占,写锁的优先级高,相较于互斥量而言,当读线程多的时候,提高访问效率。
什么叫读时共享,写时独占(如果几个线程同时读共享数据,都可以访问,如果几个线程有读有写,则先写;如果已经读在加锁,然后又来读和写,则等读完之后,先写再读)

条件变量:条件变量通常结合锁来使用,当条件不满足的时候,条件变量会解锁已有的互斥锁并阻塞等待,一旦条件满足,就解除阻塞,重新加锁,访问共享资源
互斥锁:多个线程要访问同一个共享资源,首先要加锁,得到锁的那一个线程才能访问共享资源。
自旋锁:发生阻塞时,自旋锁会让CPU一直去循环请求锁的权限

谈一下锁机制

为什么有锁机制:多个线程访问同一个共享资源,可能会造成各线程之间相互覆盖共享资源,造成数据访问不一致的现象。为了避免这种现象,就需要锁机制。

死锁条件:首先有线程和资源,每个线程都在等待其中一个资源,但所有的资源都被占用。所有的线程都在相互等待,但他们永远不会释放已经占有的资源,于是任何线程都无法继续,死锁发生。

内存的三种分配方式

1.从静态存储区分配:此时的内存在程序编译的时候已经分配好,并且在程序的整个运行期间都存在。全局变量,static变量等在此存储。
2.在栈区分配:相关代码执行时创建,执行结束时被自动释放。局部变量在此存储。栈内存分配运算内置于处理器的指令集中,效率高,但容量有限。
3.在堆区分配:动态分配内存。用new/malloc时开辟,delete/free时释放。生存期由用户指定,灵活。但有内存泄露等问题。

死锁的四个条件

产生死锁的必要条件:
1.互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
2.请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不可剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
4.环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。

死锁的两个实例

  1. 对一个锁加锁两次
  2. 线程A拥有锁1,线程B拥有锁2,线程A请求锁2,线程B请求锁1.

Linux文件类型

1.普通文件
2.目录文件
3.链接文件
4.设备文件
5.管道文件

用户态和内核态

1.区别(执行权限区分)
(1).两者以资源操作权限为区分,执行权限不一样,相互协作,用户态无法调度内核态,内核态也不干涉用户态
(2)运行空间不一样:标准Linux0-3G是一个作为有用户态。3-4G内核态作为使用;用户态共享内核态;
两者不可以互相直接访问,如果访问会出现访问非法地址或内核崩溃!

2.内核态可以通过什么方式访问用户态?
临时改变内核空间,3-4G编程0-4G改变,访问完恢复改变;
用户态正常工作是如何进入内核态?
系统调用(不是C的库调用),
中断(保存现场,进入中断上下文,上下文是处在内核态返回),
异常(会发信息给内核)

3.两者如何通讯?(I/Ocontrol,sysctl动态修改内核参数,socket,procfs进程文1.API函数,get_user(x, ptr) 在内核中被调用,获取用户空间指定地址的数值并保存到内核变量x中。
put_user(x, ptr) 在内核中被调用,将内核空间的变量x的数值保存到到用户空间指定地址处。
2. proc文件系统
3.netlink 用户态和内核态之间的进程间通信。
4.使用文件,当处于内核空间的时候,直接操作文件,将想要传递的信息写入文件,用户空间读取这个文件就可以得到想要的数据了。
5.mmap系统调用,将内核空间的地址映射到用户空间。

进程状态

进程状态:
初始态,就绪态,运行态,挂起态与终止态。
秋招面经总结_第16张图片

线程状态:
就绪:准备等待可用的CPU资源,其他条件一切准备好。当线程被pthread_create创建时或者阻塞状态结束后就处于准备状态。
运行:线程已经获得CPU的使用权,并且正在运行,在多核心的机器中同时存在多个线程正在运行。如果这种情况不加以控制,会造成整个程序没响应。
阻塞:指一个线程在执行过程中暂停,以等待某个条件的触发。
例如线程可能在处理有关I/O的任务,可能I/O设备繁忙尚未响应或没有可用的I/O缓存。
也可能当前线程等待一个可用的条件便来变量。
错误地对一个已被锁住的互斥量加锁
调用sigwait等待尚未发生的信号。
终止:线程已经从回调函数中返回,或者调用pthread_exit返回,或者被强制终止。

ps aux 和top

ps命令是系统在过去执行的进程的静态快照
top命令反应的是系统进程动态信息,默认10s更新一次

驱动是如何加载的?

动态加载
编译驱动程序,然后添加
静态加载
在内核中添加删除驱动,重新编译内核

动态链接和静态链接的优缺点,链接库位于哪个存储区
重写与重载有什么区别?
static的作用? static函数能不能访问其他非static的变量?为什么?

虚拟内存机制

在早期的计算机中,没有虚拟内存的概念,我们要运行一个程序,要把程序全部装入内存,然后运行,在运行多个程序后,会出现很多问题。由于指令都是直接访问物理内存的,那么进程就可以修改其他进程的数据,甚至会修改内核地址空间的数据,而内存是随机分配的,所以程序运行的地址也是不正确的。
为了解决这个问题,虚拟内存就被创造出来,
对于32位的系统,每一个进程运行时都会得到4G的虚拟内存,进程认为他得到的这4G的虚拟空间是一个连续的地址空间,实际上,它通常是被分隔成多个物理内存碎片,还有一部分存储在外部磁盘存储器上,在需要时进行数据交换。
每次程序要访问地址空间上的某一个地址,都需要把地址翻译为实际物理内存地址
所有进程共享这整一块物理内存,每个进程只把自己目前需要的虚拟地址空间映射到物理内存上
进程需要知道哪些地址空间上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,这就需要通过页表来记录。
页表的每一个表项分为两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)。
当进程访问某个虚拟地址的时候,就会先看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常。
发生缺页异常时,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就由操作系统决定覆盖某页。

孤儿进程、僵尸进程

孤儿进程:
一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为 1)所收养,并由 init 进程对它们完成状态收集工作。
僵尸进程:
僵尸进程: 子进程死亡,而父进程没有进行回收,此时进程变为僵尸态,解决 kill 该进程。

linux中遇到文件无法删除怎么解决

普通的删除指令 rm -rf file.sh
如果遇到无法删除 先查看文件属性 lsattr
i 属性设置之后可使文件不能被删除、改名,设置连接也无法写入或添加数据,只有 root 用户才能设置
a 属性设置之后,文件只能增加数据,既不能删除也不能修改数据,只有 root 用户才能设置
如果被设置为 a或 i 属性 首先要修改文件属性
chattr -ia
修改属性即可后删除。

linux基本命令

ls 列出当前文件夹下目录项
ls –l 显示目录项的详细信息
cd 打开摸个目录
cd … 打开上一个目录
cd ~ 打开当前用户目录
cd / 进入根目录
ps aux 查看所有进程
top 任务管理器 top后按数字键盘1 查看每个逻辑cpu的状态 x 排序高亮 shift+< 或shift+> 切换排序关键字。 Shift + m 根据内存大小排序 shift + p 根据CPU占用排序
pwd 查看当前目录
mkdir 新建目录
touch 常见空文件
rm 删除文件
rmdir 删除目录
chmod 改变文件状态
shutdown –h now 立即关机
tree 查看当前文件结构树
cat 查看文件内容 cat filename
tac 倒叙查看文件内容 tac filename
mv file1 file2 location
将文件1和文件2移动到目标位置
cp filename dirname 复制文件到目录
cp filename1 filename2 复制文件1并重命名为文件2
cat filename 查看文件内容
tac filename 逆转查看文件内容
软链接就像windows下的快捷方式
Linux下的软链接行为和windows下的快捷方式差不多,但是如果是用相对路径创建的软链接,在软链接移动之后就会失效,无法访问。这一点和windows快捷方式不同,windows快捷方式随便放哪里都行。
chmod 修改权限操作

查找文件中语句所在的那一行

grep命令
文件内容为
秋招面经总结_第17张图片

在test.txt文件中,筛选包含mother的行
grep mother test.txt
在这里插入图片描述

在test.txt文件中,筛选不包含mother的行,加上-v参数即可:
grep -v mother test.txt
秋招面经总结_第18张图片

Linux中断的响应流程

处理器收到来自中断控制器的中断处理请求,保存中断上下文,跳转到中断对应的处理处,(快速完成中断中断上半部,中断上半部返回后执行中断下半部。如果做了上下半部处理的话),中断处理函数返回时恢复现场。

Linux系统启动流程

1).开机bios自检,加载硬盘
2).读取MBR,进行MBR引导
3).boot loader
4).加载内核 kernel
5).启动init进程,依据inittab文件设定运行级别
6).init进程,执行rc.sysinit文件
7).启动内核模块,执行不同级别的脚本程序
8).执行/etc/rc.d/rc.local
9).启动mingetty,进入系统登录界面

编译需要哪几步,那几步的作用

秋招面经总结_第19张图片

1:预编译:预编译做的事情为:把伪指令转换为实际指令命令 gcc –E
  a:#define a b
  b:#条件编译指令,如#ifdef,#ifndef,#else,#elif,#endif等
  c:#include 头文件加入到编译的文件中
  d:一些符号处理如file local 等等;

2:编译    命令是  gcc -S
  把预编译好的文件逐条转化为汇编语言
  优化阶段,经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,
  以及c语言的关键字,如main,if,else,for,while,{,}, +,-,*,\等等。
  编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,
  将其翻译成等价的中间代码表示或汇编代码。如下都是汇编代码;操作寄存器

3.汇编  命令gcc -c
  将.c文件直接编译成.o的二进制文件:

4:链接 命令是 gcc *.c    链接命令是ld 链接的时候要考虑代码和数据所要放的内存位置,可以通过链接脚本来设置(这里下次课再说)

根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

(1)静态链接

在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

(2) 动态链接

在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

GCC 编译参数

-lpthread 链接阶段,链接这个库
-lm 链接数学库
如果你的程序中使用dlopen、dlsym、dlclose、dlerror 显示加载动态库,需要设置链接选项 –ldl

-c 只编译并生成目标文件。
-g 生成调试信息。GNU 调试器可利用该信息。
-o FILE 生成指定的输出文件。用在生成可执行文件时。
-O0 不进行优化处理。
-O 或 -O1 优化生成代码。
-O2 进一步优化。
-O3 比 -O2 更进一步优化,包括 inline 函数。
-w 不生成任何警告信息。

GDB调试

基础指令:
-g:使用该参数编译可以执行文件,得到调试表。

gdb ./a.out

list: list 1  列出源码。根据源码指定 行号设置断点。

b:	b 20	在20行位置设置断点。

run/r:	运行程序

n/next: 下一条指令(会越过函数)

s/step: 下一条指令(会进入函数)

p/print:p i  查看变量的值。

continue:继续执行断点后续指令。

finish:结束当前函数调用。 

quit:退出gdb当前调试。

bt:打印堆栈信息

info break 查看断点

GDB多线程调试

  1. Makefile 上加上 -g参数生成可调试信息,可以进行调试
  2. pthread不是linux下默认的库,也就是连接的时候,无法找到pthread库中函数的入口地址,连接会失败,在makefile中加上-lpthread参数即可解决。
  3. gdb text 进入调试
  4. l 15 列出15行源码
  5. Info thread 可以查看被调试的线程。显示当前可调试的所有线程,每个线程会有一个GDB为其分配的ID,后面操作线程的时候会用到这个ID。 前面有*的是当前调试的线程。
  6. set scheduler-locking off|on|step
    在使用step或continue命令调试当前被调试线程的时候,其他线程也是同时执行的,如果我们只想要被调试的线程执行,而其他线程停止等待,那就要锁定要调试的线程,只让它运行。
    off:不锁定任何线程,所有线程都执行。
    on:只有当前被调试的线程会执行。
    step:阻止其他线程在当前线程单步调试的时候抢占当前线程。只有当next、continue、util以及finish的时候,其他线程才会获得重新运行的。

Select 、poll、epoll 区别

1、支持一个进程所能打开的最大连接数
select
单个进程所能打开的最大连接数有FD_SETSIZE宏定义,其大小是32个整数的大小(在32位的机器上,大小就是3232,同理64位机器上FD_SETSIZE为3264),当然我们可以对进行修改,然后重新编译内核,但是性能可能会受到影响,这需要进一步的测试。
poll
poll本质上和select没有区别,但是它没有最大连接数的限制,原因是它是基于链表来存储的
epoll
虽然连接数有上限,但是很大,1G内存的机器上可以打开10万左右的连接,2G内存的机器可以打开20万左右的连接
2、FD剧增后带来的IO效率问题
select
因为每次调用时都会对连接进行线性遍历,所以随着FD的增加会造成遍历速度慢的“线性下降性能问题”。
poll
同上
epoll
因为epoll内核中实现是根据每个fd上的callback函数来实现的,只有活跃的socket才会主动调用callback,所以在活跃socket较少的情况下,使用epoll没有前面两者的线性下降的性能问题,但是所有socket都很活跃的情况下,可能会有性能问题。
3、 消息传递方式
select
内核需要将消息传递到用户空间,都需要内核拷贝动作
poll
同上
epoll
epoll通过内核和用户空间共享一块内存来实现的。
4.移植性
Select 全平台
Epoll只能在linux平台使用

多线程与多进程

多进程并发服务器的设计思路

  1. socket
  2. bind
  3. listen
  4. while(1)
    {
    Accept
    Fork
    If(pid = 0)
    {
    关闭lfd
    读写操作
    }
    If(pid > 0)
    {
    Close(cfd)
    Continue;
    }
    }

多线程并发服务器的设计思路

  1. socket
  2. bind
  3. listen
  4. while(1)
    {
    Cfd = accept
    Pthread_create
    Pthread_detach
    }
    //子线程
    {

读写操作
Close(cfd)
线程退出
}

子线程,父线程分辨

Int pid = Fork()
Pid > 0 父进程
Pid = 0 子进程

多个客户端和一个服务端连接的情况

多进程
多线程

Select
1.Socket
2.Bind
3.listen
Fd_set rset allset
FD_ZERO(r_set)
FD_SET(lfd, r_set)
While(1)
{
R_set = all_set
Ret = select(最大文件描述符 + 1, &r_set)
If(r_set > 0)
{
If(isset(lfd))
{
Cfd = accept
Fd_set
}
For(int I = lfd + 1; I < 最大文件描述符; i++)
{
Fd_isset(I, &r_set)
{
读写
}

}
}
}
Poll
Epoll

具体讲一讲socket编程的步骤

TCP:

S:
Socket
Bind
Listen
Accept
{
Read
write
}
Close

C:
Socket
Bind
Connect
{
Read
write
}
close

UDP:

S:
Socket
Bind
Listen
{
	Recfrom
	sendto
}
Close

C:
	Socket
	{
Sendto
	Recfrom
}
Close

嵌入式相关

不能用 sizeof()函数,如何判断操作系统是16位,还是32位

16位系统中,int变量的范围-32768到+32767,32767+1变为-32768。可以利用这个特性来判断。
32767的二进制表示 0111 1111 1111 1111
32767 + 1 为 1000 0000 0000 0000
而-32768在二进制中的补码为 1000 0000 0000 0000
-32768的绝对值为 32768 二进制码为 1000 0000 0000 0000
每位取反 0111 1111 1111 1111
反码加一 1000 0000 0000 0000
刚好就是-32768

大小端模式

0x667788 88为低字节, 66为高字节
大端模式:高地址放低字节
小端模式:低地址放低字节
ARM默认小端,X86小端,C51大端
共用体(union)时要注意
以太网通信,软件的移植也要注意

为什么有大小端之分

对于位数大于8位的处理器,例如16位或者32位的处理器,由于寄存器宽度大于一个字节,那么必然存在着一个如何将多个字节安排的问题。因此就导致了大端存储模式和小端存储模式。

判断链表有环

秋招面经总结_第20张图片

你可能感兴趣的:(面经,c语言,c++)