追求极致、务实敢为、开放谦逊、坦诚清晰、始终创业、
Always Day One!
一面
先讲了一下实习经历
讲一下进程和线程的区别,
进程用于重量级的任务,线程用于轻量级的任务,比如Go还有协程,它是负责更轻量级的任务
为什么需要进程?
资源分配的基本单位是进程,CPU调度的基本单位是线程。
当程序执行时,创建一个进程,每个进程都有自己的独立的内存空间,互相之间不受影响。
进程里的堆,是一个进程中最大的一块内存,被进程中的所有线程共享的,进程创建时分配,主要存放 new 创建的对象实例。
进程里的方法区,是用来存放进程中的代码片段的,是线程共享的。
每个进程都有自己的地址空间,即进程空间。一个服务器通常需要接收大量并发请求,为每一个请求都创建一个进程系统开销大、请求响应效率低,因此操作系统引进线程。
线程只拥有在运行中必不可少的资源(如程序计数器、栈)
本质:进程是操作系统资源分配的基本单位;线程是任务调度和执行的基本单位
内存分配:系统在运行的时候会为每个进程分配不同的内存空间,建立数据表来维护代码段、堆栈段和数据段;除了 CPU 外,系统不会为线程分配内存,线程所使用的资源来自其所属进程的资源
- 代码段(文本段):保存程序的执行码。在进程并发时,代码段是共享的且只读的,在存储器中只需有一个副本。
- 数据段:此段又称为初始化数据段,它包含了程序中已初始化的全局变量、全局静态变量、局部静态变量。例如,函数外定义的变量并赋值:int count=30;此变量count存放在数据段中。
- bss段:通常此段又称为未初始化数据段,它包含了程序中未初始化的全局变量、全局静态变量、局部静态变量,程序执行前操作系统将此段初始化为0。例如,函数外定义的变量但没有赋值:long sum[1000];此变量存放在bss段中。
- 栈:程序执行前静态分配的内存空间,栈的大小可在编译时指定,Linux环境下默认为8M。栈段是存放程序执行时局部变量、函数调用信息、中断现场保留信息的空间。程序执行时,CPU堆栈段指针会在栈顶根据执行情况进行上下移动。
- 堆:程序执行时,按照程序需要动态分配的内存空间。malloc、calloc、realloc函数分配的空间都在堆上分配。
协程为什么比线程快?
协程全程都在用户态进行,是语言层面上进行的时间片,而不是内核态进行的内核时间片争夺。同时协程的切换操作全部在用户态进行,不需要进入内核态进行处理。
总结:
(1)协程切换完全在用户空间进行,线程切换涉及特权模式切换,需要在内核空间完成;
(2)协程切换相比线程切换做的事情更少。
线程的调度只有拥有最高权限的内核空间才可以完成,所以线程的切换涉及到用户空间和内核空间的切换,也就是特权模式切换,然后需要操作系统调度模块完成线程调度(taskstruct),而且除了和协程相同基本的 CPU 上下文,还有线程私有的栈和寄存器等。
TIP: 内核栈:Linux中每个进程有两个栈,分别用于用户态和内核态的进程执行,其中的内核栈就是用于内核态的堆栈,它和进程的task_struct结构,更具体的是thread_info结构一起放在两个连续的页框大小的空间内。
用户态到内核态切换
线程切换多长时间?
一般来说,协程的上下文切换也就十几ns,一般要比线程切换快20-30倍。线程切换在 us 级别。
多个线程在切换过程中干了啥?
进程调度,切换进程上下文,包括分配的内存,包括数据段,附加段,堆栈段,代码段,以及一些表格。
线程调度,切换线程上下文,主要切换堆栈,以及各寄存器,因为同一个进程里的线程除了堆栈不同,其余基本上共享。
协程又称为轻量级线程,每个协程都自带了一个栈,可以认为一个协程就是一个函数和这个存放这个函数运行时数据的栈,这个栈非常小,一般只有几十kb。
进程切换分两步:
1.切换页目录以使用新的地址空间
2.切换内核栈和硬件上下文
对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。
切换的性能消耗:
1、线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
2、另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有一个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲(processor's Translation Lookaside Buffer (TLB))或者相当的神马东西会被全部刷新,这将导致内存的访问在一段时间内相当的低效。但是在线程的切换中,不会出现这个问题
讲一下程序的虚拟内存、常驻内存和逻辑内存。
进程的虚拟内存空间分为两个部分,低2GB(或3GB_)由应用程序使用,高2GB(或1GB)由系统内核程序使用。
(1)虚拟地址空间中的数据是分页管理的。
(2)应用程序不用考虑系统中其他应用程序的内存使用情况,如占用了多少内存、占用了哪些内存等。
(3)虚拟地址并不是物理地址空间中的地址,不是数据在内存中真实存在的地址,操作系统会将进程的虚拟地址映射到真实的物理内存的地址。
(4)进程也不用考虑真实的物理内存有多大,只需要了解可以使用2GB(一般情况下)的内存,操作系统负责转换。
(5)如果系统中没有足够的物理内存供使用,那么操作系统会将当前没有使用的内存分页“调度”到硬盘上保存起来。页面调度不会造成内存中的数据在虚拟地址空间中地址的改变,所以进程不需要知道内存分页是如何调度的,不需要知道内存中保存的数据是在内存中还是在硬盘上,只需要知道其虚拟地址就可以了。
虚拟内存(VIRT):程序占用的内存大小。程序申请多少内存,就增长多少内存。
常驻内存(RES):实际上当前占用的内存大小(部分未使用到的虚拟内存可能被写入磁盘上)
共享内存(SHR):除了自身进程的共享内存,也包括其他进程的共享内存。虽然进程只使用了几个共享库的函数,但它包含了整个共享库的大小。计算某个进程所占的物理内存大小公式:RES – SHR。swap out后,它将会降下来
逻辑内存:虚拟内存所创建出来的展示给开发者的内存空间即为逻辑内存。虚拟地址即为逻辑地址。
物理内存:实际的硬盘的内存
虚拟内存和常驻内存的存在,需要引入页表,来记录哪些存在与内存中,哪些不存在。当进程访问的数据不在物理内存上的时候,需要缺页中断。将磁盘上面的数据读入内存。而这个过程是操作系统自动完成的,不需要开发人员感知。
堆和栈是存啥的?
堆:程序员用来动态分配和释放的空间,一般可以达到4个G。栈:由编译器自动分配释放,存放函数的参数值,局部变量的值等。
堆自底向上生长,栈自顶向下生长。堆是动态分配的。栈可以是静态分配和动态分配两种,但是栈的动态分配由编译器释放。
堆和栈:
https://blog.csdn.net/qq_35637562/article/details/78550606
我们上网用到了哪些协议?
理论上七层协议:应用层,表示层,会话层,传输层,网络层,数据链路层,物理层
现实中是四层协议:应用层,传输层,网络层,链路层
csdn的总结
-
应用层:
- 网页的HTTP、HTTPS协议(https是一种由HTTP和SSL/TLS组合起来的应用,用以提供加密通信和对网络服务器的身份验证)
- 邮件的STMP协议
- DNS域名系统解析网址IP
- FTP(File Transfer Protocal)文本传送协议
- Secure Shell(安全外壳协议,简称SSH)是一种加密的网络传输协议(讲解),可在不安全的网络中为网络服务提供安全的传输环境
- 传输层:TCP/IP 协议;UDP协议
-
网络层:
- ARP协议:进行地址解析协议(IP->MAC)
- IP协议
-
ICMP协议(Internet互联网控制报文协议),提供一致易懂的出错报告信息,有
目标不可达
、超时
等10多种。ICMP报文包含在IP数据报中,属于IP的一个用户,IP头部就在ICMP报文的前面,所以一个ICMP报文包括IP头部、ICMP头部和ICMP报文
补充: HTTPS 的加密过程 【浏览器工作原理与实践】
加密分为两种:对称加密和非对称加密。对于对称加密,相当于使用相同的密钥才能正常对数据进行加解密,不管怎么商定,浏览器和网站的首次通信过程必定是明文的。对称加密的优点是加解密效率高。
HTTPS 使用的是对称加密与非对称加密相结合的方式。首次通信过程,是 HTTPS 首先要协商加解密方式的过程,这个过程就是 HTTPS 建立安全连接的过程:
- 浏览器发送它所支持的加密套件列表和一个随机数 client-random,这里的加密套件是指加密的方法,加密套件列表就是指浏览器能支持多少种加密方法列表。
- 服务器会从加密套件列表中选取一个加密套件,然后还会生成一个随机数 service-random,并将 service-random 和加密套件列表返回给浏览器。
- 最后浏览器和服务器分别返回确认消息。
这样浏览器端和服务器端都有相同的 client-random 和 service-random 了,然后它们再使用相同的方法将 client-random 和 service-random 混合起来生成一个密钥 master secret,有了密钥 master secret 和加密套件之后,双方就可以进行数据的加密传输了。
补充: 地址栏输入网址到呈现内容的流程
子网是干嘛使的?子网内,主机A访问主机B,是直达还是经过路由器?
子网就是划分一个网络区块,来把IP前缀相同的归为一个区域,从而一级一级在路由之间传递报文。涉及子网掩码的概念和计算。
经过路由器,会跳过不必要的路由器。
如何并发保持一致性???(比如拿go举例,协程a要修改一个信息,协程b也要修改这个信息,两个协程同时通过channel传给c,c怎么判断修改的先后顺序?)
加互斥锁。线程串行化(wait notify机制)。方法设置成原子性。
具体的顺序由程序的执行情况决定:c 先拿到谁先处理谁,处理完回到 channel 处,才会读取到下一个消息,从而处理下一个信息。
解决方案:加上一个参数来记录上次更新是A还是B的更新,同时加上更新时间戳,在C函数内部更新信息的时候,如果本次更新和上次更新几乎“同时”,且如果当前是A发出的更新,则忽略,以B为准。
算法题
写出二叉树的左视图(输出每层最左端的节点,说一下空间和时间复杂度)
广度遍历,两个队列分层存储(空间 O(n) 时间 O(n))
OR
深度遍历,记录已经输出的层数和当前层数(空间 O(1) 时间 O(n))
二面
速度很快,一面是周二下午2点,面试完第二天下午(周三)通知第三天上午(周四)10.30面试
讲一下项目里面用的缓存,涉及到的坑和问题。
项目里面怎么解决缓存一致性问题
看你用过 Qt ,问你一些 Qt 的知识点吧,Qt 里面搭建窗口的时候,怎么适配不同分辨率的电脑端。
我用过小程序,里面是在一开始能够得到手机的分辨率,DPI,screen width 等数据,之后的渲染,根据这些全局变量进行设定。Qt 应该一开始也能够得到电脑端的分辨率和屏幕宽度数据,之后在生成窗口的时候,根据这些数据进行相应的数据设定即可
Qt 里面的信号和槽用过吧,那你知道他们的底层是怎么实现的么?
这个不太清楚。然后我说,之前监听鼠标位置并输出的时候,发现鼠标任意动一下都会输出一行,所以可能是开一个线程,实时监听鼠标和键盘,有变化之后,看看有没有绑定这个操作的控件,如果有就去处理。
面试官:“那这样不会让性能极大下降么?大部分时间都在监听各种状态”
我:“可能也不是全部的状态都监听,比如 java script 里面需要绑定一些监听,可能是绑定了的才去监听,不绑定的就直接过了”
面试官:“java script 是脚本语言嘛,它的处理肯定和 Qt 是不一样的”
之后之后面试官耐心地讲了一下中断,中断芯片 8086 你也应该知道吧,大致就是产生事件之后,操作系统会进行中断处理,分发当前事件,类似广播,然后需要这种事件的应用程序,获取到操作系统的中断信号,然后来处理。Qt 的信号也是这样,控件产生相应的事件时,广播自己的信号,监听信号的控件发现之后,就会调用回调函数,并不是去实时监测所有的状态。
总结:槽可以与信号连接在一起,每当和槽连接的信号被发射的时候,就会调用这个槽。
Qt 写出来的 .cpp 代码,放到 gcc 编译器里面编译,是通不过的,为什么?
Qt 有自己的语法和定义,比如 Qt 对 Integer String 等基本数据类型都进行了自己的封装
面试官:“这些只是标准的C++对象嘛,对吧,那 include 相应的头文件应该就可以,比如 QString.h 等,但是实际上 include 之后,还是无法编译,这是为什么?”
(当时没回答上来,其实应该是要链接动态库 dll 的,g++ 编译器在链接这些动态库之后,是能够编译通过的)
Go 里面,如果给你这样的代码,两个函数,哪些数据能够被修改,哪些不能?为什么?
type Item struct {
n int32
m int32
p Part
p2 *Part
}
type Part struct {
a int32
}
func (i *Item) Do() {
i.p.a = 2
i.p2.a = 3
}
func (i Item) Di() {
i.p.a = 4
i.p2.a = 5
}
Do 能够修改 p2 的 a,其余的都无法修改
Di 能够修改所有参数
说完然后让我编完代码,运行一下,输出看看,主要看看 p 和 p2,主函数如下:
func main(){
i := &Item{
p: Part{
a: 1,
},
p2: &Part{
a: 1,
},
}
i.Do()
fmt.Println("1", i.p.a)
fmt.Println("1", i.p2.a)
i.Di()
fmt.Println("2", i.p.a)
fmt.Println("2", i.p2.a)
t := Item{
p: Part{
a: 1,
},
p2: &Part{
a: 1,
},
}
t.Do()
fmt.Println("3", t.p.a)
fmt.Println("3", t.p2.a)
t.Di()
fmt.Println("4", t.p.a)
fmt.Println("4", t.p2.a)
}
输出:
1 2
1 3
2 2
2 5
3 2
3 3
4 2
4 5
三面
简单问了一下项目,之前的实习
讲了一下 go 的 MPG 策略