深信服

一、

1.多态

C++多态(polymorphism)是通过虚函数来实现的,虚函数允许子类重新定义成员函数。最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,实现一个接口,多种方法,称为多态

2.静态重载发生在哪个阶段?

重载发生在编译时

3.数组和链表的区别

数组

是一块连续的内存区域,在使用的时候需要预先申请空间(数组的空间在编译器就需要进行确定,所以需要提前给出数组的大小,在运行期间不允许改变)

数组插入的时候,待插入位置的的元素和它后面的所有元素都需要向后搬移;

删除数据时,待删除位置后面的所有元素都需要向前搬移;随机访问效率很高,时间复杂度O(1);

数组不够的时候需要进行扩容,扩容的话涉及到需要把旧数组的所有元素向新数组中copy,数组的空间是从栈分配的

链表

可以在内存的任何地方,空间是分散的,不需要连续,使用的时候不需要预先分配空间,使用的时候动态申请

链表的元素会有两个属性,一个是元素的值,一个是元素的索引,一个是元素的值,此索引指向下一个元素的地址

查找数据时效率低,不具有随机访问特性,需要从第一个元素查起

4.vector和list的区别

vector和数组类似,拥有一段连续的内存空间,并且起始地址不变。
因此能高效的进行随机存取,时间复杂度为o(1);
但因为内存空间是连续的,所以在进行插入和删除操作时,会造成内存块的拷贝,时间复杂度为o(n)。
另外,当数组中内存空间不够时,会重新申请一块内存空间并进行内存拷贝。

list是由双向链表实现的,因此内存空间是不连续的。
只能通过指针访问数据,所以list的随机存取非常没有效率,时间复杂度为o(n);
但由于链表的特点,能高效地进行插入和删除。

5.什么数据结构增删改查都快

我们都知道数组的查询比较快,因为地址是连续的,可以通过下标直接找到元素。不过增删较慢,每次在数组中间增删一个元素,都涉及数组拷贝。
HashMap设计高明之处,是用hashCode代替数组下标访问,不再要求元素存储必须连续,增删的时候也就不需要数组拷贝。
使用链表解决hash冲突,使用阈值默认75%控制容量,避免元素过多、hash冲突过多,退化成链表存储,影响查询性能。HashMap通常情况下,增删改查的效率都是O(1)

6.什么是B+树?

B+树是一B树的一个升级版

  • 有n棵子树的非叶子结点中含有n个关键字(b树是n-1个),这些关键字不保存数据,只用来索引,所有数据都保存在叶子节点(b树是每个关键字都保存数据)
  • 所有的叶子结点中包含了全部关键字的信息,及指向含这些关键字记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接
  • 通常在b+树上有两个头指针,一个指向根结点,一个指向关键字最小的叶子结

b+树相比于b树的查询优势

  1. b+树的中间节点不保存数据,所以磁盘页能容纳更多节点元素,更“矮胖”;
  2. b+树查询必须查找到叶子节点,b树只要匹配到即可不用管元素位置,因此b+树查找更稳定(并不慢);
  3. 对于范围查找来说,b+树只需遍历叶子节点链表即可,b树却需要重复地中序遍历,如下两图:

 

7.FIN包发送失败会怎么样?

重传?

8.服务器接收成功了三个包?

 

9.IO多路复用?

selectpoll,epoll

10.epoll介绍

epoll分为三个部分:

1:调用epoll_create()建立一个epoll对象(在epoll文件系统中为这个句柄对象分配资源)

2:调用epoll_ctl向epoll对象中添加这100万个连接的套接字

3:调用epoll_wait收集发生的事件的连接

epoll向内核注册了一个文件系统,用于存储上述的被监控socket,这些socket会以红黑树的形式存在,同时会建立一个list链表,用于存储准备就绪的事件.

执行epoll_create时,创建了红黑树和就绪链表,执行epoll_ctl时,如果增加socket,则检查在红黑树中是否存在,存在立即返回,不存在则添加到树干上,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。

 

11.为啥epoll_wiait不用轮询

检测到描述符事件发生并将此事件通知应用程序;

当我们执行epoll_ctl时,除了把socket放到epoll文件系统里file对象对应的红黑树上之外,还会给内核中断处理程序注册一个回调函数,然后向内核注册回调函数,用于当中断事件来临时向准备就绪链表中插入数据。执行epoll_wait时立刻返回准备就绪链表里的数据即可。

执行epoll_wait时立刻返回准备就绪链表里的数据即可。

二、

1.多态

