温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

go tool objdump怎么用

发布时间:2022-01-17 09:14:32 来源:亿速云 阅读:249 作者:iii 栏目:大数据

本篇内容主要讲解“go tool objdump怎么用”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“go tool objdump怎么用”吧!

1.用go tool objdump,可以看到任意函数的机器码、汇编指令、偏移。(go源码下面有一个cmd/internal/goobj包,可以读到.o文件的重定向信息,更好。)

2.修改里面的golang内部函数的相对跳转,指向加载者相同的函数的地址(仍然可以用go tool objdump看到函数的初始地址),常见的有runtime.newobject、runtime.convT2Eslice、runtime.panicindex、runtime.morestack_noctxt等runtime系列函数。

3.修改golang类型指针偏移(当对象转换成interface{}时候,需要一个类型指针),指向加载者相同的类型。

4.修改指向字符串,全局变量,自定义函数的偏移(一般都是相对偏移)。

5.写入mmap,并执行。

整体思路是,通过修改偏移,复用加载者所用到的函数、golang内部函数、golang类型信息等。

缺点:

1.可以自定义类型,但是不能将这些类型的对象赋值到interface{}(加载者已定义的类型可以),比如使用fmt.Println打印这些对象(但是可以打印这些对象的成员)。因为golang内部的一些全局变量(比如golang类型)可能存在指针,而且开始就初始化了。

2.不能在函数外初始化全局变量。(可能的解决方法:定义一个入口函数,在里面初始化,或者读取main.init函数,取出初始化代码。)

优点:

仍然使用golang和golang编译工具。

速度极快,体积极小。相当于复用了golang内部的调度器、内存分配器、类型系统等。

可以自定义。golang函数内的汇编足够简单,可以开发自己的工具来实现上面的思路。

golang本质就是GPM三个实体实现的调度。

G对应每个任务,P对应每个processor概念(就是会包含一堆的G,比如先执行G1,在执行G2)M对应系统线程,M(还包含系统栈之类的概念)绑定一个P之后就开始逐个运行P里面的G。

最基本的流程图就是雨痕给的 

后面雨痕对于GPM三者的解释也很到位。我这里不抄袭了。

2.初始化 

首先介绍的就是schedinit()里面主要是procresize函数。

这个procresize()就是调整系统里面P的数量。一般就是系统的cpu内核的数量,初始化时也实行多退少补的原则,只是退的时候要注意是否退出的P包含了当前P,如果是就需要一大堆的细节上的处理。

这里还有个所有P的管理结构

var allp [_MaxGomaxprocs + 1]*p

type schedt struct {

    pidle puintptr //  P  

    npidle uint32 //  P 

}

还有个提示,如果调用手动调用并修改runtime.GOMAXPROCS就会引发stopTheWorld以及startTheWorld,这两个动作本身是比较好耗时的,之后在startTheWorld执行的procresize()也是比较耗时的。

3.任务 G/P 

先举了个栗子,通过

go build -o test test.go

go tool objdump -s "main\.main" test

go add(x, y)会被汇编成类似

 CALL runtime.newproc(SB)

这种代码

然后就去runtime找了。

newproc(获取pc/ip地址以及入参等重要信息后)->newproc1

之后登场的G的数据结构

type g struct {

    stack stack  //执行栈

    sched gobuf  //用于保存执行现场

    goid  int64  //唯一序号

    gopc  uintptr //调用者 PC/IP

    startpc uintptr //任务函数

}

newproc1一开始就处理各种处理创建G,测试G,对齐地址,拷贝栈,保存现场的各种杂活儿。然后一个runqput(p, newg, true),被put进去了。

runqput有可能把g作为P.runnext,也可能放在末尾,也有可能丢到全局队列。

稍微介绍了g通过p然后进行二级缓存复用的逻辑,类似cache/object,central的做法。分别对应gfget, gfput两个函数。

所有的g 还有个全局应用allgs/allg,用来索引所有的G方便回收和shrinkstack。

补充了个细节只有本地的P队列堆满了才会丢到全局队列,而且一次会丢本地队列长度的一半,保证效率和多核均匀调度。

4.线程 M 

当结束runqput之后,开始wakep了, 

wake->startm->newm创建/或者notewakeup(&mp.park) 

newm->newosproc->linux调用

clone(cloneFlags,stk,unsafe.Pointer(mp),unsafe.Pointer(mp.g0),unsafe.Pointer(funcPC(mstart))) 开启系统线程,并且入口函数是mstart

所有m会被添加到allm链表,不会被释放,超过10000崩溃。

最后补充了两个细节1:m也是有复用的,mput&mget使用1级缓存。

然后说不要创建太多m啊,time.Sleep比C.sleep(1)要好,之类的。

5.执行 

上面说到newm的时候会注册系统线程并把mstart作为入口函数。

然后这里就讲mstart

mstart -> 

mstart1 aquirep绑定p ->

schedule()兼顾帮助垃圾回收标记之类的各种杂活,findrunable,->

调用execute->

各种准备好栈JMP入函数入口地址PC->

各种调用结束后RET指令把预先压入的goexit地址恢复到PC/IP->

将G返回服用链表->

重新schedule()

然后介绍了一下findrunable的主干:

1.通过runqget拿本地的P的东西, 

2.globrunqget 

3.检查netpoll任务 

4.尝试偷取其他P的任务。(基于CAS和atomicset弄的Work-Stealing算法)

… 

5.这后还会进行各种尝试,如果实在没有就stopm了。

Lockedg 

这是cgo的一个特定调用方式,会把当前的g和m绑定,而且只有在结束调用的时候才会松开。

一个m在调用schedule() 如果发现它是被某个G绑定的则会暂时休息。如果发现自己将要调用的G,是被别的m绑定的,则会将它唤醒,然后自己休眠。

所以每个cgo routine在调用完成之前都会有自己专属的一个G调用。cgo因此会产生大量的m。

到此,相信大家对“go tool objdump怎么用”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI