当 Goroutines 切换时,CPU 上下文会发生什么变化?

huangapple go评论76阅读模式
英文:

What happens with CPU context when Goroutines are switching?

问题

如果我正确理解goroutine如何在系统线程之上工作,它们是按顺序从队列中运行的。但这是否意味着每个goroutine都要将其上下文加载/卸载到CPU上?如果是的,系统线程和goroutine之间有什么区别?

最重要的问题是上下文切换的时间成本。这样理解正确吗?

在检测哪个goroutine请求了哪些数据方面,有什么机制?例如:我从goroutine A发送请求到数据库,并且不等待响应,同时切换到下一个goroutine。系统如何知道请求来自A而不是B或C?

英文:

If I correctly understand how goroutines work on top of system threads - they run from queue one by one. But does it mean that every goroutine loads\unloads it's context to CPU? If yes what's difference between system threads and goroutines?

The most significant problem is time-cost of context-switching. Is it correct?

What mechanism lays under detecting which data was requested by which goroutine? For example: I am sending request to DB from goroutine A and doesn't wait for response and at the same time occurred switch to a next goroutine. How system understands that a request came from A and not from B or C?

答案1

得分: 15

Goroutines、内存和操作系统线程

Go语言具有根据需要增长的分段堆栈。Go运行时负责调度,而不是操作系统。运行时将goroutine复用到相对较少的真实操作系统线程上。

Goroutines切换的成本

Goroutines是协作调度的,当切换发生时,只需要保存/恢复3个寄存器 - 程序计数器、堆栈指针和DX寄存器。从操作系统的角度来看,Go程序的行为类似于事件驱动程序。

Goroutines和CPU

你不能直接控制运行时将创建的线程数。可以通过调用runtime.GOMAXPROCS(n)来设置程序使用的处理器核心数。

程序计数器

另一个完全不同的故事

在计算机中,程序是计算机执行的一组有序操作。指令是程序向计算机处理器发出的命令。在计算机内部,地址是内存或存储中的特定位置。程序计数器寄存器是处理器使用的一组少量数据存储位置之一。

这是关于程序如何工作和彼此通信的不同故事,与goroutine的主题没有直接关系。

来源:

英文:

Goroutines, memory and OS threads

Go has a segmented stack that grows as needed. Go runtime does the scheduling, not the OS. The runtime multiplexes the goroutines onto a relatively small number of real OS threads.

Goroutines switch cost

Goroutines are scheduled cooperatively and when a switch occurs, only 3 registers need to be saved/restored - Program Counter, Stack Pointer, and DX. From the OS's perspective Go program behaves as an event-driven program.

Goroutines and CPU

You cannot directly control the number of threads that the runtime will create. It is possible to set the number of processor cores used by the program by setting the variable GOMAXPROCS with a call of runtime.GOMAXPROCS(n).

Program Counter

and a completely different story

In computing, a program is a specific set of ordered operations for a computer to perform. An instruction is an order given to a computer processor by a program. Within a computer, an address is a specific location in memory or storage. A program counter register is one of a small set of data holding places that the processor uses.

This is a different story of how programs work and communicate with each other and it doesn't directly relate to a goroutine topic.

Sources:

答案2

得分: 5

Gs、Ms和Ps
一个"G"简单地指的是一个goroutine。它由类型g表示。当一个goroutine退出时,它的g对象会返回到一个空闲的g池中,以便稍后可以被其他goroutine重用。

一个"M"是一个可以执行用户Go代码、运行时代码、系统调用或者处于空闲状态的操作系统线程。它由类型m表示。可以同时存在任意数量的M,因为任意数量的线程可能被阻塞在系统调用中。

最后,一个"P"代表执行用户Go代码所需的资源,例如调度器和内存分配器状态。它由类型p表示。恰好有GOMAXPROCS个P。可以将P视为操作系统调度器中的CPU,而p类型的内容则类似于每个CPU的状态。这是一个很好的地方,可以将需要进行分片以提高效率的状态放置在这里,但不需要每个线程或每个goroutine都有。

调度器的工作是将一个G(要执行的代码)、一个M(要在哪里执行)和一个P(执行所需的权限和资源)进行匹配。当一个M停止执行用户Go代码时,例如进入系统调用,它会将其P返回到空闲的P池中。为了恢复执行用户Go代码,例如从系统调用返回时,它必须从空闲池中获取一个P。

所有的g、m和p对象都是在堆上分配的,但它们永远不会被释放,因此它们的内存保持类型稳定。因此,运行时可以避免在调度器的深层中使用写屏障。

用户栈和系统栈
每个非dead的G都有一个与之关联的用户栈,用户Go代码在其上执行。用户栈开始很小(例如2K),并且可以动态增长或缩小。

每个M都有一个与之关联的系统栈(也称为M的"g0"栈,因为它是作为一个存根G实现的),在Unix平台上还有一个信号栈(也称为M的"gsignal"栈)。系统栈和信号栈不能增长,但足够大以执行运行时和cgo代码(在纯Go二进制文件中为8K;在cgo二进制文件中由系统分配)。

运行时代码经常使用systemstack、mcall或asmcgocall临时切换到系统栈,以执行不能被抢占、不能增长用户栈或切换用户goroutine的任务。在系统栈上运行的代码隐式地是不可抢占的,并且垃圾收集器不会扫描系统栈。在系统栈上运行时,当前的用户栈不用于执行。

参考:https://github.com/golang/go/blob/master/src/runtime/HACKING.md

英文:

Gs, Ms, Ps

A "G" is simply a goroutine. It's represented by type g. When a goroutine exits, its g object is returned to a pool of free gs and can later be reused for some other goroutine.

An "M" is an OS thread that can be executing user Go code, runtime code, a system call, or be idle. It's represented by type m. There can be any number of Ms at a time since any number of threads may be blocked in system calls.

Finally, a "P" represents the resources required to execute user Go code, such as scheduler and memory allocator state. It's represented by type p. There are exactly GOMAXPROCS Ps. A P can be thought of like a CPU in the OS scheduler and the contents of the p type like per-CPU state. This is a good place to put state that needs to be sharded for efficiency, but doesn't need to be per-thread or per-goroutine.

The scheduler's job is to match up a G (the code to execute), an M (where to execute it), and a P (the rights and resources to execute it). When an M stops executing user Go code, for example by entering a system call, it returns its P to the idle P pool. In order to resume executing user Go code, for example on return from a system call, it must acquire a P from the idle pool.

All g, m, and p objects are heap allocated, but are never freed, so their memory remains type stable. As a result, the runtime can avoid write barriers in the depths of the scheduler.

User stacks and system stacks

Every non-dead G has a user stack associated with it, which is what user Go code executes on. User stacks start small (e.g., 2K) and grow or shrink dynamically.

Every M has a system stack associated with it (also known as the M's "g0" stack because it's implemented as a stub G) and, on Unix platforms, a signal stack (also known as the M's "gsignal" stack). System and signal stacks cannot grow, but are large enough to execute runtime and cgo code (8K in a pure Go binary; system-allocated in a cgo binary).

Runtime code often temporarily switches to the system stack using systemstack, mcall, or asmcgocall to perform tasks that must not be preempted, that must not grow the user stack, or that switch user goroutines. Code running on the system stack is implicitly non-preemptible and the garbage collector does not scan system stacks. While running on the system stack, the current user stack is not used for execution.

Ref: https://github.com/golang/go/blob/master/src/runtime/HACKING.md

huangapple
  • 本文由 发表于 2016年12月9日 01:28:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/41045362.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定