米哈游,启动!

今天来分享一位同学米哈游的面经,投递的岗位是云原生,同学是校招生没有云原生的基础,所以面试没有问云原生的内容,但是都在计算机基础方面的内容,都是经典的面试问题。

不管是面前后端开发、客户端开发、测试开发等岗位,计算机基础的内容都逃不开的,包括社招面大厂,即使工作了好几年,也会问考察一些计算机基础的问题,所以同学们一定要好好掌握。

这个面试难在的是在算法, 抽五星卡概率,不愧是游戏大厂,出的题目也是游戏背景。

操作系统

死锁发生条件是什么?

死锁只有同时满足以下四个条件才会发生:

  • 互斥条件:互斥条件是指多个线程不能同时使用同一个资源

  • 持有并等待条件:持有并等待条件是指,当线程 A 已经持有了资源 1,又想申请资源 2,而资源 2 已经被线程 C 持有了,所以线程 A 就会处于等待状态,但是线程 A 在等待资源 2 的同时并不会释放自己已经持有的资源 1

  • 不可剥夺条件:不可剥夺条件是指,当线程已经持有了资源 ,在自己使用完之前不能被其他线程获取,线程 B 如果也想使用此资源,则只能在线程 A 使用完并释放后才能获取。

  • 环路等待条件:环路等待条件指的是,在死锁发生的时候,两个线程获取资源的顺序构成了环形链

如何避免死锁?

避免死锁问题就只需要破环其中一个条件就可以,最常见的并且可行的就是使用资源有序分配法,来破环环路等待条件。那什么是资源有序分配法呢?线程 A 和 线程 B 获取资源的顺序要一样,当线程 A 是先尝试获取资源 A,然后尝试获取资源 B 的时候,线程 B 同样也是先尝试获取资源 A,然后尝试获取资源 B。也就是说,线程 A 和 线程 B 总是以相同的顺序申请自己想要的资源。

介绍一下操作系统内存管理

操作系统设计了虚拟内存,每个进程都有自己的独立的虚拟内存,我们所写的程序不会直接与物理内打交道。

有了虚拟内存之后,它带来了这些好处:

  • 第一,虚拟内存可以使得进程对运行内存超过物理内存大小,因为程序运行符合局部性原理,CPU 访问内存会有很明显的重复访问的倾向性,对于那些没有被经常使用到的内存,我们可以把它换出到物理内存之外,比如硬盘上的 swap 区域。

  • 第二,由于每个进程都有自己的页表,所以每个进程的虚拟内存空间就是相互独立的。进程也没有办法访问其他进程的页表,所以这些页表是私有的,这就解决了多进程之间地址冲突的问题。

  • 第三,页表里的页表项中除了物理地址之外,还有一些标记属性的比特,比如控制一个页的读写权限,标记该页是否存在等。在内存访问方面,操作系统提供了更好的安全性。

Linux 是通过对内存分页的方式来管理内存,分页是把整个虚拟和物理内存空间切成一段段固定尺寸的大小。这样一个连续并且尺寸固定的内存空间,我们叫(_Page_)。在 Linux 下,每一页的大小为 4KB。虚拟地址与物理地址之间通过页表来映射,如下图:

页表是存储在内存里的,内存管理单元 (_MMU_)就做将虚拟内存地址转换成物理地址的工作。

而当进程访问的虚拟地址在页表中查不到时,系统会产生一个缺页异常,进入系统内核空间分配物理内存、更新进程页表,最后再返回用户空间,恢复进程的运行。

介绍copy on write

主进程在执行 fork 的时候,操作系统会把主进程的「页表」复制一份给子进程,这个页表记录着虚拟地址和物理地址映射关系,而不会复制物理内存,也就是说,两者的虚拟空间不同,但其对应的物理空间是同一个。

这样一来,子进程就共享了父进程的物理内存数据了,这样能够节约物理内存资源,页表对应的页表项的属性会标记该物理内存的权限为只读

不过,当父进程或者子进程在向这个内存发起写操作时,CPU 就会触发写保护中断,这个写保护中断是由于违反权限导致的,然后操作系统会在「写保护中断处理函数」里进行物理内存的复制,并重新设置其内存映射关系,将父子进程的内存读写权限设置为可读写,最后才会对内存进行写操作,这个过程被称为「**写时复制(Copy On Write)**」。

写时复制顾名思义,在发生写操作的时候,操作系统才会去复制物理内存,这样是为了防止 fork 创建子进程时,由于物理内存数据的复制时间过长而导致父进程长时间阻塞的问题。

