news 2026/5/1 5:20:12

12.15 学习笔记

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
12.15 学习笔记

一、Linux 基础命令类(面试口吻回答)

1. 如何查找工程下是否存在某个文件?

面试官您好,查找工程下指定文件我常用find命令,核心用法:
find [工程目录路径] -name "目标文件名"
示例:工程路径/home/project/robot,查找config.jsonfind /home/project/robot -name "config.json"
扩展:忽略大小写加-iname,限制查找深度(如仅查2层)加-maxdepth 2,提升检索效率。

2. 如何查找哪些文件包含了某个头文件?

我一般用grep递归查找,核心写法:
grep -rl "#include <目标头文件.h>" [工程目录路径]
示例:找包含robot_api.h的文件 →grep -rl "#include <robot_api.h>" /home/project/robot
参数说明:-r递归遍历目录,-l仅输出匹配文件名;兼容双引号头文件写法可简化为grep -rl "robot_api.h" 工程路径

3. 如何查看某个进程的线程状态?

  • 精准查看:top -H -p [进程PID]-H显示线程级信息,-p指定进程ID,可查线程CPU占用、状态、线程ID;
  • 简洁查看:ps -T -p [PID],快速列出线程PID和状态;
  • 进阶排查:pstack [PID],查看线程调用栈,定位线程阻塞问题。

二、编程手撕题(Go语言实现)

用无缓冲Channel实现三个协程轮流打印ABC

面试讲解

面试官您好!用无缓冲Channel实现该需求,核心利用无缓冲Channel的阻塞特性:无缓冲Channel的发送/接收操作必须配对完成,否则会阻塞,以此精准控制协程的执行顺序,完全避免无关协程被唤醒的问题(替代sync.CondSignal()精准唤醒逻辑)。

实现思路:

  1. 定义3个无缓冲Channel(chAchBchC),分别控制A、B、C协程的执行时机;
  2. 初始化时仅向chA发送信号(触发A协程执行),B、C协程初始阻塞;
  3. 每个协程执行完打印后,向“下一个协程对应的Channel”发送信号,触发下一个协程执行,形成闭环。
Go代码实现
packagemainimport("fmt""sync")funcmain(){varwg sync.WaitGroup// 定义3个无缓冲Channel,控制协程执行顺序chA:=make(chanstruct{})chB:=make(chanstruct{})chC:=make(chanstruct{})wg.Add(3)// 打印A的协程:接收chA信号,执行后向chB发信号gofunc(){deferwg.Done()fori:=0;i<10;i++{<-chA// 阻塞,直到收到信号fmt.Print("A")chB<-struct{}{}// 触发B协程执行}}()// 打印B的协程:接收chB信号,执行后向chC发信号gofunc(){deferwg.Done()fori:=0;i<10;i++{<-chB// 阻塞,直到收到信号fmt.Print("B")chC<-struct{}{}// 触发C协程执行}}()// 打印C的协程:接收chC信号,执行后向chA发信号gofunc(){deferwg.Done()fori:=0;i<10;i++{<-chC// 阻塞,直到收到信号fmt.Println("C")chA<-struct{}{}// 触发A协程执行,形成闭环}}()// 初始化:向chA发送信号,启动第一个协程(A)chA<-struct{}{}// 等待所有协程执行完毕wg.Wait()// 关闭Channel(可选,防止资源泄漏)close(chA)close(chB)close(chC)}
核心优势(面试补充)
  1. 更简洁:相比sync.Cond无需维护counter状态和互斥锁,完全通过Channel的阻塞特性控制顺序,代码量更少;
  2. 天然线程安全:无缓冲Channel的发送/接收操作本身是线程安全的,无需额外加锁;
  3. 无无效唤醒:每个协程仅在收到对应Channel信号时执行,其他时间均阻塞,不存在“被唤醒后因状态不满足再次等待”的无效操作,效率更高。
执行逻辑拆解(面试口述版)
  1. 程序启动后,先向chA发送空结构体,触发A协程从<-chA处唤醒,打印“A”;
  2. A协程打印后向chB发送信号,B协程从<-chB处唤醒,打印“B”;
  3. B协程打印后向chC发送信号,C协程从<-chC处唤醒,打印“C”;
  4. C协程打印后向chA发送信号,回到第一步,形成“A→B→C→A”的闭环;
  5. 每个协程循环10次后结束,wg.Wait()等待所有协程执行完毕,程序退出。

这种实现方式是Go语言“以通信代替共享内存”设计理念的典型体现,也是面试中更推荐的简洁方案。

三、网络编程高频考点

TCP三次握手阶段解答

① 客户端net.Dial()触发底层connect(),发送SYN包启动三次握手;
② 服务端Listen()监听后收到SYN包,回复SYN+ACK完成第二次握手;
③ 客户端收到SYN+ACK后回复ACK,三次握手完成,Dial()返回;
④ 服务端将已建立连接放入监听队列,Accept()取出连接并返回;
综上,三次握手发生在客户端Dial()后、服务端Accept()返回前

四、核心概念问答(面试口吻)

1. 进程具体有哪些资源

面试官您好,进程作为OS资源分配基本单位,占用资源分6类:

  1. 内存资源:独立地址空间(代码段/数据段/堆/栈/共享内存)、页表、内核态内存描述符(如Linux mm_struct);
  2. CPU资源:PCB(进程控制块),含PID、优先级、状态、CPU上下文(寄存器/PC)、调度信息;
  3. 文件IO资源:文件描述符表(FD)、文件锁、IO缓冲区、设备句柄;
  4. 通信资源:信号量、消息队列、管道、共享内存(IPC);
  5. 特权/上下文资源:UID/GID、环境变量、信号处理函数、网络连接(socket);
  6. 其他资源:定时器、进程组/会话、内存限制(ulimit)、CPU配额(cgroup)。

补充:线程共享进程资源,仅私有栈和CPU上下文;进程是“资源分配单位”,线程是“调度单位”。

2. 其他语言协程的用户态实现

面试官您好,不同语言协程核心是“用户态调度+上下文切换+IO多路复用”,主流实现如下:

语言核心模型实现细节
Python协作式+事件循环async def/await定义协程,IO阻塞时主动让出CPU;底层epoll实现IO多路复用
C++(libco)抢占+协作式ucontext_t做上下文切换;co_yield主动让出,SIGALRM信号强制抢占;epoll管理IO
Java(Loom)JVM层Fiber用户态调度Fiber,IO阻塞自动让出CPU,复用JVM线程调度但无内核态切换
Lua协作式+协程对象coroutine.create/resume/yield控制,无自动切换,适用于简单异步场景

核心共性:用户态上下文切换+调度器+IO多路复用,区别仅在调度方式(协作/抢占)和上下文实现(ucontext_t/字节码/VM)。

3. K8s中Go服务GOMAXPROCS默认值

面试官您好,核心结论分版本:

  • Go 1.19及之前:默认等于宿主机CPU核数(无视Pod CPU配额);
  • Go 1.20及之后:默认读取cgroup CPU限制(Pod配额),如Pod配2核则GOMAXPROCS=2。

补充:

  • 原理:Go 1.20+读取/sys/fs/cgroup/cpu下的配额文件,适配容器资源;
  • 建议:生产环境显式设置GOMAXPROCS(环境变量/代码),避免调度低效;
  • 影响:Pod配额2核但GOMAXPROCS=16,会导致M远大于P,CPU上下文切换开销飙升。

4. 算法题:最长公共子序列(DP实现)

面试讲解

核心是状态定义+转移:

  • 状态:dp[i][j]表示s1前i个、s2前j个字符的LCS长度;
  • 转移:s1[i-1]==s2[j-1] →dp[i][j] = dp[i-1][j-1]+1;否则dp[i][j] = max(dp[i-1][j], dp[i][j-1])
  • 初始:dp[0][j]=dp[i][0]=0
Go代码(基础版)
packagemainimport"fmt"funclongestCommonSubsequence(text1,text2string)int{m,n:=len(text1),len(text2)dp:=make([][]int,m+1)fori:=rangedp{dp[i]=make([]int,n+1)}fori:=1;i<=m;i++{forj:=1;j<=n;j++{iftext1[i-1]==text2[j-1]{dp[i][j]=dp[i-1][j-1]+1}else{dp[i][j]=max(dp[i-1][j],dp[i][j-1])}}}returndp[m][n]}funcmax(a,bint)int{ifa>b{returna};returnb}funcmain(){fmt.Println(longestCommonSubsequence("abcde","ace"))// 输出3}
优化版(空间O(n))
funclongestCommonSubsequenceOpt(text1,text2string)int{m,n:=len(text1),len(text2)dp:=make([]int,n+1)fori:=1;i<=m;i++{prev:=0forj:=1;j<=n;j++{temp:=dp[j]iftext1[i-1]==text2[j-1]{dp[j]=prev+1}else{dp[j]=max(dp[j],dp[j-1])}prev=temp}}returndp[n]}

5. 算法题:10亿整数找最大100个数(小顶堆思路)

面试讲解

核心用小顶堆(O(NlogK)时间+O(K)空间),避免全量排序(O(NlogN)),步骤如下:

  1. 初始化大小为100的小顶堆,放入前100个整数;
  2. 遍历剩余整数:
    • 若当前数>堆顶:弹出堆顶,插入当前数,调整堆;
    • 若≤堆顶:直接跳过;
  3. 遍历完成后,堆中100个数即为最大;如需从大到小输出,反转堆元素即可。

补充:

  • 数据读取:10亿数分批次流式读取,避免内存溢出;
  • 分布式场景:先分节点各找Top100,再汇总找全局Top100。
Go代码实现
packagemainimport("container/heap""fmt")// 小顶堆定义typeIntHeap[]intfunc(h IntHeap)Len()int{returnlen(h)}func(h IntHeap)Less(i,jint)bool{returnh[i]<h[j]}func(h IntHeap)Swap(i,jint){h[i],h[j]=h[j],h[i]}func(h*IntHeap)Push(xinterface{}){*h=append(*h,x.(int))}func(h*IntHeap)Pop()interface{}{old:=*h n:=len(old)x:=old[n-1]*h=old[:n-1]returnx}// 找Top K最大数funcfindTopK(nums[]int,kint)[]int{ifk<=0||len(nums)==0{returnnil}h:=&IntHeap{}heap.Init(h)for_,num:=rangenums{ifh.Len()<k{heap.Push(h,num)}elseifnum>(*h)[0]{heap.Pop(h)heap.Push(h,num)}}// 反转堆元素,从大到小输出res:=make([]int,k)fori:=k-1;i>=0;i--{res[i]=heap.Pop(h).(int)}returnres}funcmain(){nums:=[]int{5,2,9,1,7,6,8,3,4,100,99,88}fmt.Println(findTopK(nums,3))// 输出[100 99 88]}

五、Go语言核心考点

1. defer函数能否修改变量

面试官您好,核心取决于变量类型和是否为返回值,分3种场景:

  1. 普通局部变量:可修改。defer延迟执行函数体,变量引用有效(如defer中i++,最终i值改变);
  2. 命名返回值:可修改。如func calc() (res int) { defer res++ ; return 1 },返回值为2(return先赋值,defer后修改);
  3. 匿名返回值:不可修改。如func calc() int { var i int; defer i++ ; return i },return拷贝i值到匿名返回值,defer修改的是局部i,不影响返回值。

补充:defer注册时立刻计算参数值(如defer fmt.Println(i),注册时确定i值),但函数体延迟执行。

2. Go执行顺序:import/var/const/init()/main()

面试官您好,执行顺序为:

  1. import:递归初始化依赖包(main→pkgA→pkgB,先初始化pkgB);
  2. const:按声明顺序初始化包级常量(无依赖问题);
  3. var:常量初始化后,按“声明顺序+依赖分析”初始化包级变量;
  4. init():每个包var初始化完成后执行init(同一包多文件按文件名排序);
  5. main():所有依赖包+main包init执行完毕后,进入main函数(程序入口)。

示例:main导入pkg1 → import pkg1 → pkg1 const → pkg1 var → pkg1 init → main const → main var → main init → main()。

3. GMP调度模型

面试官您好,GMP是Go并发核心,对应3个组件:

  • G(Goroutine):轻量级协程,默认栈2KB(动态扩缩),是任务执行单元;
  • M(Machine):OS内核线程,G的执行载体,同一时间仅绑定1个P;
  • P(Processor):逻辑处理器,维护本地G队列(LRQ),数量=GOMAXPROCS(默认CPU核数),决定并行执行的M数。

调度流程

  1. 初始化GOMAXPROCS个P,每个P绑定1个M;
  2. 主G加入P的LRQ,M从LRQ取G执行;
  3. LRQ空时,P从全局队列(GRQ)/其他P的LRQ“偷取”G(负载均衡);
  4. G阻塞(syscall)时,M与P解绑,P绑定新M继续执行;G阻塞结束后入GRQ等待调度。

优势:用户态调度减少内核切换开销,多核并行提升执行效率。

4. 进程/线程/协程的区别

维度进程线程协程(Goroutine)
资源占用大(独立地址空间)中(共享进程资源)极小(共享线程资源)
调度方式内核态(OS调度)内核态(OS调度)用户态(runtime)
并发能力低(单机数百个)中(单机数千个)极高(单机百万级)
安全性高(地址空间隔离)中(需锁同步)中(需channel/sync)
生命周期OS管理OS管理语言runtime管理

总结:进程是“资源分配单位”,线程是“OS调度单位”,协程是“用户态调度单位”。

5. 进程/线程/协程的通信方式

(1)进程间(IPC)
  • 管道(匿名/命名)、消息队列、共享内存(最快,需锁同步)、信号量、信号、Socket(跨网络/本地)。
(2)线程间
  • 共享内存(全局变量/堆,需Mutex/RWMutex/Cond同步)、TLS(线程本地存储)、信号量/条件变量。
(3)协程间
  • Go:channel(推荐,通信实现共享)、sync包(共享内存+锁);
  • 其他:Python(事件循环+队列)、C++ libco(全局队列+信号量)。

6. Channel底层实现原理

面试官您好,Channel底层是runtime/hchan结构体,核心字段:buf(环形队列)、sendq/recvq(阻塞G队列)、lock(互斥锁)、cap/len(容量/当前长度)。

工作流程

  • 无缓冲Channel(cap=0):发送/接收需配对,无对应方则阻塞,唤醒时直接拷贝数据;
  • 缓冲Channel(cap>0):队列未满则入队,满则发送G阻塞;队列非空则出队,空则接收G阻塞;

补充:close(ch)会唤醒所有阻塞G(发送G panic,接收G取零值);单向Channel是编译期限制,底层仍是hchan;所有操作通过lock保证线程安全。

7. ES实现原理

面试官您好,ES基于Lucene,核心是“倒排索引+分片+近实时搜索”:

  1. 倒排索引:Term(词条)→ Posting List(文档ID列表),词典用FST压缩,Posting List差值编码,支撑全文检索;
  2. 分布式架构:索引拆分为主分片(不可改数量)+ 副本(高可用/分担读压力),按hash(文档ID)%主分片数路由;
  3. 近实时搜索:写入先存内存缓冲区+Translog,1秒刷新为Segment(不可修改),Translog阈值触发Flush合并Segment,延迟约1秒。

补充:支持IK分词(中文)、BM25相关性评分,适用于全文检索/日志分析。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/26 22:50:18

2025项目管理软件怎么选?十大热门工具深度评测,避坑指南来了

无论是中小型团队的轻量协作&#xff0c;还是大型企业的复杂项目管控&#xff0c;选择合适的工具能让管理效率翻倍。精选10款好用的项目管理软件&#xff0c;从核心功能、适用场景到优劣势进行深度解析&#xff1a;进度猫 核心定位&#xff1a;国内领先的轻量级可视化项目管理工…

作者头像 李华
网站建设 2026/4/18 7:03:52

SpringBoot4.0合 Scala/Java 混编?我踩过的坑,请你绕行

SpringBoot4.0合 Scala/Java 混编&#xff1f;我踩过的坑&#xff0c;请你绕行 本节说明一下Scala和Java混合开发时&#xff0c;本地运行没问题&#xff0c;只要上线部署打成Jar包就会找不到启动类&#xff0c;启动时就会报错 1. 需要配置两个东西 1. Scala的依赖2. Scala的打…

作者头像 李华
网站建设 2026/4/23 20:41:16

WebUploader支持国密加密的大文件分块上传方案?

前端老哥的外包求生记&#xff1a;20G大文件上传系统&#xff08;Vue3原生JS&#xff09; 兄弟们&#xff01;我是福建一名“头发渐少但代码不秃”的前端程序员&#xff0c;最近接了个外包活——给客户做文件管理系统&#xff0c;核心需求就一个&#xff1a;“20G大文件文件夹…

作者头像 李华
网站建设 2026/5/1 0:33:01

提高表达能力必看的七本演讲与口才类书籍推荐

古语有云&#xff1a;「三寸之舌&#xff0c;强于百万之师」&#xff0c;足见口才的力量。TED掌门人克里斯也曾说&#xff1a;无论今天公众演讲有多重要&#xff0c;未来只会更重要&#xff01;为了帮助大家提升演讲与口才能力&#xff0c;特此推荐七本演讲方面的经典书籍&…

作者头像 李华
网站建设 2026/4/25 14:40:46

百度热搜榜:近期Qwen3-VL-8B关注度持续攀升原因

Qwen3-VL-8B为何突然火了&#xff1f;轻量多模态模型的落地突围 在AI技术不断向“更聪明”演进的今天&#xff0c;一个现象值得关注&#xff1a;越来越多企业不再盲目追逐千亿参数的大模型&#xff0c;而是将目光投向像Qwen3-VL-8B这样参数适中、部署灵活、能真正用起来的轻量级…

作者头像 李华
网站建设 2026/4/19 18:26:21

Mapbox GL JS 核心表达式:`all` 多条件且判断完全教程

all 是 Mapbox GL JS 表达式系统中最核心的逻辑判断表达式之一&#xff0c;用于实现多条件“且”判断——只有所有输入的布尔表达式结果均为 true 时&#xff0c;all 才返回 true&#xff1b;且支持短路求值&#xff08;一旦某个条件为 false&#xff0c;立即停止后续条件计算&…

作者头像 李华