分享
  1. 首页
  2. 文章

go在stack上干了神马?

mikespook · · 3913 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

  1. 对这个话题已经有深入理解的童鞋请绕道;
  2. 对这个话题感兴趣,且有极强学习能力的同学请阅读这里,并且不用回来了。
  3. 其他和我一样愚笨的IT民工们,继续向前冲吧......
  4. 【2013年3月21日】以下关于 stackless 的描述有致命的脑残错误,请忽略。感谢 @minux 指出。

首先,来看一段神奇的 golang 代码:

package main
var (
 i = 1
)
func main() {
 i = i + 1
 print(i, "\n")
 main()
}


熟悉 c 语言的人都知道,如果在 c 语言中编译执行类似的代码,程序最终会发生栈溢出(stack overflow),从而导致段错误(segmentation fault)。在 32 位环境下(我只有 32 位的实验环境)编译链接并执行这段代码,预期的"段错误"并没有出现。

程序长时间稳定的执行。同时,利用 top 命令,会看到程序所使用的内存不断上升,丝毫没有减少的痕迹。

祭出神器 gdb,来看一下这个 go 程序到底做了什么。

gdb stackless
...
(gdb) b main.main
...
(gdb) r
...
(gdb) disassemble

当输入 disassemble 命令后,gdb 帮我们反编译了程序代码:

 0x08048c00 <+0>:	mov %gs:0x0,%ecx
 0x08048c07 <+7>:	mov -0x8(%ecx),%ecx
 0x08048c0a <+10>:	cmp (%ecx),%esp
 0x08048c0c <+12>:	ja 0x8048c17 <main.main+23>
 0x08048c0e <+14>:	xor %edx,%edx
 0x08048c10 <+16>:	xor %eax,%eax
 0x08048c12 <+18>:	call 0x8049515 <runtime.morestack>
 0x08048c17 <+23>:	sub 0ドルx1c,%esp
 0x08048c1a <+26>:	mov 0x806c00c,%eax
 0x08048c20 <+32>:	inc %eax
 0x08048c21 <+33>:	mov %eax,0x806c00c
 0x08048c27 <+39>:	cltd 
 0x08048c28 <+40>:	mov %eax,(%esp)
 0x08048c2b <+43>:	mov %edx,0x4(%esp)
 0x08048c2f <+47>:	call 0x804ef1b <runtime.printint>
 0x08048c34 <+52>:	lea 0x80557b0,%esi
 0x08048c3a <+58>:	lea (%esp),%edi
 0x08048c3d <+61>:	cld 
 0x08048c3e <+62>:	movsl %ds:(%esi),%es:(%edi)
 0x08048c3f <+63>:	movsl %ds:(%esi),%es:(%edi)
 0x08048c40 <+64>:	call 0x804f099 <runtime.printstring>
 0x08048c45 <+69>:	call 0x8048c00 <main.main>
 0x08048c4a <+74>:	add 0ドルx1c,%esp
 0x08048c4d <+77>:	ret 

main.main+0到main.main+12处,我们看到取了两个值进行比较,当 (%ecx)大于%esp时,跳转到main.main+23。

main.main+23到main.main+64处,显然是我们程序的逻辑处理:变量i自加一、打印i、打印"\n"。

main.main+69再次调用地址 0x8048c00 的 main.main。

除了main.main+0到main.main+12的比较,main.main+14到main.main+18对 runtime.morestack 的调用外,这和一个普通的程序并无二至。

那么程序一开始比较两个数据和调用 runtime.morestack 是什么呢?

原来在程序一开始先比较了栈限制和已经使用的栈的大小(%esp)。当栈限制大于已经使用的栈的时候,说明空间充裕,则直接跳转到实际代码处执行(+23)。当栈限制小于等于时已经使用的栈时,执行 runtime.morestack(+18)申请更多的栈空间。

在 $(GOROOT)/src/pkg/runtime/stack.h 中,有略微详细的说明。

由此看来 golang 实现的(2013年03月21日 削除) 并不是 stackless,而 (削除ここまで)是在每次函数调用前判断栈空间是否足够,如果发现栈空间不够用,就立刻申请新的栈空间使用。当函数退出时,才释放这些栈空间。

还是在 gdb 中,执行:

(gdb) b runtime.morestack
...
(gdb) r

在函数调用 100 多次后(猜测 32 位跟 64 位应当不同,没试),触发了 runtime.morestack 申请新的栈空间。


有疑问加站长微信联系(非本文作者)

本文来自:mikespook 的博客

感谢作者:mikespook

查看原文:go在stack上干了神马?

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
3913 次点击
暂无回复
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