更新于 

50K Gopher面试题

来源

问题列表

1.项目中用到的锁

项目中主要使用到了互斥锁、读写锁、原子锁,以及基于 Redis 的分布式锁。

2.介绍一下线程安全的共享内存方式
  1. 通过对共享对象进行加锁,保证线程读写安全
  2. 保存两块共享内存,一块用于读操作,一块用于写操作。
    初始时,两块共享内存内容一致。读操作均是读取第一块共享内存的数据;写操作均是写第二块共享内存。
    在多个读操作一个写操作的情况下,读操作均从第一块共享内存读取,写操作修改第二块共享内存的数据。直至写操作完成,交换两块共享内存的下标,即第二块共享内存用于接下来的读操作,第一块共享内存根据第二块共享内存数据更新,用于接来下的写操作。
3.介绍一下goroutine
4.goroutine的自旋占用资源如何解决,gmp
5.介绍Linux系统信号
  • SIGHUP 用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一 session 内的各个作业, 这时它们与控制终端不再关联。当用户登录 Linux 时,此时所有的前后台进程组都属于这个 session,退出登录时,这个信号默认操作为终止进程。不过用户可以捕捉这个信号,忽略它或者执行其他操作。
  • SIGINT 程序终止信号,用户键入INTR字符(通常Ctrl+C)时触发,用于通知前台进程组终止进程。
  • SIGNQUITSIGNINT类似,但通常由QUIT字符(通常Ctrl+\)来控制,进程在因收到 SIGQUIT 信号退出时会产生一个 core 文件,类似一个程序错误信号。
  • SIGILL 执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
  • SIGTRAP 由断点指令或其它trap指令产生. 由debugger使用。
  • SIGABRT 调用abort函数生成的信号。
  • SIGBUS 非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的
  • SIGFPE 在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
  • SIGKILL 用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。kill -9 PID 使用的就是这个信号。
  • SIGUSR1 用户自定义
  • SIGUSR2 用户自定义
  • SIGSEGV 试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据。
  • SIGPIPE 管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
  • SIGALRM 时钟定时信号, 计算的是实际的时间或时钟时间。alarm函数使用该信号。
  • SIGTERM 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。
  • SIGCHLD 子进程结束时, 父进程会收到这个信号。如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。
  • SIGCONT 让一个停止(stopped)的进程继续执行。 本信号不能被阻塞,可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作。例如: 重新显示提示符
  • SIGSTOP 停止(stopped)进程的执行。它和terminate以及interrupt的区别: 该进程还未结束, 只是暂停执行。 本信号不能被阻塞, 处理或忽略。
  • SIGTSTP 停止进程的运行, 但该信号可以被处理和忽略。 用户键入SUSP字符时(通常是Ctrl+Z)发出这个信号
  • SIGTTIN 当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
  • SIGTTOU 类似于SIGTTIN, 但在写终端(或修改终端模式)时收到。
  • SIGURG 带外数据处理,套接字上有紧急数据时,向当前正在运行的进程发出些信号,报告有紧急数据到达。如网络带外数据到达,默认动作为忽略该信号。
  • SIGXCPU 超过CPU时间资源限制。这个限制可以由getrlimit/setrlimit来读取/改变。
  • SIGXFSZ 超过文件的最大长度设置。默认动作为终止进程。
  • SIGVTALRM 虚拟时钟信号。类似于SIGALRM, 但是计算的是该进程占用的CPU时间。默认动作为终止进程。
  • SIGPROF 类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间。默认动作为终止进程。
  • SIGWINCH 窗口大小改变时发出。默认动作为忽略该信号。
  • SIGIO 文件描述符准备就绪, 可以开始进行输入/输出操作。此信号向进程指示发出了一个异步IO事件。默认动作为忽略。
  • SIGPWR 关机。默认动作为终止进程。
  • SIGSYS 无效的系统调用。默认动作为终止进程并产生core文件。
  • SIGRTMIN LINUX的实时信号,没有固定的含义(可以由用户自定义)。所有的实时信号的默认动作都为终止进程。
6.goroutine抢占时机,gc栈扫描
7.Gc触发时机
8.是否了解其他gc机制
9.内存管理方式
10.Channel分配在堆上还是在栈上? 哪些对象分配在堆上? 哪些对象分配在栈上?
11.代码效率分析,考虑局部性原理
12.多核CPU下,cache如何保持一致,不冲突
13.uint类型溢出
14.聊聊rune类型
15.介绍一下channel,有缓冲和无缓冲的区别
16.channel是否线程安全
17.介绍一下Mutex的实现,是悲观锁还是乐观锁
18.Mutex几种模式?
19.Muxtez可以做自旋锁?
20.介绍一下RWMutex
21.介绍一下大对象和小对象, 为什么小对象多了会造成gc压力?
22.介绍项目中遇到的oop情况
23.介绍项目中遇到的坑
24.如果指定指令执行的顺序
25.什么是写屏障、混合写屏障, 如何实现?
26.gc的stw是怎么回事
27.协程之间是怎么调度的
28.简单聊聊内存逃逸
29.为sync.WaitGroup中Wait函数支持 WaitTimeout 功能.
30.字符串转成byte数组,会发生内存拷贝吗?
31.http包的内存泄漏
32.Goroutine调度策略
33.对已经关闭的的chan进行读写,会怎么样? 为什么?
34.实现阻塞读的并发安全Map
35.什么是goroutine leak?
36.data race问题怎么解决? 能不能不加锁解决这个问题?
37.epoll原理
38.etcd怎么实现分布式锁?
39.滑动窗口的概念以及应用?
40.grpc内部原理是什么?
41.http2的特点是什么,与http1.1的对比。
42.time.Now有几次系统调用? 如何优化?
43.空struct{}是否使用过? 会在什么情况下使用, 举例说明一下。

空struct{}主要能够节省内存。第一,如果使用的是map,而且map又很长,通常会节省不少资源;第二,空struct{}也在向别人表明,这里并不需要一个值。

  1. map中节省内存
    1
    2
    3
    4
    5
    6
    set := make(map[string]struct{})
    for _, value := range []string{"apple", "orange", "apple"} {
    set[value] = struct{}{}
    }
    fmt.Println(set)
    // Output: map[orange:{} apple:{}]
  2. 空struct{}仅包含需要方法。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    type Lamp struct{}

    func (l Lamp) On() {
    println("On")

    }
    func (l Lamp) Off() {
    println("Off")
    }

    func main() {
    // Case #1.
    var lamp Lamp
    lamp.On()
    lamp.Off()
    // Output:
    // on
    // off

    // Case #2.
    Lamp{}.On()
    Lamp{}.Off()
    // Output:
    // on
    // off
    }
  3. channel仅需要一个信号触发, 无需传输真实数据
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    func worker(ch chan struct{}) {
    // Receive a message from the main program.
    <-ch
    println("abnerwei")

    // Send a message to the main program.
    close(ch)
    }

    func main() {
    ch := make(chan struct{})
    go worker(ch)

    // Send a message to a worker.
    ch <- struct{}{}

    // Receive a message from the worker.
    <-ch
    println("abnerwei")
    // Output:
    // abnerwei
    // abnerwei
    }
44.聊聊runtime
45.介绍下你平时都是怎么调试bug以及性能问题的?
46.通过通信来共享内存, 而不是通过共享内存而通信, 怎么理解这句话, 如何处理共享变量?
47.chan比mutex更轻么? 还有更轻量的方法么?
48.什么时候用chan不如mutex效率高?
50K能招到这样的Gopher么?

欢迎👏大家补充面试题答案。