百摩网
当前位置: 首页 生活百科

go源码分析(语言源码阅读有感)

时间:2023-07-07 作者: 小编 阅读量: 2 栏目名: 生活百科

以下文章来源于Golang技术分享,作者机器铃砍菜刀Golang技术分享专注于Go语言的知识分享小菜刀读Go源码时,发现一个高频注释语句“XXXmustnotbecopiedAfterfirstuse“。例如sync包下的Pool、Cond、WaitGroup、Mutex、Map和atomoic.Vaule、strings.Builder等,都有该句注释。为什么注释文档中要强调nocopy?安全!

以下文章来源于Golang技术分享 ,作者机器铃砍菜刀

Golang技术分享

专注于Go语言的知识分享

小菜刀读Go源码时,发现一个高频注释语句“XXX must not be copied After first use“。例如sync包下的Pool、Cond、WaitGroup、Mutex、Map和atomoic.Vaule、strings.Builder等,都有该句注释。

为什么注释文档中要强调no copy?

安全!

如果结构体对象包含指针字段,当该对象被拷贝时,会使得两个对象中的指针字段变得不再安全。

typeSstruct{f1intf2*s}typesstruct{namestring}funcmain(){mOld:=S{f1:0,f2:&s{name:"mike"},}mNew:=mOld//拷贝mNew.f1=1mNew.f2.name="jane"fmt.Println(mOld.f1,mOld.f2)//输出:0&{jane}}

如上,结构体对象S中存在两个field,分别是f1和f2,其中f2是指向s类型的指针。当mNew复制了mOld之后,mNew对两个字段进行了改变,可以看到f1字段的更改,不会对mOld造成影响。但是,nNew中f2字段的修改也会把mOld中的f2字段修改掉,这引发了安全问题。

Go是如何保证no copy的?

1. runtime checking

  • strings.Builder中copy检查

funcmain(){varastrings.Buildera.Write([]byte("a"))b:=ab.Write([]byte("b"))}//运行报错:panic: strings: illegal use of non-zero Builder copied by value

报错信息,来源于strings.Builder的copycheck。

typeBuilderstruct{addr*Builder//ofreceiver,todetectcopiesbyvaluebuf[]byte}func(b*Builder)Write(p[]byte)(int,error){b.copyCheck()b.buf=append(b.buf,p...)returnlen(p),nil}func(b*Builder)copyCheck(){ifb.addr==nil{b.addr=(*Builder)(noescape(unsafe.Pointer(b)))}elseifb.addr!=b{panic("strings:illegaluseofnon-zeroBuildercopiedbyvalue")}}

在Builder中,addr是一个指向自身的指针。当对上文中的a复制给b时,a和b本身是不同的对象。因此,b.addr实际还是指向a的指针,这就会触发条件b.addr!=b,造成panic。

  • sync.Cond中copy检查

在源码中,拥有copy检查机制的还有sync.Cond。

typeCondstruct{noCopynoCopyLLockernotifynotifyListcheckercopyChecker}func(c*Cond)Wait(){c.checker.check()...}typecopyCheckeruintptrfunc(c*copyChecker)check(){ifuintptr(*c)!=uintptr(unsafe.Pointer(c))&&!atomic.CompareAndSwapUintptr((*uintptr)(c),0,uintptr(unsafe.Pointer(c)))&&uintptr(*c)!=uintptr(unsafe.Pointer(c)){panic("sync.Condiscopied")}}

这里的check函数初看不易明白。因此,定义一个相似的结构体对象,来探究这里的check函数究竟是如何做copy检查的。

typecondstruct{checkercopyChecker}typecopyCheckeruintptrfunc(c*copyChecker)check(){fmt.Printf("Before:c:%12v,*c:v,uintptr(*c):v,uintptr(unsafe.Pointer(c)):v\n",c,*c,uintptr(*c),uintptr(unsafe.Pointer(c)))swapped:=atomic.CompareAndSwapUintptr((*uintptr)(c),0,uintptr(unsafe.Pointer(c)))fmt.Printf("After:c:v,*c:v,uintptr(*c):v,uintptr(unsafe.Pointer(c)):v,swapped:v\n",c,*c,uintptr(*c),uintptr(unsafe.Pointer(c)),swapped)}funcmain(){varaconda.checker.check()b:=ab.checker.check()}//输出Before:c:0xc0000b4008,*c:0,uintptr(*c):0,uintptr(unsafe.Pointer(c)):824634458120After:c:0xc0000b4008,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458120,swapped:trueBefore:c:0xc0000b4040,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458176After:c:0xc0000b4040,*c:824634458120,uintptr(*c):824634458120,uintptr(unsafe.Pointer(c)):824634458176,swapped:false

这下,sync.Cond的copy检查就很清晰了。当a被b copy之后,uintptr(*c)和uintptr(unsafe.Pointer(c))的值是不同的,通过uint对象的原子比较方法CompareAndSwapUintptr将返回false,它证明了对象a被copy过,从而调用panic保护sync.Cond不被复制。

2. go vet checking

上述两个例子都是在程序编译后,runtime检查的。但是,正如文中开篇所述,sync包下的其他的对象如Pool、WaitGroup、Mutex、Map等,它们其实也需要copy检查机制,但是在源码中,却没有提供运行时检查。那该如何保证我们的代码中这些对象在使用中未被copy,从而避免潜在的安全问题呢?

Go在源代码src/sync/cond.go中的一段注释给了我们答案。

//noCopymaybeembeddedintostructswhichmustnotbecopied//afterthefirstuse.////Seehttps://golang.org/issues/8005#issuecomment-190753527//fordetails.typenoCopystruct{}//Lockisano-opusedby-copylockscheckerfrom`govet`.func(*noCopy)Lock(){}func(*noCopy)Unlock(){}

很明显,runtime时的copy检查虽然很重要,但是,该操作会影响程序的执行性能。Go官方目前只提供了strings.Builder和sync.Cond的runtime拷贝检查机制,对于其他需要nocopy对象类型来说,使用go vet工具来做静态编译检查。

具体实施来说,就是该对象,或对象中存在filed,它拥有Lock()和Unlock()方法,即实现sync.Locker接口。之后,可以通过go vet功能,来检查代码中该对象是否有被copy。

例如sync.Pool和sync.WaitGroup就内嵌了noCopy属性,sync.Mutex实现了sync.Locker接口,sync.Map内嵌了sync.Mutex。

  • 静态检查

// wg.gopackage mainimport "sync"func main() {var sm sync.Mutexsm.Lock()sm.Unlock()sm2 := smsm2.Lock()}

如上,sm在first use后,被copy给sm2。注意:该代码运行时,不会报错,但是却存在安全隐患。

$govetwg.go#command-line-arguments./wg.go:9:9:assignmentcopieslockvaluetosm2:sync.Mutex

通过以上命令,即可检查出sync.Mutex有被copy。因此,举一反三,如果在我们自己的项目开发中,定义某对象不能被copy,那么就可以参考Go源码中,嵌入noCopy结构体,最终通过go vet进行copy检查。

typenoCopystruct{}func(*noCopy)Lock(){}func(*noCopy)Unlock(){}typeMyTypestruct{noCopynoCopy...}

更多关于Go关于no copy的讨论请参考官方Github issue,地址:https://github.com/golang/go/issues/8005

    推荐阅读
  • 如何能更好地阅读(读书的方法)

    做好阅读计划无规矩不成方圆,想要提高阅读效率的第一步,就是要先做好自己的阅读计划,阅读计划里面要包括,阅读的主题是什么,每天阅读的时间是什么,阅读时长是多久,这些都要包含在阅读计划里,如果是还没有养成阅读习惯的小白宝宝,可以设定每天固定的时间去阅读,这不仅可以让你养成好的阅读习惯,还会帮你提高阅读效率,我来为大家科普一下关于如何能更好地阅读?下面希望有你要的答案,我们一起来看看吧!

  • 哪些食物不会转换成糖(每次人们去切它时都会流泪的是什么食物)

    哪些食物不会转换成糖洋葱又名为葱头,是许多美食家所极力推荐的养生蔬菜。洋葱被誉为“菜中皇后”,药食俱佳,功效不同凡响。本文将为大家介绍洋葱的功效与作用及禁忌。洋葱性温,味辛甘。

  • 河南出入境活猪价值1317.4万美元

    记者昨日从河南出入境检验检疫局获悉,今年前10个月,经该局检验检疫,我省供港活猪356批、56960头、1317.4万美元。据了解,我省是全国供港活猪的主要省份之一,目前共有供港注册猪场20个。14年来,我省已向香港市民供应活猪300多万头,实现14年安全无疫、7年无药残超标。

  • 百香果施肥管理方法(百香果怎么施肥)

    百香果一般都是移植的方法开始定植。促花肥是为了促进花朵的品质,提高营养吸收,施肥时间一般是在开花前的十五天左右,百香果已经成功的上了棚架。如果遇到了高温干旱的情况,应该将肥料与水兑在一起一起喷施,这样既保证了百香果的所需营养,又能为它及时补充水分。但是百香果的根部非常脆弱,所以在水肥的时候不宜开沟,防治肥料将根烧坏,且为了防止施肥后肥料流失,可以用淋湿的方式施肥。

  • 机动车发生车祸致死怎么判刑 机动车发生事故致人死亡

    对于赔偿,受害人遭受人身损害,因就医治疗支出的各项费用以及因误工减少的收入,包括医疗费、误工费、护理费、交通费、住宿费、住院伙食补助费、必要的营养费,赔偿义务人应当予以赔偿。受害人死亡的,赔偿义务人除应当根据抢救治疗情况赔偿本条第一款规定的相关费用外,还应当赔偿丧葬费、被扶养人生活费、死亡补偿费以及受害人亲属办理丧葬事宜支出的交通费、住宿费和误工损失等其他合理费用。

  • 市民卡有什么用 张家港市民卡有什么用

    此外,市民卡的金融账户挂失需拨打发卡银行客服热线挂失。正式挂失及补卡时请携带有效身份证件至市民卡服务银行制卡网点办理,一般立等可取。

  • 月光伤感朋友圈文案(与月亮有关的经典句子推荐)

    坐在院子里,托起下巴,享受着月光的沐浴,编织着未来,编织着理想,编织着美丽的少女的梦。迷蒙的月光,常使我多愁善感,亮朗的月光常使我豁然开朗精神为之一震。美丽的月光曾带给我多少梦幻般的幻想,我编织着美丽的人生。月色下,那些情窦初开的少女,在透彻的月光下卸下那淡淡的淡妆,那一刻女儿同这月色一样让吸引那多情的男儿。

  • 瓜子二手车交易罚款(深圳瓜子二手车销售私收客户车款)

    瓜子二手车销售私自收款,涉嫌诈骗被警方刑拘今年2月份,黄先生通过瓜子二手车APP认识了一名阮姓销售。该工作人员表示,瓜子平台已知悉此事,并与客户取得联系,针对部分签署了购车合同的客户,平台将妥善进行处理。由于目前公安机关已经介入调查,平台将会严格按照公安机关调查结果以及法院的最终判决履行相应的责任。

  • 简单的过日子的说说(过日子的句子介绍)

    简单的过日子的说说?跟着小编一起来看一看吧!但最好的幸福,是你给的在乎。有时,生活没什么惊天动地的目的,只化约到还活着这么个简单的念头。顺着心的方向走下去,那便是最温暖的家。简单生活,生活简单,这就是生活的真谛。我喜欢简单的生活,简单的思维,简单的人际关系,一切都是简简单单。生活给予你的是那么美好,简单的幸福,简单的人陪着你过你喜欢的简单生活,在最美的年纪享受简单的年华。

  • 咸鸭蛋的营养价值(你知道吗)

    丰富的钙:鸭蛋在腌制成为咸蛋之后,其中所含有的蛋白质含量会明显的减少,由鲜蛋的每百克含14.7克下降为10.4克。不仅如此,咸鸭蛋中的碳水化合物含量也有明显的增加,由鲜蛋每百克含1.6克,上升为10.7克。所以它们的营养价值是基本相同的各有好处,适当的食用对身体没有危害,因此在日常生活中可以放心的食用。