GO语言性能问题的发现和解决

GO语言性能问题的发现和解决

作者:佚名 2018-11-22 15:07:17

云计算 事情起因于公司一位同事在内部邮件组中post了一个问题,一个使用了go1.8.3写的业务程序跑了一段时间后出现部分goroutine卡在等待一个锁ForkLock的现象,同事认为这是go1.8.3的bug,升级到go1.10后没有再重现。

事件起因

事情起因于公司一位同事在内部邮件组中post了一个问题,一个使用了go1.8.3写的业务程序跑了一段时间后出现部分goroutine卡在等待一个锁ForkLock的现象,同事认为这是go1.8.3的bug,升级到go1.10后没有再重现。为了搞清楚这个事情,同事在github上发了issue https://github.com/golang/go/issues/26836,期间也做了很多重现的尝试,但并未重现。

我浏览了一下出现该问题的业务代码,大概的使用方式是父进程调用os/exec下的Command开子进程执行shell命令。Command后面会调用golang封装的forkExec来开子进程并执行命令,forkExec使用了ForkLock。

问题分析

ForkLock 的存在是为了避免下面的情况:在有多个goroutine同时fork exec的情况下, 为了子进程只继承它需要的文件描述符,需要在父进程在创建这些文件描述符的时候加上O_CLOEXEC标志,这样在子进程中这些描述符是关闭的,子进程按需把自己需要继承的描述符打开即可。

Linux在2.6.27之后,打开文件或者管道,和设置O_CLOEXEC是一个原子操作,因此问题不大,但golang对内核版本的要求是2.6.23及以上,另外Unix系统中,open和设置O_CLOEXEC是两个操作,如果在两个操作之间发生fork, 子进程就可能继承它不需要的文件描述符,因此需要加锁。重点看下forkExec时候的源代码:

从问题的现象看,肯定是某goroutine在forkExecPipe或者forkAndExecInChild这两步卡住了,锁没释放,因此有些goroutine一直拿不到锁,饥饿致死。forkExecPipe***调用的是内核pipe2, forkAndExecInChild***调用的是内核clone和exec。

原因猜测

pipe2是一个快速系统调用,因此可能block的系统调用是clone和exec, 加上在go1.10上这个问题没有重现,对比go1.8代码和go1.9在forkAndExecInChild函数上的差异:

go1.8

go1.9

go1.9增加了CLONE_VFORK和CLONE_VM 。只带SIGCHILD的clone可以认为类似于fork(***都是调用do_fork), fork的问题是,在父进程占用内存越大性能越差,具体可以看这个链接:https://bugzilla.redhat.com/show_bug.cgi?id=682922

这个case 2011年提出,今年7月还在更新,这个case反馈的问题是,尽管Linux kernel 引入copy-on-write机制,但fork的时候依然要拷贝页表,进程虚拟内存越大,需要拷贝的页表项越多,因此fork越慢。Golang的讨论组有人测试过,heap size在2G的情况下,fork耗时可以到毫秒级别, 正常是及几十微秒,上千倍差距。

Go1.9加上这两个参数是为了让子进程和父进程共享内存,相当于调用vfork, 不需要拷贝页表, 加快创建速度,从测试效果看,稳定在几十微妙。

所以一个合理的猜测是,在低于go1.9版写的程序中,当程序内存占用足够大,而且创建进程频率足够频繁,会导致ForkLock长时间等待。

实验论证

我用go1.8.3写了一个测试程序,在2核4G的虚拟机(kernel 3.10.0-693.17.1.el7.x86_64)下测试。

在外部每隔10秒,给这个程序发SIGUSR1信号,打印运行时堆栈,运行一段时间后,部分goroutine获取ForkLock的时间越来越长。见下面两图:

而在go1.9及以上版本上并未出现上述情况,这个结果我觉得已经可以说明问题。升级版本到go1.9及以上版本可以解决该问题。

写在***

vfork是为了解决fork拷贝页表项导致的性能问题, 而且大部分场景fork之后是调用exec,exec要把所有页表删除重置新的页表, 实在没必要再拷贝页表项。但由于vfork父子进程共享内存,所以使用要很小心,如果子进程修改某个变量,会影响到父进程,而且kernel会挂起父进程,让子进程先执行,这些限制基本限制vfork只适合跟exec的场景,不如fork通用。

正因为vfork的使用需要小心,因此go1.9准备加入vfork发布之前,有人提出代码不够健壮,因为rawVforkSyscall返回之后,在父进程段还执行指令,这样子进程有机会破坏双方的共享栈,因此提了一个commit去让rawVforkSyscall在返回后,在父进程段什么都不做直接return,解决这个互相影响,如图所示:

如有兴趣深入了解,可以看下这个commit 的review,Rob Pike等人都有发言。

https://go-review.googlesource.com/c/go/+/46173

 

文章来源网络,作者:运维,如若转载,请注明出处:https://shuyeidc.com/wp/296063.html<

(0)
运维的头像运维
上一篇2025-05-21 20:16
下一篇 2025-05-21 20:17

相关推荐

  • 个人主题怎么制作?

    制作个人主题是一个将个人风格、兴趣或专业领域转化为视觉化或结构化内容的过程,无论是用于个人博客、作品集、社交媒体账号还是品牌形象,核心都是围绕“个人特色”展开,以下从定位、内容规划、视觉设计、技术实现四个维度,详细拆解制作个人主题的完整流程,明确主题定位:找到个人特色的核心主题定位是所有工作的起点,需要先回答……

    2025-11-20
    0
  • 社群营销管理关键是什么?

    社群营销的核心在于通过建立有温度、有价值、有归属感的社群,实现用户留存、转化和品牌传播,其管理需贯穿“目标定位-内容运营-用户互动-数据驱动-风险控制”全流程,以下从五个维度展开详细说明:明确社群定位与目标社群管理的首要任务是精准定位,需明确社群的核心价值(如行业交流、产品使用指导、兴趣分享等)、目标用户画像……

    2025-11-20
    0
  • 香港公司网站备案需要什么材料?

    香港公司进行网站备案是一个涉及多部门协调、流程相对严谨的过程,尤其需兼顾中国内地与香港两地的监管要求,由于香港公司注册地与中国内地不同,其网站若主要服务内地用户或使用内地服务器,需根据服务器位置、网站内容性质等,选择对应的备案路径(如工信部ICP备案或公安备案),以下从备案主体资格、流程步骤、材料准备、注意事项……

    2025-11-20
    0
  • 如何企业上云推广

    企业上云已成为数字化转型的核心战略,但推广过程中需结合行业特性、企业痛点与市场需求,构建系统性、多维度的推广体系,以下从市场定位、策略设计、执行落地及效果优化四个维度,详细拆解企业上云推广的实践路径,精准定位:明确目标企业与核心价值企业上云并非“一刀切”的方案,需先锁定目标客户群体,提炼差异化价值主张,客户分层……

    2025-11-20
    0
  • PS设计搜索框的实用技巧有哪些?

    在PS中设计一个美观且功能性的搜索框需要结合创意构思、视觉设计和用户体验考量,以下从设计思路、制作步骤、细节优化及交互预览等方面详细说明,帮助打造符合需求的搜索框,设计前的规划明确使用场景:根据网站或APP的整体风格确定搜索框的调性,例如极简风适合细线条和纯色,科技感适合渐变和发光效果,电商类则可能需要突出搜索……

    2025-11-20
    0

发表回复

您的邮箱地址不会被公开。必填项已用 * 标注