In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-15 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "what are the knowledge points of Golang in runtime". The content of the explanation is simple and clear, and it is easy to learn and understand. Please follow the editor's train of thought to study and learn "what are the knowledge points of Golang in runtime"?
Scheduler structure
The scheduler manages three types that are very important in runtime: G, M, and P. Even if you don't write scheduler-related code, you should understand these concepts.
G, M and P
A G is a goroutine, represented by the type g in runtime. When a goroutine exits, the g object is placed in a pool of free g objects for subsequent goroutine use.
An M is a system thread that can execute the user's go code, runtime code, system calls, or idle waits. In runtime, it is represented by type m. There may be any number of M at the same time, because any number of M may block in the system call. (translator's note: when an M executes a blocked system call, M and P are unbound and a new M is created to execute other G.)
Finally, a P represents the resources needed to execute the user's go code, such as scheduler status, memory allocator status, and so on. It is represented by type p in runtime. The number of P is exactly (exactly) equal to GOMAXPROCS. A P can be understood as the CPU,p type in the operating system scheduler and can be understood as the state of each CPU. Here you can put some states that need to be shared efficiently but not for every P (Per P) or every M (Per M).
The job of the scheduler is to combine a G (the code that needs to be executed), an M (the place where the code is executed), and a P (the permissions and resources needed for code execution). When an M stops executing user code (such as entering a blocked system call), it needs to return its P to the free P pool; in order to continue executing the user's go code (such as when exiting from a blocked system call), it needs to get a P from the idle P pool.
All g, m, and p objects are allocated on the heap and never released, so their memory usage is stable. Thanks to this, runtime can avoid write barriers in the scheduler implementation.
User stack and system stack
Each non-dead G has an associated user stack on which the user's code executes. The user stack starts small (such as 2K) and grows or shrinks dynamically.
Each M has an associated system stack (also known as the g0 stack because it is also implemented through g); if it is on the Unix platform, there is also a signal stack (also known as the gsignal stack). The system stack and signal stack cannot grow, but are large enough to run any runtime and cgo code (8K in pure go binaries and allocated by the system in the case of cgo).
Runtime code often performs special tasks by calling systemstack, mcall, or asmcgocall to temporarily switch to the system stack, such as those that cannot be preempted, those that should not expand the user stack, and those that switch user goroutine. The code running on the system stack implies non-preemption, and the garbage collector does not scan the system stack. When an M is running on the system stack, the current user stack is not running.
Getg () and getg (). M.curg
If you want to get the g of the current user, you need to use getg (). M.curg.
Getg () returns the current g, but when executing on the system stack or signal stack, it returns the g0 or gsignal of the current M, which is probably not what you want.
If you want to determine if you are currently executing on the system stack or the user stack, you can use getg () = = getg () .m.curg.
Error handling and escalation
In user code, there are some errors that can be reasonably recovered using panic as usual, but in some cases, panic can cause immediate fatal errors, such as calling in the system stack or when mallocgc is executed.
Most runtime errors are unrecoverable, and using throw,throw for these unrecoverable errors will print out the traceback and terminate the process immediately. Throw should be passed in a string constant to avoid allocating memory for string in this case. According to the convention, more information should be printed in print or println before throw, and should be in runtime. The beginning.
In order to debug errors in runtime, a very practical method is to set GOTRACEBACK=system or GOTRACEBACK=crash.
Synchronization
There are many synchronization mechanisms in runtime, which are not only semantically different, but also different from the interaction between go scheduler and operating system scheduler.
The simplest is mutex, which can be operated using lock and unlock. This method is mainly used to protect some shared data in the short term (poor performance in the long term). Blocking on mutex directly blocks the entire M without interacting with go's scheduler. Therefore, it is safe to use mutex at the lowest level in runtime because it also prevents the associated G and P from being rescheduled (M is blocked and cannot be scheduled). Rwmutex is similar.
If you want to make an one-time notification, you can use note. Note provides notesleep and notewakeup. Unlike traditional UNIX's sleep/wakeup,note, there is no race-free, so if the notewakeup has already occurred, the notesleep will return immediately. Note can be reset through noteclear after use, but be aware that noteclear cannot compete with notesleep and notewakeup. Similar to mutex, blocking on note blocks the entire M. However, note provides different ways to call sleep:notesleep to prevent the associated G and P from being rescheduled; notetsleepg behaves like a blocked system call, allowing P to be reused to run another G. However, this is still less efficient than blocking a G directly, because it consumes an M.
If you need to interact directly with the go scheduler, you can use gopark and goready. Gopark suspends the current goroutine-- and changes it to the waiting state and removes it from the scheduler's run queue-- and then dispatches another goroutine to the current M or P. Goready restores a suspended goroutine to the runnable state and puts it in the running queue.
To sum up, the following table:
Atomicity
Runtime uses some of the atomic operations that are native to runtime/internal/atomic. This corresponds to sync/atomic, except that there are some differences in method names due to historical reasons, and there are some additional methods that runtime requires.
In general, we are very cautious about the use of atomic in runtime and try to avoid unwanted atomic operations. If access to a variable is already protected by another synchronization mechanism, then the protected access generally does not need to be atomic. This is done for the following main reasons:
The rational use of non-atomic and atomic operations makes the code more readable, and atomic operations on one variable mean that there may be concurrent operations on that variable in another.
Non-atomic operations allow automatic competition detection. Runtime itself does not currently have a competition detector, but there may be one in the future. Atomic operations can cause competition detectors to ignore this detection, but non-atomic operations can use competition detectors to verify your hypothesis (whether there will be competition).
Non-atomic operations can improve performance.
Of course, all non-atomic operations on a shared variable should indicate in the document how the operation is protected.
Some of the more common scenarios that mix atomic and non-atomic operations are:
Most operations are read, and write operations are lock-protected variables. Within the scope of lock protection, read operations do not have to be atomic, but write operations must be atomic. Outside the scope of lock protection, the read operation must be atomic.
Read operations occur only during STW, and there are no write operations during STW. So at this time, the read operation does not need to be atomic.
Having said that, the advice given by Go Memory Model still holds Don't be [too] clever. The performance of runtime is important, but robustness is more important.
Out of heap memory (Unmanaged memory)
In general, runtime will try to use the normal method to request memory (memory on the heap, managed by gc), but in some cases runtime must apply for some out-of-heap memory (unmanaged memory) that is not managed by gc. This is necessary because it is possible that the memory is the memory manager itself, or that the caller does not have a P (e.g. there is no P until the scheduler is initialized).
There are three ways to apply for out-of-heap memory:
SysAlloc acquires memory directly from the operating system, and the requested memory must be an integral multiple of the length of the system page table. Can be released through sysFree.
Persistentalloc combines multiple small memory requests into one large sysAlloc to avoid memory fragmentation (fragmentation). However, as the name implies, memory requested through persistentalloc cannot be freed.
Fixalloc is a SLAB-style memory allocator that allocates fixed-size memory. Objects allocated through fixalloc can be freed, but memory can only be reused by the same fixalloc pool. So fixalloc is suitable for the same type of object.
In general, the types of memory allocated using the above three methods should be marked / / go:notinheap (see later).
Objects allocated in out-of-heap memory should not contain pointer objects on the heap unless the following rules are followed:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
All pointers to the heap in out-of-heap memory must be the garbage collection roots of garbage collection. That is, all pointers must be accessible through a global variable or explicitly marked with runtime.markroot.
If memory is reused, the pointer on the heap must be initialized with 0 before it can be marked as GC root and visible to GC (zero-initialized, see below). Otherwise, GC may observe stale heap pointers. You can see the following Zero-initialization versus zeroing.
Zero-initialization versus zeroing
There are two types of zero initialization in runtime, depending on whether memory has been initialized to a type-safe state.
If the memory is not in a type-safe state, it means that it may contain some junk values because it has just been allocated and initialized for the first time. If students who have learned the C language should be able to understand what it means, then this piece of memory must use memclrNoHeapPointers for zero-initialized or pointer-free writing. This does not trigger a write barrier.
Memory can be written to zero via typedmemclr or memclrHasPointers, which is set to a type-safe state. This triggers the write barrier.
Runtime-only compilation instruction (compiler directives)
In addition to the / / go: compilation instruction noted in go doc compile, the compiler supports some additional instructions in the runtime package.
Go:systemstack
Go:systemstack indicates that a function must run on the system stack, which is dynamically verified by a special function prologue.
Go:nowritebarrier
Go:nowritebarrier tells the compiler to trigger an error if the following function contains a write barrier (this does not prevent the write barrier from being generated, it's just a hypothesis).
In general, you should use go:nowritebarrierrec. Go:nowritebarrier is used if and only if the write barrier is "preferably not", but not necessary for correctness.
Go:nowritebarrierrec and go:yeswritebarrierrec
Go:nowritebarrierrec tells the compiler to trigger an error if the following function and the function it calls (recursively) until a go:yeswritebarrierrec contains a write barrier.
Logically, the compiler starts with each go:nowritebarrierrec function on the generated call graph until it encounters (or ends) the function of go:yeswritebarrierrec. If you encounter a function that contains a write barrier, an error will be generated.
Go:nowritebarrierrec is mainly used to implement the write barrier itself and to avoid an endless loop.
Both compilation instructions are used in the scheduler. The write barrier requires an active P (getg () .m.p! = nil), however, the scheduler-related code may run without an active P. In this case, go:nowritebarrierrec will be used to run on functions that release P or no P, and go:yeswritebarrierrec will be used to retrieve the code that gets P. Because these are function-level comments, the code that releases P and gets P must be split into two functions.
Go:notinheap
Go:notinheap applies to type declarations, indicating that a type must not be assigned to the GC heap. In particular, pointers to this type should always fail in the runtime.inheap judgment. This type may be used for global variables, variables on the stack, or objects on out-of-stack memory (such as allocation through sysAlloc, persistentalloc, fixalloc, or other manually managed span). In particular:
Hongmeng official Strategic Cooperation to build HarmonyOS Technology Community
New (T), make ([] T), append ([] T,...) And implicit allocation on the heap of T is not allowed (although implicit allocation is never allowed in runtime).
A pointer to a normal type (except unsafe.Pointer) cannot be converted to a pointer to a go:notinheap type, even if they have the same underlying type (underlying type).
Any type that contains a go:notinheap type is itself go:notinheap. If structures and arrays contain elements of go:notinheap, then they themselves are of type go:notinheap. Map and channel do not allow go:notinheap types. To make things clearer, any implicit go:notinheap type should explicitly indicate go:notinheap.
Write barriers to pointers of type go:notinheap can be ignored.
The last point is the real benefits of the go:notinheap type. Runtime uses this in the underlying structure to avoid memory barriers between schedulers and memory allocators to avoid illegal checks or simply improve performance. This approach is moderately secure (reasonably safe) and does not reduce the readability of runtime.
Thank you for your reading, the above is the content of "what are the knowledge points of Golang in runtime". After the study of this article, I believe you have a deeper understanding of what the knowledge points of Golang in runtime have, and the specific use needs to be verified in practice. Here is, the editor will push for you more related knowledge points of the article, welcome to follow!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.