【问题标题】:Golang, processes and shared memoryGolang,进程和共享内存
【发布时间】:2017-05-10 09:28:45
【问题描述】:

今天我的一个朋友告诉我,Go 程序可以在多个 CPU 内核上扩展自己。得知系统任务调度程序对 goroutine 一无所知,因此无法在多个内核上运行它们,我感到非常惊讶。

我做了一些搜索,发现 Go 程序可以生成多个操作系统任务以在不同的内核上运行它们(数量由 GOMAXPROCS 环境变量控制)。但据我所知,分叉一个进程会导致进程数据的完整副本,并且不同的进程在不同的地址空间中运行。

那么 Go 程序中的全局变量呢?它们与多个 goroutine 一起使用是否安全?它们是否以某种方式在系统进程之间同步?如果他们这样做了,那怎么办?我主要关心linux和freebsd的实现。

【问题讨论】:

  • The official documentation of goroutines 应该会有所帮助。
  • 还有this article
  • @chrk,问题是:官方文档和第二篇文章都没有阐明 go runtime 如何提供共享变量的同步。官方文档指出:“一个 goroutine 有一个简单的模型:它是一个与同一地址空间中的其他 goroutine 并发执行的函数”。但是如果它们在不同的进程中,它们就不能在同一个地址空间中......
  • 那么让我们再仔细阅读一遍。它指出:“它们之所以被称为 goroutines,是因为现有的术语——线程、协程、进程等等——传达了不准确的含义";所以不要假设它们是过程。就地址空间而言,它指出“Goroutines are multiplexed on multiple OS threads”。我不打算在这里提供完整的答案,但是您真正需要按顺序学习要自己找出问题的答案,是线程在 Linux 中的工作方式;然后测试:strace,/proc/<PID>/* 并在 SO 中查看一些答案以获得更高级的内容
  • Linux 中的线程也称为轻量级进程 (tldp.org/FAQ/Threads-FAQ/Types.html)。每个线程都可以在一个单独的核心上并拥有自己的堆栈,但共享生成进程的地址空间(共享内存)

标签: linux go process ipc shared-memory


【解决方案1】:

我想通了!一切尽在 go 资源中。

有一个我不知道的 Linux 系统调用。 它被称为“克隆”。它比 fork 更灵活,它允许 一个子进程住在其父地址空间中。

这里是线程创建过程的简短概述。

首先在src/runtime/proc.go 中有一个newm 函数。这 函数负责创建一个新的工作线程 (或在 cmets 中称为机器)。

// Create a new m. It will start off with a call to fn, or else the scheduler.
// fn needs to be static and not a heap allocated closure.
// May run with m.p==nil, so write barriers are not allowed.
//go:nowritebarrier
func newm(fn func(), _p_ *p) {

    // ... some code skipped ...

    newosproc(mp, unsafe.Pointer(mp.g0.stack.hi))
}

此函数调用特定于操作系统的newosproc。 对于 Linux,它可以在 src/runtime/os_linux.go 中找到。这里 是该文件的相关部分:

var (
    // ...

    cloneFlags = _CLONE_VM | /* share memory */
        _CLONE_FS | /* share cwd, etc */
        _CLONE_FILES | /* share fd table */
        _CLONE_SIGHAND | /* share sig handler table */
        _CLONE_THREAD /* revisit - okay for now */
)

// May run with m.p==nil, so write barriers are not allowed.
//go:nowritebarrier
func newosproc(mp *m, stk unsafe.Pointer) {

    // ... some code skipped ...

    ret := clone(cloneFlags, /* ... other flags ... */)

    // ... code skipped
}

clone 函数是在特定架构中定义的 文件。对于 amd64,它位于 src/runtime/sys_linux_amd64.s。 这是实际的系统调用。

所以 Go 程序确实在多个操作系统线程中运行,这使得 跨 CPU,但它们使用一个共享地址空间。

呼……我喜欢围棋。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-08
    • 1970-01-01
    • 2018-03-26
    • 1970-01-01
    • 1970-01-01
    • 2012-07-29
    • 2023-03-31
    相关资源
    最近更新 更多