2019腾讯一面面经(C++软开)


2019年8月16日C++开发岗位一面面经。

1、线程池

概念

线程池(thread pool):一种线程的使用模式,线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

线程池组成

1、线程池管理器
创建一定数量的线程,启动线程,调配任务,管理着线程池。
本篇线程池目前只需要启动(start()),停止方法(stop()),及任务添加方法(addTask).
start()创建一定数量的线程池,进行线程循环.
stop()停止所有线程循环,回收所有资源.
addTask()添加任务.

2、工作线程
线程池中线程,在线程池中等待并执行分配的任务.

3、任务接口,
添加任务的接口,以供工作线程调度任务的执行。

4、任务队列
用于存放没有处理的任务。提供一种缓冲机制
同时任务队列具有调度功能,高优先级的任务放在任务队列前面;

工作机制

  • 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程。
  • 一个线程同时只能执行一个任务,但可以同时向一个线程池提交多个任务。
    线程池的工作的四种情况:
    a. 主程序当前没有任务要执行,线程池中的任务队列为空闲状态.
    此情况下所有工作线程处于空闲的等待状态,任务缓冲队列为空.
    b. 主程序添加小于等于线程池中线程数量的任务.
    主程序添加任务,并唤醒线程池中的线程开始执行任务。此时任务缓冲队列为空。
    c. 主程序添加任务数量大于当前线程池中线程数量的任务.
    主程序添加任务后发现现在线程池中的线程用完了,于是存入任务缓冲队列。工作线程空闲后主动从任务队列取任务执行.
    d. 主程序添加任务数量大于当前线程池中线程数量的任务,且任务缓冲队列已满.
    主程序添加第N个任务,添加后发现池子中的线程用完了,任务缓冲队列也满了,于是进入等待状态、等待任务缓冲队列中的任务腾空通知。
    但是要注意这种情形会阻塞主线程

详细介绍请移步至 线程池的工作原理

使用原因

多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。

2、栈溢出

从物理上讲,堆栈是就是一段连续分配的内存空间。在一个程序中,会声明各种变量。静态全局变量是位于数据段并且在程序开始运行的时候被加载。而程序的动态的局部变量则分配在堆栈里面。
从操作上来讲,堆栈是一个先入后出的队列。他的生长方向与内存的生长方向正好相反。我们规定内存的生长方向为向上,则栈的生长方向为向下。压栈的操作push=ESP-4,出栈的操作是pop=ESP+4.换句话说,堆栈中老的值,其内存地址,反而比新的值要大,这是堆栈溢出的基本理论依据。
在C语言程序中,参数的压栈顺序是反向的。比如func(a,b,c)。在参数入栈的时候,是:先压c,再压b,最后a。在取参数的时候,由于栈的先入后出,先取栈顶的a,再取b,最后取c。

以下程序:

#include <iostream>
#include <cstring>
#include <cstdio>

using namespace std;

int main()
{
    char name[8];
    gets(name);
    
    cout << name << endl;
    return 0;
}

堆栈向上增长8个字节,用来存放name[]数组。当输入的name大于8个字节时就会发生栈溢出。
如输入“wflonggggggg”,由于我们输入的name字符串太长,name数组容纳不下,只好向内存顶部继续写‘A’。由于堆栈的生长方向与内存的生长方向相反,这些‘g’覆盖了堆栈的老的元素。在main函数返回的时候,就会把“gggg”的ASCII码最为返回地址,执行其指向的指令,结果出现错误,这就是一次栈溢出。

在上面的例子中,这导致CPU去访问一个不存在的指令,结果出错。事实上,当堆栈溢出的时候,我们已经完全的控制了这个程序下一步的动作。如果我们用一个实际存在指令地址来覆盖这个返回地址,CPU就会转而执行我们的指令。黑客们可能会利用这个漏洞进行攻击,这也就是最常见的缓冲区攻击的一种。

3、DNS服务器的工作原理

域名系统作为一个层次结构和分布式数据库,包含各种类型的数据,包括主机名和域名。DNS数据库中的名称形成一个分层树状结构称为域命名空间。
在这里插入图片描述
根域: DNS域名使用中规定由尾部句点’.'来指定名称位于根或者更高层次的域层次结构。

顶级域: 用来指示某个国家、地区或者组织。采用三个字符,如com -> 商业公司,edu -> 教育机构,net -> 网络公司,gov -> 非军事政府机构等等。

二级域: 个人或者组织在Internet使用的注册名称。采用两个字符,如:cn -> 代表中国,jp -> 日本,uk -> 英国,hk -> 香港等等。

主机: 主机名处于域名空间结构中的最底层,主机名和域名结合构成FQDN,主机名是FQDN最左端的部分。

DNS的查询过程:
DNS的查询过程
从域名到IP的过程:
1、在浏览器中输入www . qq .com 域名,操作系统会先检查自己本地的hosts文件是否有这个网址映射关系,如果有,就先调用这个IP地址映射,完成域名解析。

2、如果hosts里没有这个域名的映射,则查找本地DNS解析器缓存,是否有这个网址映射关系,如果有,直接返回,完成域名解析。

3、如果hosts与本地DNS解析器缓存都没有相应的网址映射关系,首先会找TCP/ip参数中设置的首选DNS服务器,在此我们叫它本地DNS服务器,此服务器收到查询时,如果要查询的域名,包含在本地配置区域资源中,则返回解析结果给客户机,完成域名解析,此解析具有权威性。

4、如果要查询的域名,不由本地DNS服务器区域解析,但该服务器已缓存了此网址映射关系,则调用这个IP地址映射,完成域名解析,此解析不具有权威性。

5、如果本地DNS服务器本地区域文件与缓存解析都失效,则根据本地DNS服务器的设置(是否设置转发器)进行查询,如果未用转发模式,本地DNS就把请求发至13台根DNS,根DNS服务器收到请求后会判断这个域名(.com)是谁来授权管理,并会返回一个负责该顶级域名服务器的一个IP。本地DNS服务器收到IP信息后,将会联系负责.com域的这台服务器。这台负责.com域的服务器收到请求后,如果自己无法解析,它就会找一个管理.com域的下一级DNS服务器地址(http://qq.com)给本地DNS服务器。当本地DNS服务器收到这个地址后,就会找http://qq.com域服务器,重复上面的动作,进行查询,直至找到www . qq .com主机。

6、如果用的是转发模式,此DNS服务器就会把请求转发至上一级DNS服务器,由上一级服务器进行解析,上一级服务器如果不能解析,或找根DNS或把转请求转至上上级,以此循环。不管是本地DNS服务器用是是转发,还是根提示,最后都是把结果返回给本地DNS服务器,由此DNS服务器再返回给客户机。

从客户端到本地DNS服务器是属于递归查询,而DNS服务器之间就是的交互查询就是迭代查询。

解析顺序:
1) 浏览器缓存

当用户通过浏览器访问某域名时,浏览器首先会在自己的缓存中查找是否有该域名对应的IP地址(若曾经访问过该域名且没有清空缓存便存在);

2) 系统缓存

当浏览器缓存中无域名对应IP则会自动检查用户计算机系统Hosts文件DNS缓存是否有该域名对应IP;

3) 路由器缓存

当浏览器及系统缓存中均无域名对应IP则进入路由器缓存中检查,以上三步均为客服端的DNS缓存;

4) ISP(互联网服务提供商)DNS缓存

当在用户客服端查找不到域名对应IP地址,则将进入ISP DNS缓存中进行查询。比如你用的是电信的网络,则会进入电信的DNS缓存服务器中进行查找;

5) 根域名服务器

当以上均未完成,则进入根服务器进行查询。全球仅有13台根域名服务器,1个主根域名服务器,其余12为辅根域名服务器。根域名收到请求后会查看区域文件记录,若无则将其管辖范围内顶级域名(如.com)服务器IP告诉本地DNS服务器;

6) 顶级域名服务器

顶级域名服务器收到请求后查看区域文件记录,若无则将其管辖范围内主域名服务器的IP地址告诉本地DNS服务器;

7) 主域名服务器

主域名服务器接受到请求后查询自己的缓存,如果没有则进入下一级域名服务器进行查找,并重复该步骤直至找到正确纪录;

8)保存结果至缓存

本地域名服务器把返回的结果保存到缓存,以备下一次使用,同时将该结果反馈给客户端,客户端通过这个IP地址与web服务器建立链接。

4、快排

5、洗牌算法

请移步至 洗牌算法

6、C++内存管理

一个由c/C++编译的程序占用的内存分为以下几个部分

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

7、野指针

a. 未初始化的指针变量。
b. 指向的内存被释放没有置NULL的指针。
c. 超过了变量作用范围的指针

8、网站访问,输入域名之后的流程

请移步至 计算机网络——http协议和https协议

9、C++栈和堆上内存申请限制问题

在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存区域。栈顶的地址和栈的最大容量是系统预先规定好的,在Window下,栈的大小是2MB,Linux下,默认栈空间大小为8MB,可通过ulimit -s来设置。

特点:栈的速度快、空间小,不灵活

如果申请的内存大小大于栈的最大空间会报错。

更改栈大小方法

link时用/STACK指定它的大小,或者在.def中使用STACKSIZE指定它的大小。
使用控制台命令“EDITBIN”更改exe的栈空间大小

堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储空闲内存地址的,自然是不连续的,而链表的遍历方向是由低地址向高地址的。在Window下,堆的大小一般小于2GB。

特点:堆的大小受限于计算机系统中有效的虚拟内存,所以堆获得的空间比较灵活,也比较大,但速度相对慢一些,也容易产生内存泄露问题。

在堆中理论上最大可以申请的内存 = 物理内存大小 + 虚拟内存大小(交换空间)。

堆的逻辑地址是连续的,物理地址不一定连续

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值