Go语言中的地址空间是什么?

12

我试图理解Go语言并发编程的基础知识。几乎所有的文章都使用了术语"地址空间",例如:"所有goroutine共享同一地址空间"。这是什么意思?

我尝试从维基百科上理解以下主题,但并没有成功:

然而,目前对于我来说很难理解,因为我在内存管理和并发编程等领域的知识非常贫乏。有许多未知的词汇,如段、页、相对/绝对地址、VAS等。

有人能向我解释一下问题的基础知识吗?也许有一些有用的文章,我找不到。

2个回答

20

Golang spec:

"go"语句启动一个函数调用的独立并发线程控制,或者说是在同一个地址空间中的"goroutine"。

有人能解释一下这个问题的基本概念吗?

"地址空间"是一个通用术语,可以适用于许多上下文环境:

地址空间是通过组合足够唯一标识符来使地址在特定地址空间内变得清晰明了而创建的。

Dave Cheney的演示文稿 "Five things that make Go fast" 阐述了在同一 进程 地址空间中使用goroutine所解决的主要问题:堆栈管理

戴夫谈到了“地址空间”,首先提到了线程:
“因为进程在执行过程中可以在任何时候进行切换,所以操作系统需要存储所有这些寄存器的内容,因为它不知道哪些是当前正在使用的。”
这导致了线程的发展,它们在概念上与进程相同,但共享同一内存空间。
(所以这是关于内存的)
然后,戴夫说明了进程地址空间中的堆栈(由进程管理的地址):

http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-39.jpg

传统上,进程的地址空间中,堆位于内存底部,紧挨着程序(文本)并向上增长。栈位于虚拟地址空间的顶部,并向下增长。为了防止堆和栈互相覆盖造成灾难性后果,操作系统通常会在堆和栈之间放置一块不可写的内存区域,以确保如果它们发生碰撞,程序将会中止。对于线程,这可能会限制进程的堆大小。另请参见“堆和栈是什么?在哪里?”。

http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-41.jpg

随着程序中线程数量的增加,可用地址空间的数量将减少。
goroutine使用一种不同的方法,同时仍然共享相同的进程地址空间:
那些goroutine的堆栈需求如何呢?Go编译器在每个函数调用中插入一个检查来检查函数是否有足够的堆栈运行,而不是使用守卫页面。如果没有足够的堆栈空间,运行时可以分配更多的堆栈空间。
由于这个检查,goroutines的初始堆栈可以变得更小,这反过来又使得Go程序员能够将goroutines视为廉价的资源。
Go 1.3引入了一种管理这些堆栈的新方式:

http://dave.cheney.net/wp-content/uploads/2014/06/Gocon-2014-45.jpg

如果goroutine的堆栈太小,不必添加和删除额外的堆栈段,而是会分配一个新的更大的堆栈。旧堆栈的内容将被复制到新堆栈中,然后goroutine将继续使用新的更大堆栈。在第一次调用H之后,堆栈将足够大,可用堆栈空间的检查将始终成功。

非常感谢!特别是这个链接https://dev59.com/hHVD5IYBdhLWcg3wHn2d#80113! - Timur Fayzrakhmanov
@VonC,关于1.3版本的增长堆栈有一个问题。如果F()需要更多的堆栈,会发生什么?是将G()和H()向左移动以腾出空间,还是将F()复制到末尾,留下空闲空间?谢谢! - siritinga
@siritinga 我不确定,但是 F() 应该使用更大的堆栈进行复制(如 https://dev59.com/yWIk5IYBdhLWcg3wPcCB#22684602 所示)。 - VonC
谢谢VonC,我觉得我在这里混淆了概念。我想F()不能在图中增长,因为它没有运行,F()调用了G(),G()又调用了H(),它们都在同一个goroutine中。如果F()在不同的goroutine中,那么它将有自己的堆栈(每个goroutine一个堆栈)。据您所知,我是正确的吗? - siritinga
如果F()在不同的goroutine中,则它将拥有自己的堆栈(每个goroutine一个堆栈)。是的,这意味着在新的“连续堆栈”(http://golang.org/s/contigstacks)中,如果其中一个函数在当前堆栈中缺少空间,则整个堆栈都会被复制到更大的堆栈中。这也是http://agis.io/2014/03/25/contiguous-stacks-in-go.html所描述的内容。 - VonC

3
当你的应用程序在RAM上运行时,内存管理器会分配RAM中的地址给你的应用程序。这被称为地址空间。
概念: 处理器(CPU)以Fetch-Decode-Execute周期执行指令。它通过将指令获取到RAM(随机存储器)中来执行应用程序。这样做是因为从磁盘获取指令非常低效。有人需要跟踪内存使用情况,因此操作系统实现了内存管理器。你的应用程序由一些程序组成,在你的情况下是用Go编程语言编写的。当你执行脚本时,操作系统以上述方式执行指令。
阅读你的帖子后,我能够体会你的感受。随着你越来越多地编写程序,你提到的术语将变得更加熟悉。
我第一次遇到这些术语是从操作系统书,也就是恐龙书中了解到的。
希望这可以帮助到你。

1
当你的应用程序运行在RAM上时,内存管理器会将RAM中的地址分配给您的应用程序。 "在RAM上运行" 是什么意思?是操作系统还是程序分配内存? "Memory manager" 所属的是哪个部分?请详细解释一下。 - Timur Fayzrakhmanov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接