C++多态(polymorphism)是通过虚函数来实现的,虚函数允许子类重新定义成员函数。最常见的用法就是声明基类的指针,利用该指针指向任意一个子类对象,调用相应的虚函数,实现一个接口,多种方法,称为多态

2.虚函数

定义他为虚函数是为了允许用基类的指针来调用子类的这个函数

3.c++11的特性

关键字以及新语法:

1.auto关键字

2.for循环

3.nullptr

STL容器:

1.array (和数组没有太大区别)

2.forward_list新增的线性表,与list的区别他是单向链表。

3.unorderd_map:内部是hash表的实现方式

4.unordered_set也是哈希表但是不会自动排序

多线程:

1.thread

2.atmoic原子变量

3.condition_variable

condition_variable就像Linux下使用pthread_cond_wait和pthread_cond_signal一样,可以让线程休眠,直到别唤醒,现在在从新执行。线程等待在多线程编程中使用非常频繁,经常需要等待一些异步执行的条件的返回结果。

4.智能指针

4.智能指针

智能指针只是用对象去管理一个资源指针,同时用一个计数器计算当前指针引用对象的个数,当管理指针的对象增加或者减少时,计数器也相应的+1或者-1;

shared_ptr:可以多个指针引用一个对象,采用引用计数,每有一个指针引用一个对象,引用一次,计数加1,每当智能指针销毁了,引用计数减1,引用计数减少到0,就释放引用的对象,这种引用计数的增减发生在智能指针的构造函数,复制构造函数,赋值操作符,析构函数中。

weak_ptr:只是一个观察者,配合shared_ptr共同工作,weak_ptr构造不会引起引用计数的增加,析构不会引起引用计数的减少;weak_ptr是和shared_ptr配合使用的,在实现shared_ptr的时候也就考虑了weak_ptr的因素。weak_ptr是shared_ptr的观察者,它不会干扰shared_ptr所共享对象的所有权,当一个weak_ptr所观察的shared_ptr要释放它的资源时,它会把相关的weak_ptr的指针设置为空,防止weak_ptr持有悬空的指针。

注意:weak_ptr并不拥有资源的所有权,所以不能直接使用资源。可以从一个weak_ptr构造一个shared_ptr以取得共享资源的所有权

unique_ptr:独占型的智能指针,不允许其他的智能指针共享其内部的指针,不允许赋值和拷贝,只支持移动语义

5.STL容器  哈希表

 

 

6.排序算法   堆排序和快排  时间复杂度

 

深信服_第1张图片

7.如何从一篇文章中找到频率最高的词汇

1、海量日志数据,提取出某日访问百度次数最多的那个IP。
    既然是海量数据处理,那么可想而知,给我们的数据那就一定是海量的。针对这个数据的海量,我们如何着手呢?对的,无非就是分而治之/hash映射 + hash统计 + 堆/快速/归并排序,说白了,就是先映射,而后统计,最后排序:

  1. 分而治之/hash映射:针对数据太大,内存受限,只能是:把大文件化成(取模映射)小文件,即16字方针:大而化小,各个击破,缩小规模,逐个解决
  2. hash_map统计:当大文件转化了小文件,那么我们便可以采用常规的hash_map(ip,value)来进行频率统计。
  3. 堆/快速排序:统计完了之后,便进行排序(可采取堆排序),得到次数最多的IP。

   具体而论,则是: “首先是这一天,并且是访问百度的日志中的IP取出来,逐个写入到一个大文件中。注意到IP是32位的,最多有个2^32个IP。同样可以采用映射的方法,比如%1000,把整个大文件映射为1000个小文件,再找出每个小文中出现频率最大的IP(可以采用hash_map对那1000个文件中的所有IP进行频率统计,然后依次找出各个文件中频率最大的那个IP)及相应的频率。然后再在这1000个最大的IP中,找出那个频率最大的IP,即为所求。

 

8.链表翻转

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL)return head;
     
        ListNode *pre=head,*cur=head->next;
        ListNode* temp;

        while(cur!=NULL){
            if(pre==head){
                pre->next=NULL;
            }
            temp = cur->next;

            cur->next = pre;

            pre = cur;
            cur = temp;
        }

        return pre;

    }
};
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        if(head==NULL||head->next==NULL) return head;

        ListNode *node=reverseList(head->next);
        head->next->next = head;
        head->next=NULL;

        return node;
    }
};

 

9.socket套接字

TCP:

服务端:

socket():套接字初始化,用于创建一个socket描述符(socket descriptor),它唯一标识一个socket

bind():绑定socket和地址

listen():监听端口

accept():listen队列中接收一个连接

recv()send():读写函数

close():关闭连接

客户端:

connect():发起连接

send()recv():读写函数

close():关闭连接

