引导启动和初始化

引导启动和初始化

1.引导启动

1.1启动函数入口

1.1.1 准备运行文件

hello.go

func main() {

    fmt.Println("hello world")

}

使用-gcflags "-N -l" 参数关闭编译器代码优化和函数内联,避免断点和单步执行无法准确对应源码行数,避免小函数和局部变量被优化掉。

 

go build -gcflags "-N -l" -o hello hello.go

1.1.2 找到入口文件

用GDB 设置断点命令看看这个 rt0_go入门文件在什么位置。

根据系统平台架构不相同,入口文件也会不一样,这里的入口文件在/usr/local/go/src/runtime/rt0_linux_amd64.s第八行。

 

gdb hello

(gdb) info files

Local exec file:

Entry point: 0x4561d0

 

(gdb) b *0x4561d0

Breakpoint 1 at 0x4561d0: file /usr/local/go/src/runtime/rt0_linux_amd64.s, line 8.

1.1.3 查看入口源码

TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8

         …省略代码…

         MOVQ      $main(SB), AX //跳转到62行

 

TEXT main(SB),NOSPLIT,$-8

         MOVQ $runtime·rt0_go(SB), AX

         JMP AX

 

注意:源码文件中的 “·” 符号编译后变成正常的 “.”。

(gdb) b runtime.rt0_go

Breakpoint 2 at 0x44a780: file /usr/local/go/src/runtime/asm_amd64.s, line 12.

TEXT runtime·rt0_go(SB),NOSPLIT,$0

         …省略代码…

         // 初始化函数

         CALL runtime·args(SB)

         CALL runtime·osinit(SB)

         CALL runtime·schedinit(SB)

         // 准备执行runtime.main

         MOVQ $runtime·mainPC(SB), AX

        //创建main goroutine

         CALL runtime·newproc(SB)

         // 让当前线程开始执行main goroutine

         CALL runtime·mstart(SB)

         RET

         DATA runtime·mainPC+0(SB)/8,$runtime·main(SB)

         GLOBL runtime·mainPC(SB),RODATA,$8

 

1.2 初始化流程

1.2.1 runtime.args

将参数(argc 和 argv )存储到静态变量中。

(gdb) b runtime.args

Breakpoint 4 at 0x434ec0: file /usr/local/go/src/runtime/runtime1.go, line 48.

 

func args(c int32, v **byte) {

         argc = c

         argv = v

         sysargs(c, v)

}

1.2.2 runtime.osinit

在启动过程中接下来调用的是 runtime.osinit 函数。在 Linux 系统上,这个函数唯 一做的事就是初始化 ncpu 变量,这个变量存储了当前系统的 CPU 的数量。这是通过一个系统调用来实现的。

(gdb) b runtime.osinit

Breakpoint 5 at 0x424290: file /usr/local/go/src/runtime/os1_linux.go, line 172.

func osinit() {

         ncpu = getproccount()

}

1.2.3 runtime.schedinit

接下便调用了 runtime.schedinit 函数,这个函数比较有意思。首先,它获得当前 goroutine 的指针,该指针指向一个 g 结构体。在讨论 TLS 实现的时候,我们就已经讨论过这个指针是如何存储的。接下来,它会调用 runtime.raceinit。这里我们不会讨论 runtime.raceinit 函数,因为正常情况下竞争条件(race condition)被禁止时,这个函数是不会被调用的。随后,runtime.schedinit 函数中还会调用另外一些初始化函数。

 

(gdb) b runtime.schedinit

Breakpoint 6 at 0x429eb0: file /usr/local/go/src/runtime/proc1.go, line 40.

 

//运用时环境初始化构造都在里被调,内存分配,垃圾回收,调度器等。

func schedinit() {

         //最大系统线程数量设置

         sched.maxmcount = 10000

         //栈空间复用链表初始化

         stackinit()

         //内存分配器初始化

         mallocinit()

         //初始化当前M

         mcommoninit(_g_.m)

         //垃圾回收初始化

         gcinit()

         //初始化,调整P的数量

         if procresize(int32(procs)) != nil {

                   throw("unknown runnable goroutine during bootstrap")

         }

}

1.2.4 runtime.main

接下来要执行的是 runtime.main,而不是用户逻辑入口函数 main.main。

(gdb) b runtime.main

Breakpoint 10 at 0x428b70: file /usr/local/go/src/runtime/proc.go, line 28.

 

 

func main() {

         //系统后台监控(内存释放,并发调度,垃圾回收监控)

         systemstack(func() {

                   newm(sysmon, nil)

         })

         //执行runtime包所有的init函数

         runtime_init()

         //启动垃圾回收器(后台运行)

         gcenable()

         //执行用户用户包init(包括标准库)

         main_init()

         //执行用户逻辑入口,main.main函数

         main_main()

         //执行结束

         exit(0)

}

 

 

参考资料:http://blog.jobbole.com/author/scbzyhx/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值