Golang、进程和共享内存
Golang, processes and shared memory
今天,我的一个朋友告诉我,Go 程序可以在多个 CPU 核心上自我扩展。听说系统任务调度程序对 goroutine 一无所知,因此不能 运行 它们在多个内核上,我感到非常惊讶。
我做了一些搜索,发现 Go 程序可以在不同的内核上生成多个 OS 任务以 运行 它们(数量由 GOMAXPROCS 环境变量控制)。但据我所知,分叉一个进程会导致进程数据的完整副本和不同地址空间中的不同进程运行。
那么Go程序中的全局变量呢?它们可以安全地与多个 goroutine 一起使用吗?它们是否以某种方式在系统进程之间同步?如果他们这样做,那又如何呢?我主要关心 linux 和 freebsd 实现。
我想通了!全部在 go sources.
有一个我不知道的 Linux 系统调用。
它被称为 "clone"。它比 fork 更灵活,它允许
子进程居住在其父进程的地址 space.
这里是线程创建过程的简短概述。
首先src/runtime/proc.go
中有一个newm
函数。这个
函数负责创建一个新的工作线程
(或在评论中称为机器)。
// 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
,它是 OS 特定的。
对于 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 程序在多个 OS 线程中执行 运行 这使得
跨 CPU,但它们使用一个共享地址 space。
呼...我喜欢围棋
今天,我的一个朋友告诉我,Go 程序可以在多个 CPU 核心上自我扩展。听说系统任务调度程序对 goroutine 一无所知,因此不能 运行 它们在多个内核上,我感到非常惊讶。
我做了一些搜索,发现 Go 程序可以在不同的内核上生成多个 OS 任务以 运行 它们(数量由 GOMAXPROCS 环境变量控制)。但据我所知,分叉一个进程会导致进程数据的完整副本和不同地址空间中的不同进程运行。
那么Go程序中的全局变量呢?它们可以安全地与多个 goroutine 一起使用吗?它们是否以某种方式在系统进程之间同步?如果他们这样做,那又如何呢?我主要关心 linux 和 freebsd 实现。
我想通了!全部在 go sources.
有一个我不知道的 Linux 系统调用。 它被称为 "clone"。它比 fork 更灵活,它允许 子进程居住在其父进程的地址 space.
这里是线程创建过程的简短概述。
首先src/runtime/proc.go
中有一个newm
函数。这个
函数负责创建一个新的工作线程
(或在评论中称为机器)。
// 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
,它是 OS 特定的。
对于 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 程序在多个 OS 线程中执行 运行 这使得 跨 CPU,但它们使用一个共享地址 space。
呼...我喜欢围棋