总结:
TCP Server端    :create --> bind --> listen -->  accept -->  recv/send --> close
TCP Client端     :create --> conncet --> send/recv --> close.

UDP:

. 服务器端:
    1)创建套接字create;
    2)绑定端口号bind;
    3)接收/发送消息recvfrom/sendto;
    4)关闭套接字。

2. 客户端:
    1)创建套接字create;
    2)发送/接收消息sendto/recvfrom;
    3)关闭套接字.

总结:
UDP Server端 :create --> bind --> recvfrom/sendto -- >close
UDP Client端  :create --> sendto/recvfrom --> close.

 

10.进程通信方式

1:共享内存

进程通信最快的一种方式,允许两个进程或者更多进程同时访问同一块内存,就如果malloc向不同进程返回了指向了同一个物理内存区域的指针,当一个进程改变了这块地址中的内容的时候,其他进程都会感应到

2:管道通信

分为有名管道(pipe)和无名管道(FIFO)

有名管道(pipe):只支持单向数据流,只能用于具有亲缘关系的进程之间

管道的实质是一个内核缓冲区,进程以先进先出的方式从缓冲区存取数据,管道一端的进程顺序的将数据写入缓冲区,另一端的进程则顺序的读出数据

为了克服只能用于亲缘关系的进程通信,提出了有名管道,

有名管道不同于匿名管道之处在于它提供了一个路径名与之关联,以有名管道的文件形式存在于文件系统中,这样,即使与有名管道的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过有名管道相互通信,因此,通过有名管道不相关的进程也能交换数据。有名管道的名字存在于文件系统中,内容存放在内存中

3.消息队列

存放在内核中的消息链表,每个消息队列由消息队列标识符表示,与管道不通的是消息队列放在内核中(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统),只有等内核重启,或者显示的删除一个消息队列的时候,该队列才会被真正的额删除。

4.socket  异机通信

5.信号(信号是软件层次上对中断机制的一种模拟,是一种异步通信方式)

信号是Linux系统中用于进程间互相通信或者操作的一种机制,信号可以在任何时候发给某一进程,而无需知道该进程的状态

11.子进程没回收会怎样

通过代码理解僵尸进程

会产生僵尸进程:

如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

僵尸进程的危害:

如果有一个进程定期的产生一个子进程,但是子进程退出之后,父进程不进行回收,一段时间之后,就会产生很多的僵尸进程,如果用ps命令查看的话,会有很多状态为Z的进程,严格来说问题不在于僵尸进程而在于产生很多僵尸进程的父进程

如何消除:

通过kill命令杀死父进程,僵尸进程就变成了孤儿进程,孤儿进程会被Init接管,INIT进程会wait这些孤儿进程,释放她们占用的系统进程表中的资源,这样这些僵死的孤儿进程就可以处理了。

12.布隆过滤

应用:布隆过滤器次可以快速的解决一些比较棘手的问题,如URL去重、垃圾邮件识别、大集合红重复元素的判断和缓存穿透等问题

Tip:布隆过滤器不能完全解决缓存穿透,只能将其控制在一个可以容忍的范围内

 

原理:

布隆过滤器(Bloom Filter)本质上是由长度为 m 的位向量或位列表(仅包含 0 或 1 位值的列表)组成,最初所有的值均设置为 0

深信服_第2张图片

为了将数据项添加到布隆过滤器中,我们会提供 K 个不同的哈希函数并将结果位置上对应位的值置为 “1”。而对于布隆过滤器来说,我们将使用多个哈希函数,这将会产生多个索引值。

 

三次哈希(三次哈希的函数不一样)之后,对应具体的位置设置成1,再次三次哈希取出值,看一下三个部分相乘是不是等于1

当我们搜索一个值的时候,若该值经过 K 个哈希函数运算后的任何一个索引位为 ”0“,那么该值肯定不在集合中。但如果所有哈希索引值均为 ”1“,则只能说该搜索的值可能存在集合中。

13.构造函数为什么不能是虚函数

虚函数的调用需要虚函数表指针,而该指针存放在对象的内存空间中;若构造函数声明为虚函数,那么由于对象还未创建,还没有内存空间,更没有虚函数表地址来调用虚函数

 

3.https://mp.weixin.qq.com/s?__biz=MzI3NzcwNjY3NQ%3D%3D&chksm=eb636305dc14ea13a39862f7a3b3a4f7978f99c524cbc51734757b7f0abb567d830cfb1c9b47&idx=1&mid=2247483865&scene=21&sn=972e503e479a0d5f6c03eaf448df58fb#wechat_redirect

你可能感兴趣的:(面经答案,运维)