copy on write节省了什么资源

节省了物理内存的资源,因为 fork 的时候,子进程不需要复制父进程的物理内存,避免了不必要的内存复制开销,子进程只需要复制父进程的页表,这时候父子进程的页表指向的都是共享的物理内存。

只有当父子进程任何有一方对这片共享的物理内存发生了修改操作,才会触发写时复制机制,这时候才会复制发生修改操作的物理内存。

线程和进程区别

  • 本质区别:进程是操作系统资源分配的基本单位,而线程是任务调度和执行的基本单位

  • 在开销方面:每个进程都有独立的代码和数据空间(程序上下文),程序之间的切换会有较大的开销;线程可以看做轻量级的进程,同一类线程共享代码和数据空间,每个线程都有自己独立的运行栈和程序计数器(PC),线程之间切换的开销小

  • 稳定性方面:进程中某个线程如果崩溃了,可能会导致整个进程都崩溃。而进程中的子进程崩溃,并不会影响其他进程。

  • 内存分配方面:系统在运行的时候会为每个进程分配不同的内存空间;而对线程而言,除了CPU外,系统不会为线程分配内存(线程所使用的资源来自其所属进程的资源),线程组之间只能共享资源

  • 包含关系:没有线程的进程可以看做是单线程的,如果一个进程内有多个线程,则执行过程不是一条线的,而是多条线(线程)共同完成的;线程是进程的一部分,所以线程也被称为轻权进程或者轻量级进程

线程切换为什么比进程切换快,节省了什么资源?

线程切换比进程切换快是因为线程共享同一进程的地址空间和资源,线程切换时只需切换堆栈和程序计数器等少量信息,而不需要切换地址空间,避免了进程切换时需要切换内存映射表等大量资源的开销,从而节省了时间和系统资源。

linux 如何查看进程状态?

可以通过 ps 命令或者 top 命令来查看进程的状态。比如我想看 nginx 进程的状态,可以在 linux 输入这条命令:

top 命令除了能看进程的状态,还能看到系统的信息,比如系统负载、内存、cpu 使用率等等

linux 如何查看线程状态?

在 ps 和 top 命令加一下参数,就能看到线程状态了:

`top -H

ps -eT | grep <进程名或线程名>

`

image.png

网络

如何查看网络连接情况?

可以通过 netstat 命令来查看网络连接的情况,比如下面,我通过 命令:

netstat -napt

显示了服务器上的 tcp 连接状态,可以观察到每一个 tcp 连接的状态,以及四元组信息(源 ip 地址、目标 ip 地址,源端口、源 ip)

tcp连接过程?

image.png

  • 一开始,客户端和服务端都处于 CLOSE 状态。先是服务端主动监听某个端口,处于 LISTEN 状态

  • 客户端会随机初始化序号(client_isn),将此序号置于 TCP 首部的「序号」字段中,同时把 SYN 标志位置为 1,表示 SYN 报文。接着把第一个 SYN 报文发送给服务端,表示向服务端发起连接,该报文不包含应用层数据,之后客户端处于 SYN-SENT 状态。

  • 服务端收到客户端的 SYN 报文后,首先服务端也随机初始化自己的序号(server_isn),将此序号填入 TCP 首部的「序号」字段中,其次把 TCP 首部的「确认应答号」字段填入 client_isn + 1, 接着把 SYN 和 ACK 标志位置为 1。最后把该报文发给客户端,该报文也不包含应用层数据,之后服务端处于 SYN-RCVD 状态。

  • 客户端收到服务端报文后,还要向服务端回应最后一个应答报文,首先该应答报文 TCP 首部 ACK 标志位置为 1 ,其次「确认应答号」字段填入 server_isn + 1 ,最后把报文发送给服务端,这次报文可以携带客户到服务端的数据,之后客户端处于 ESTABLISHED 状态。

  • 服务端收到客户端的应答报文后,也进入 ESTABLISHED 状态。

server a和server b,如何判断两个服务器正常连接?出错怎么办?

直不会发送数据给客户端,那么服务端是永远无法感知到客户端宕机这个事件的,也就是服务端的 TCP 连接将一直处于 ESTABLISH 状态,占用着系统资源。

为了避免这种情况,TCP 搞了个保活机制。这个机制的原理是这样的:定义一个时间段,在这个时间段内,如果没有任何连接相关的活动,TCP 保活机制会开始作用,每隔一个时间间隔,发送一个探测报文,该探测报文包含的数据非常少,如果连续几个探测报文都没有得到响应,则认为当前的 TCP 连接已经死亡,系统内核将错误信息通知给上层应用程序。

在 Linux 内核可以有对应的参数可以设置保活时间、保活探测的次数、保活探测的时间间隔,以下都为默认值:

net.ipv4.tcp_keepalive_time=7200 net.ipv4.tcp_keepalive_intvl=75 net.ipv4.tcp_keepalive_probes=9

  • tcp_keepalive_time=7200:表示保活时间是 7200 秒(2小时),也就 2 小时内如果没有任何连接相关的活动,则会启动保活机制

  • tcp_keepalive_intvl=75:表示每次检测间隔 75 秒;

  • tcp_keepalive_probes=9:表示检测 9 次无响应,认为对方是不可达的,从而中断本次的连接。

也就是说在 Linux 系统中,最少需要经过 2 小时 11 分 15 秒才可以发现一个「死亡」连接。

注意,应用程序若想使用 TCP 保活机制需要通过 socket 接口设置 SO_KEEPALIVE 选项才能够生效,如果没有设置,那么就无法使用 TCP 保活机制。

如果开启了 TCP 保活,需要考虑以下几种情况:

  • 第一种,对端程序是正常工作的。当 TCP 保活的探测报文发送给对端, 对端会正常响应,这样 TCP 保活时间会被重置,等待下一个 TCP 保活时间的到来。

  • 第二种,对端主机宕机并重启。当 TCP 保活的探测报文发送给对端后,对端是可以响应的,但由于没有该连接的有效信息,会产生一个 RST 报文,这样很快就会发现 TCP 连接已经被重置。

  • 第三种,是对端主机宕机(_注意不是进程崩溃,进程崩溃后操作系统在回收进程资源的时候,会发送 FIN 报文,而主机宕机则是无法感知的,所以需要 TCP 保活机制来探测对方是不是发生了主机宕机_),或对端由于其他原因导致报文不可达。当 TCP 保活的探测报文发送给对端后,石沉大海,没有响应,连续几次,达到保活探测次数后,TCP 会报告该 TCP 连接已经死亡

TCP 保活的这个机制检测的时间是有点长,我们可以自己在应用层实现一个心跳机制。

比如,web 服务软件一般都会提供 keepalive_timeout 参数,用来指定 HTTP 长连接的超时时间。如果设置了 HTTP 长连接的超时时间是 60 秒,web 服务软件就会启动一个定时器,如果客户端在完成一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,定时器的时间一到,就会触发回调函数来释放该连接。

项目

因为项目写了 raft 分布式 kv 的项目,问了一下 raft 算法相关的问题

  • 一致性是怎么保证的

  • leader选举过程是怎么样的?

  • leader崩溃后重新加入如何保证日志一致

手撕

  • 给了一道go相关题目,看打印出什么,关于append的

  • hard算法:抽五星卡概率,给了个思路,没写出来,目测gg了

  • 10
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
米哈游是一家知名的游戏开发公司,他们的SDK工程师是负责开发和维护米哈游游戏的软件开发工具包(SDK)的工程师。以下是面试时可能会涵盖的一些问题和要点: 1. 编程语言和技术: 在面试中,你可能会被要求谈论你熟悉的编程语言和技术。作为一名米哈游SDK工程师,你必须熟悉至少一种编程语言,如C++或Java,并且具备良好的编码和调试能力。 2. SDK开发和维护能力: 面试官可能会询问你如何开发和维护SDK。你需要了解SDK的基本概念和用途,并能够解释如何设计和构建SDK,以方便游戏开发人员使用。 3. API设计和文档撰写: SDK工程师通常需要设计API,以便外部开发人员能够轻松地使用SDK。你可能会被问到如何设计易用的API,并如何撰写清晰和完整的文档。 4. 跨平台开发经验: 米哈游游戏通常在多个平台上发布,因此SDK工程师需要有跨平台开发经验。你可能会被问到如何保证在不同平台上的SDK兼容性和性能优化。 5. 解决问题能力: SDK工程师通常需要解决各种问题,包括性能问题、兼容性问题和代码错误等。你可能会被要求谈论你在过去的项目中遇到的问题,并如何解决它们。 总之,成为米哈游SDK工程师需要具备扎实的编程基础、SDK开发和维护经验、API设计和文档撰写能力,以及解决问题的能力。面试中,你需要展示你在这些方面的技能和经验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Web面试那些事儿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值