草庐IT

go - 在 Go 中什么时候仅通过 `uintptr` 引用对象是安全的?

coder 2023-06-27 原文

Go 编程语言在第 13.2 节中说这是代码是安全的并且 x 将始终 对垃圾收集器可见:

pb := (*int16)(unsafe.Pointer(
  uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
*pb = 42

并且这段代码是不安全的,因为 x 暂时对 垃圾收集器,它可以移动它,使 pb 成为悬空指针:

tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
pb := (*int16)(unsafe.Pointer(tmp))
*pb = 42

但我看不出这两个例子之间的区别。

在描述为安全的情况下,在 uintptr 被调用之后,唯一的 对 x 的引用是 uintptr 值,不是吗?有一个指针 它在同一行,但它是 uintptr 的一个参数,它已经运行, 所以没有任何东西引用参数,所以 Pointer 不是事件的,uintptr 是对该对象的唯一引用。

我看不出如何将 uintptr 存储在局部变量中而不是作为 表达式中间值使它更安全。不是局部变量 就像 tmp 无论如何在编译阶段被删除,成为匿名数据流边缘, 以便生成的代码在语义上应该是等价的?或者 Go 有 垃圾收集何时可以运行的一些规则?例如只有安全点 语句之间?但是第一个例子中的代码有方法调用,所以我 会假设它们总是安全点吗?

最佳答案

找到我在评论中暗示的引用here

A uintptr is an integer, not a reference. Converting a Pointer to a uintptr creates an integer value with no pointer semantics. Even if a uintptr holds the address of some object, the garbage collector will not update that uintptr's value if the object moves, nor will that uintptr keep the object from being reclaimed.

这意味着这个表达式:

pb := (*int16)(unsafe.Pointer(
  uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)))
*pb = 42

是安全的,因为你正在创建一个 uintptr,它被视为一个整数,而不是一个引用,但它会被立即分配(除非其他地方存在竞争条件, 的对象x 引用不能被 GC 处理)直到 赋值之后)。 uintptr(再次:整数类型)也立即转换为指针,将其转换为引用,以便 GC 管理 pb。这意味着:

  • uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)):一切安全,因为 x 显然是对对象的现有引用
  • pb 被分配了一个整数,该整数(通过转换)标记为对 int16 对象的引用

但是,当你这样写的时候:

tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
pb := (*int16)(unsafe.Pointer(tmp))

有可能在分配tmp(记住整数,而不是引用)之间,移动内存中的实际对象。正如文档中所说:tmp 更新。因此,当您分配 pb 时,您可能会得到一个无效指针。
将本例中的 tmp 视为第一种情况下的 x。它不是对对象的引用,而是你写的

tmp := 123456 // a random integer
pb := (*int16) (unsafe.Pointer(tmp)) // not safe, obviously

例如:

var pb *int16
tmp := uintptr(unsafe.Pointer(&x)) + unsafe.Offsetof(x.b)
go func() {
    time.Sleep(1 * time.Second)
    pb = (*int16)(unsafe.Pointer(tmp))
}()
// original value of x could be GC'ed here, before the goroutine starts, or the time.Sleep call returns
x = TypeOfX{
    b: 123,
}

关于go - 在 Go 中什么时候仅通过 `uintptr` 引用对象是安全的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42067478/

有关go - 在 Go 中什么时候仅通过 `uintptr` 引用对象是安全的?的更多相关文章

  1. ruby - 为什么我可以在 Ruby 中使用 Object#send 访问私有(private)/ protected 方法? - 2

    类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc

  2. ruby-on-rails - Rails - 子类化模型的设计模式是什么? - 2

    我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co

  3. ruby - 什么是填充的 Base64 编码字符串以及如何在 ruby​​ 中生成它们? - 2

    我正在使用的第三方API的文档状态:"[O]urAPIonlyacceptspaddedBase64encodedstrings."什么是“填充的Base64编码字符串”以及如何在Ruby中生成它们。下面的代码是我第一次尝试创建转换为Base64的JSON格式数据。xa=Base64.encode64(a.to_json) 最佳答案 他们说的padding其实就是Base64本身的一部分。它是末尾的“=”和“==”。Base64将3个字节的数据包编码为4个编码字符。所以如果你的输入数据有长度n和n%3=1=>"=="末尾用于填充n%

  4. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  5. ruby - 通过 rvm 升级 ruby​​gems 的问题 - 2

    尝试通过RVM将RubyGems升级到版本1.8.10并出现此错误:$rvmrubygemslatestRemovingoldRubygemsfiles...Installingrubygems-1.8.10forruby-1.9.2-p180...ERROR:Errorrunning'GEM_PATH="/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/ruby-1.9.2-p180@global:/Users/foo/.rvm/gems/ruby-1.9.2-p180:/Users/foo/.rvm/gems/rub

  6. ruby - 为什么 4.1%2 使用 Ruby 返回 0.0999999999999996?但是 4.2%2==0.2 - 2

    为什么4.1%2返回0.0999999999999996?但是4.2%2==0.2。 最佳答案 参见此处:WhatEveryProgrammerShouldKnowAboutFloating-PointArithmetic实数是无限的。计算机使用的位数有限(今天是32位、64位)。因此计算机进行的浮点运算不能代表所有的实数。0.1是这些数字之一。请注意,这不是与Ruby相关的问题,而是与所有编程语言相关的问题,因为它来自计算机表示实数的方式。 关于ruby-为什么4.1%2使用Ruby返

  7. ruby - 通过 erb 模板输出 ruby​​ 数组 - 2

    我正在使用puppet为ruby​​程序提供一组常量。我需要提供一组主机名,我的程序将对其进行迭代。在我之前使用的bash脚本中,我只是将它作为一个puppet变量hosts=>"host1,host2"我将其提供给bash脚本作为HOSTS=显然这对ruby​​不太适用——我需要它的格式hosts=["host1","host2"]自从phosts和putsmy_array.inspect提供输出["host1","host2"]我希望使用其中之一。不幸的是,我终其一生都无法弄清楚如何让它发挥作用。我尝试了以下各项:我发现某处他们指出我需要在函数调用前放置“function_”……这

  8. ruby - 如何使用 Ruby aws/s3 Gem 生成安全 URL 以从 s3 下载文件 - 2

    我正在编写一个小脚本来定位aws存储桶中的特定文件,并创建一个临时验证的url以发送给同事。(理想情况下,这将创建类似于在控制台上右键单击存储桶中的文件并复制链接地址的结果)。我研究过回形针,它似乎不符合这个标准,但我可能只是不知道它的全部功能。我尝试了以下方法:defauthenticated_url(file_name,bucket)AWS::S3::S3Object.url_for(file_name,bucket,:secure=>true,:expires=>20*60)end产生这种类型的结果:...-1.amazonaws.com/file_path/file.zip.A

  9. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  10. ruby - ruby 中的 TOPLEVEL_BINDING 是什么? - 2

    它不等于主线程的binding,这个toplevel作用域是什么?此作用域与主线程中的binding有何不同?>ruby-e'putsTOPLEVEL_BINDING===binding'false 最佳答案 事实是,TOPLEVEL_BINDING始终引用Binding的预定义全局实例,而Kernel#binding创建的新实例>Binding每次封装当前执行上下文。在顶层,它们都包含相同的绑定(bind),但它们不是同一个对象,您无法使用==或===测试它们的绑定(bind)相等性。putsTOPLEVEL_BINDINGput

随机推荐