Golang开发面经米哈游(一轮游)

Posted 小生凡一

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Golang开发面经米哈游(一轮游)相关的知识,希望对你有一定的参考价值。

文章目录

写在前面

米哈游 面试下来感觉还行吧,挺注重基础的,面试官水平也很高。但是感觉不是在招人的样子,我有好多同学都是简历挂(

笔试

无笔试,很奇怪(

一面

线程和协程有什么区别?各自有什么优缺点?

进程线程
系统中正在运行的一个应用程序系统分配处理器时间资源的基本单元
程序一旦运行就是进程进程之内独立执行的一个单元执行流
资源分配的最小单位程序执行的最小单位
独立的地址空间保护模式下不会对其它进程产生影响
没有单独的地址空间进程切换要求同时进行并且又要共享某些变量的并发操作,只能用线程,不能用进程

进程之间如何进行通信?

管道、消息队列、共享内存、信号量、信号、socket

什么是信号,信号量是如何实现的?

kill -l
 SIGINT 信号
kill -9 PIDSIGKILL

信号量:

当使用共享内存的通信方式,如果有多个进程同时往共享内存写入数据,有可能先写的进程的内容被其他进程覆盖了。

进程间的互斥和同步

信号量代表着资源的数量,操作信号量的方式有两种:

  • P操作:这个操作会将信号量减一,相减后信号量如果小于0,则表示资源已经被占用了,进程需要阻塞等待;如果大于等于0,则说明还有资源可用,进程可以正常执行。
  • V操作:这个操作会将信号量加一,相加后信号量如果小于等于0,则表明当前有进程阻塞,于是会将该进程唤醒;如果大于0,则表示当前没有阻塞的进程。

讲讲Go里面的GMP模型?

Go的GMP模型

可以重用
逻辑处理单元P 和 M 的这种关系就相当于 Linux 系统中的用户层面的线程和内核的线程是一样的

map用过吧?怎么对map进行排序?

go的map不保证有序性,所以按key排序需要取出key,对key排序,再遍历输出value

讲讲map底层?为什么是无序的?

map 的底层是一个结构体

// Go map 的底层结构体表示
type hmap struct 
    count     int    // map中键值对的个数,使用len()可以获取 
	flags     uint8
	B         uint8  // 哈希桶的数量的log2,比如有8个桶,那么B=3
	noverflow uint16 // 溢出桶的数量
	hash0     uint32 // 哈希种子

	buckets    unsafe.Pointer // 指向哈希桶数组的指针,数量为 2^B 
	oldbuckets unsafe.Pointer // 扩容时指向旧桶的指针,当扩容时不为nil 
	nevacuate  uintptr        

	extra *mapextra  // 可选字段


const (
	bucketCntBits = 3
	bucketCnt     = 1 << bucketCntBits     // 桶数量 1 << 3 = 8
)

// Go map 的一个哈希桶,一个桶最多存放8个键值对
type bmap struct 
    // tophash存放了哈希值的最高字节
	tophash [bucketCnt]uint8
    
    // 在这里有几个其它的字段没有显示出来,因为k-v的数量类型是不确定的,编译的时候才会确定
    // keys: 是一个数组,大小为bucketCnt=8,存放Key
    // elems: 是一个数组,大小为bucketCnt=8,存放Value
    // 你可能会想到为什么不用空接口,空接口可以保存任意类型。但是空接口底层也是个结构体,中间隔了一层。因此在这里没有使用空接口。
    // 注意:之所以将所有key存放在一个数组,将value存放在一个数组,而不是键值对的形式,是为了消除例如map[int64]所需的填充整数8(内存对齐)
    
    // overflow: 是一个指针,指向溢出桶,当该桶不够用时,就会使用溢出桶

 k 的 hash 值与 buckets 长度取余
runtime.mapiterinit
func mapiterinit(t *maptype, h *hmap, it *hiter) 
	...
	it.t = t
	it.h = h
	it.B = h.B
	it.buckets = h.buckets
	if t.bucket.kind&kindNoPointers != 0 
		h.createOverflow()
		it.overflow = h.extra.overflow
		it.oldoverflow = h.extra.oldoverflow
	

	r := uintptr(fastrand())
	if h.B > 31-bucketCntBits 
		r += uintptr(fastrand()) << 31
	
	it.startBucket = r & bucketMask(h.B)
	it.offset = uint8(r >> h.B & (bucketCnt - 1))
	it.bucket = it.startBucket
    ...

	mapiternext(it)

 mapiterinit 方法哈希表的类型信息、当前哈希表的存储信息和当前遍历迭代的数据
fastrand
...
// decide where to start
r := uintptr(fastrand())
if h.B > 31-bucketCntBits 
	r += uintptr(fastrand()) << 31

it.startBucket = r & bucketMask(h.B)
it.offset = uint8(r >> h.B & (bucketCnt - 1))

// iterator state
it.bucket = it.startBucket
根据随机数,选择一个桶位置作为起始点进行遍历迭代。
 for range map

知道TCP连接吧?三次握手的目的是什么?

接收和发送信息是正常让 接收方 知道 发送方 有发送信息的能力让 发送方 知道 接收方 有发送和接受信息的能力让 接收方 知道 发送方 有接受信息的能力

那四次挥手有了解过吗?为什么是四次?

四次挥手则是为了保证等数据完成的被接收完再关闭连接。

都达到关闭连接的条件才能断开。
发起关闭连接
确认报文自己知道客户端想要关闭连接了
 FIN 报文已经发送完了准备关闭连接了
 ACK 报文

什么是粘包和拆包?为什么会出现?

粘包就是两个或者多个以上的包粘在一起,拆包就是为什么解决粘包问题。

出现粘包原因有两个,一个是发送方发送不及时,导致多个包在发送端粘黏。另一个是接收方接受不及时,导致包在接收端堆叠导致。

怎么解决?

设计一个带包头的应用层报文结构就能解决

什么是事务?

原子性、一致性、隔离性、持久性

那 redis 支持事务吗?

redis 是不支持事务的,因为 他不支持原子性,但是支持一致性,是最终一致性。

算法:手撕链表,要求递归实现。

算法:忘记了。。好像是中等题,反正不难,也不简单。

二面

我以为过了简历,免了笔试,一面算法写出来,八股也能吹了,应该有二面,但还是挂了(

真的是 海量 hc 吗。。。

以上是关于Golang开发面经米哈游(一轮游)的主要内容,如果未能解决你的问题,请参考以下文章