初识协程栈

协程学习(一)--初识协程 本篇文章和大家来了解一下协程的概念 阅读详情

系列导读

本文基于64位平台、1Page=8KB、Go1.6
原创不易,欢迎👏🏻右下角点赞和在看支持,让作者的辛勤付出得到认可,感谢~

26a50b606955053a2bfa1e80693e7675.png

今天开始《Go语言轻松进阶》系列第二章「内存与垃圾回收」第3小节「Go语言垃圾回收原理」。

关于「Go语言垃圾回收原理」章节,会从如下三小节展开:

第一小节「读前知识储备」和第二小节「Go语言内存管理」已经完结,为了更好理解本文大家可以点击下文蓝色历史链接进行查看或复习。

本文拉开垃圾回收部分序幕(预告:会切入一些关键点分析,杜绝千篇一律的内容)。由于Go协程的栈是Go运行时管理的,并分配于堆上,不由操作系统管理,所以我们先来看看协程栈的内存如何被Go运行时管理和回收的。本篇文章先从初步认识协程栈开始。

查看本系列完整内容请访问

https://tigerb.cn/go/#/kernal/

前言


为了对协程栈有个初步的认识,我们先来回顾数据结构中栈的概念,再来看看内存栈的概念作用,最后我们再来通过对比进程中的栈内存和线程中的栈内存来对协程中的栈内存有个初步的认知,全文大致结构如下:

  1. 回顾数据结构中栈的概念

  2. 内存管理中栈的概念

  3. 理解进程栈和线程栈

  4. 认识协程栈

数据结构中栈的概念


栈是一种先进后出(Frist In Last Out)的数据结构。第一个元素所在位置为栈底,最后一个元素所在位置为栈顶。栈顶添加一个元素的过程为压栈(入栈),栈顶移出一个元素的过程为出栈(弹栈)。如下图所示:

ba0e7f5549e5bdc66d62dfcea58a2886.png

内存管理中栈的概念


栈内存

什么是栈内存?

栈内存是计算机对连续内存的采取的「线性分配」管理方式,便于高效存储指令运行过程中的临时变量等。

  1. 栈内存分配逻辑:current - alloc

672a08f3babadf3f8e885724e2a8b5ce.png

  1. 栈内存释放逻辑:current + alloc

cbc73e65d5c2467f9f687b134404a987.png

  • 栈内存的分配过程:看起来像不像数据结构「栈」的压栈过程。

  • 栈内存的释放过程:看起来像不像数据结构「栈」的出栈过程。

什么是栈空间?

栈空间是一个固定值,决定了栈内存最大可分配的内存范围,也就是决定了栈顶的上限。

e4fd1cd06bbac0564f8137135883f3b3.png

栈内存的作用?

总的来说就是存放程序运行过程中,指令传输的、生产的各种临时变量的值,和函数递归调用过程的上文信息,以及进程、线程、协程切换的上下文信息。

  • 存放函数执行过程中的参数的值

  • 存放函数执行过程中的局部变量的值

  • 发生函数调用时,存放调用函数栈帧的栈基BP的值(下篇文章详细讲)

  • 发生函数调用时,存放调用函数下一个待执行指令的地址(下篇文章详细讲)

  • 等等

接着我有两个问题:

  1. 谁决定了栈空间的大小范围?

  2. 谁决定了代码在运行过程中,从栈空间分配或释放多少内存?

我们分别从「进程栈」和「线程栈」、「协程栈」视角看看以上两个问题。

进程栈

什么是进程栈?

答:位于进程虚拟内存的用户空间,以用户空间的高地址开始位置作为栈底,向地址分配。如下图所示:

8346a5d61cf779887dc7476bbfdf71e1.png

谁决定了栈空间(进程栈)的大小范围?

答:操作系统的配置决定,可通过`ulimit -s`查看。示例如下:
(TIGERB) 🤔 ➜ go1.16 git:((go1.16)) ✗ ulimit -s
8192 //注释:单位kb,8m
(TIGERB) 🤔 ➜ go1.16 git:((go1.16)) ✗ ulimit -a
-t: cpu time (seconds)             unlimited
-f: file size (blocks)             unlimited
-d: data seg size (kbytes)         unlimited
-s: stack size (kbytes)             8192     //注释:单位kb,8m
-c: core file size (blocks)         0
-v: address space (kbytes)         unlimited
-l: locked-in-memory size (kbytes) unlimited
-u: processes                       1392
-n: file descriptors               256

谁决定了代码在运行过程中,从栈空间(进程栈)分配或释放多少内存?

答:编译器决定。详细解释如下:

代码编译时,编译器会插入分配和释放栈内存的指令,比如像下面这段简单的程序一样:

一段简单的加法示例代码:

// 源代码
package main

func main() {
  a := 1
  b := 2
  c := a + b
  // 略...
}

编译以上代码时,编译器会插入分配(SUB)和释放(ADD)栈内存的指令:

// 汇编伪代码
SUB 24, SP // 栈上分配24字节内存 3*8byte(分配栈内存指令)
略...
ADD 24, SP // 栈上释放24字节内存 3*8byte(释放栈内存指令)
略...

最后汇编代码转换为二进制代码:

// 二进制伪代码 随便乱写的
011100011000000101...

进程栈总结

「进程栈」位于虚拟内存的用户空间,进程栈的栈底为用户空间部分高地址的开始位置。进程栈的栈空间大小为固定值,由操作系统的配置决定。进程运行过程中栈内存的分配和释放的时机和大小值由编译器决定。

c3d2f43a6bf3396d192d72ba917eb51a.png

线程栈

什么是线程栈?

答:创建一个线程时,使用malloc从堆上分配一块连续内存作为线程的栈空间。

伪代码示例:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define STACK_SIZE 1024 * 1024 // 栈空间大小

int main() {
   pthread_t thread;
   void* stack = malloc(STACK_SIZE); // 堆上申请一块内存

   // ...

   pthread_attr_t attr;
   pthread_attr_init(&attr);
   pthread_attr_setstack(&attr, stack, STACK_SIZE); // 设置线程栈

   int ret = pthread_create(&thread, &attr, thread_func, NULL); // 创建线程

   // ...
}

becee188d26aca1f7bd4c85292751fb3.png

谁决定了栈空间(线程栈)的大小范围?

答:创建线程的运行时。

谁决定了代码在运行过程中,从栈空间(线程栈)分配或释放多少内存?

答:同进程,编译器决定。

协程栈

什么是协程栈?

答:使用`go`关键字创建一个协程时,Go运行时从堆上分配一块连续内存作为协程的栈空间。

3ec284fde6bb5d9c9b24ebfa08b6e5f7.png

谁决定了协程栈的栈空间的大小范围?

答:Go运行时决定,g0为8KB,g为2KB

创建g0函数代码片段:

// src/runtime/proc.go::1720
// 创建 m
func allocm(_p_ *p, fn func(), id int64) *m {
   // ...略
   if iscgo || mStackIsSystemAllocated() {
    mp.g0 = malg(-1)
   } else {
       // 创建g0 并申请8KB栈内存
       // 依赖的malg函数
       mp.g0 = malg(8192 * sys.StackGuardMultiplier)
   }
   // ...略
}

创建g函数代码片段:

// src/runtime/proc.go::3999
// 创建一个带有任务fn的goroutine
func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *g {
   // ...略
   newg := gfget(_p_)
   if newg == nil {
     // 全局队列、本地队列找不到g 则 创建一个全新的goroutine
     // _StackMin = 2048
     // 申请2KB栈内存
     // 依赖的malg函数
     newg = malg(_StackMin)
     casgstatus(newg, _Gidle, _Gdead)
     allgadd(newg)
  }
   // ...略
}

以上都依赖malg函数代码片段,其作用是创建一个全新g:

// src/runtime/proc.go::3943
// 创建一个指定栈内存的g
func malg(stacksize int32) *g {
 newg := new(g)
 if stacksize >= 0 {
  // ...略
  systemstack(func() {
            // 分配栈内存
   newg.stack = stackalloc(uint32(stacksize))
  })
  // ...略
 }
 return newg
}

谁决定了代码在运行过程中,从协程栈的栈空间分配或释放多少内存?

答:同进程、线程,都由编译器决定。

总结


​表格左右滑动查看

类型创建时机谁决定栈空间大小内存位置谁来分配和释放栈内存
进程栈进程启动时操作系统配置,ulimit -s虚拟内存的用户空间栈区编译器,汇编SUB、ADD指令
线程栈创建线程时创建线程的运行时,pthread_attr_setstack虚拟内存的用户空间进程堆区域编译器,汇编SUB、ADD指令
协程栈使用go关键字运行函数时Go运行时,malg(栈内存)g0 8KB、g 2KB虚拟内存的用户空间进程堆区域编译器,汇编SUB、ADD指令

Go轻松进阶系列 更多文章

0124752880fd3b52910806584b4b4726.png

原创不易,欢迎👏🏻右下角点赞和在看支持,

让作者的辛勤付出得到认可,感谢~

" 小小打赏,动力满满🚀 "

MTK-AWB 小结 (二) 调试总结:可以将offset 对应的ratio加大,保留更多的喜好色(偏色),影响WF statistic gain,从而影响最终的awb gain。这一部分前期CCT校准的时候就会算出一组默认值,当然这个offset offse 值用于优化T,S,wf 光下的偏色喜好度. offset。后面遇到 A H Shad 光收白或者偏色,还可以优化 A 的offset ,H offset,shad offset.new offset 计算过程(Day locus offset 和M/G offset ) 阅读详情

相关推荐

IAR编译相同工程生产hex文件checksum不一致问题

IAR Checksum

xqw19891201的博客 1384

【网络-性能】协程2-初识 C++20 协程

本文结合协程示例源码,和编译器转换后的代码,对 C++ 20 协程的构成,及其执行流程进行分析和整理。

三人行,必有我师,其余为同学。老师好,同学好。 1434

VSCode玩转ESP-ADF音频开发:从环境搭建到成功播放MP3的避坑全流程

本文详细介绍了如何在VSCode环境下使用ESP-ADF框架进行ESP32音频开发,从环境搭建到成功播放MP3的全流程。重点解析了`IDF_PATH`和`ADF_PATH`的配置问题,以及如何解决`xTaskCreateRestrictedPinnedToCore`等常见编译错误,帮助开发者快速上手ESP-ADF音频开发。

weixin_42520909的博客 339

协程初识

一 什么是进程和线程 进程是什么呢 直白地讲,进程就是应用程序的启动实例。比如我们运行一个游戏,打开一个软件,就是开启了一个进程。 进程拥有代码和打开的文件资源、数据资源、独立的内存空间。 线程又是什么呢? 线程从属于进程,是程序的实际执行者。一个进程至少包含一个主线程,也可以有更多的子线程。 线程拥有自己的栈空间。 有人给出了很好的归纳: 对操作系统来说,线程是最小的执行单元...

许进进的博客 5048

在ArcGIS Pro软件中进行滑坡的敏感性和易发性分析

滑坡的敏感性和易发性分析需要综合地形、水文、地质及环境等多维数据,本文罗列了基于ArcGIS Pro软件进行的一系列可执行分析。

weixin_49609713的博客 1735

从原理到调试:一次搞懂Camera Sensor的曝光、增益与帧率三角关系

本文深入解析Camera Sensor中曝光、增益与帧率之间的动态平衡关系,揭示低光环境下帧率下降的根本原因。通过实际调试案例和底层公式分析,帮助开发者理解如何优化相机参数设置,实现画质与流畅度的最佳平衡。

weixin_31310913的博客 432

初识C++20协程

协程什么特点呢?简单概括就是可挂起和重新执行的执行体,非常适合用于什么呢?根据到现在了解的特点,主要还是用于异步执行里面,类似多业务处理需要将某些任务分出去执行,同时自己这个执行流又去做其他任务然后再会来接着执行。也可以添加协程执行流,多个协程任务,然线程选择执行,形成一种具备优先级的调度模式,哪个任务执行多少这种,这样其实有点类似于多路转接的思想。归根结底就是因为协程能够暂停执行后再回来执行。至于更多其他用法,目前也还在了解中,可以自行查看文档或者找寻其他教程。

choose_heart的博客 1144

添加odbc数据源时的一些问题和解决办法

pl/sql

Mr_Syh的博客 9645

协程asyncio_初识asyncio协程

初识asyncio协程一、基本概念​ 要想了解学习协程相关知识要先对以下几个概念先行了解:阻塞​ 阻塞状态是指程序未得到某所需计算资源时的挂起状态,简单说就是程序在等待某个操作未执行完前无法执行其他操作。非阻塞​ 非阻塞状态是指在等待某项操作执行完前可以继续运行其他操作,也就是不会被阻塞。同步​ 不同程序单元为了完成某项任务,在执行过程中以某种通信方式保证协调一致,假如有一个售票窗口,你必须等前面...

weixin_39777242的博客 145

告别手动拍照!用Python+JAKA机械臂+D455相机实现全自动手眼标定(附完整代码)

本文详细介绍了如何利用Python脚本实现JAKA机械臂与Intel RealSense D455相机的全自动手眼标定流程,提供从硬件配置到代码实现的完整解决方案。通过自动化标定,显著提升工业自动化中的精度和效率,适用于机器人视觉领域的二次开发需求。

weixin_30555125的博客 526

防护装备穿戴检查:基于YOLOv8与UI界面的深度学习应用

通过结合YOLOv8模型与UI界面,本系统实现了防护装备穿戴检查的自动化。该系统能够快速识别工作人员是否正确佩戴了所需的防护装备,帮助提高安全管理水平。未来,随着技术的发展,防护装备穿戴检查系统将在更多领域得到应用,如工业现场、建筑工地等,实现更加精确和高效的实时监控。

YOLO 355

初识协程原理

工作中经常会听到 “协程” 这个概念,第一次听到时,我就去查阅相关教材,发现根本就没有介绍“协程”的。于是上网查阅了相关的解释,当时大概理解了它是干什么的,但是深入的细节并不了解。为了搞明白协程,趁着假期我好好研究了一下协程,然后产出本文作为学习笔记进行总结,以用于后续进行回顾。要说明白“协程”,我们得先从进程和线程开始进行介绍。

m0_73915763的博客 236

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

初识Kotlin协程

协程可以让我们使用顺序的方式去写异步代码,而且不会阻塞UI线程。 Kotlin 协程提供了一种全新处理并发的方式,你可以在 Android 平台上使用它来简化异步执行的代码。协程从 Kotlin 1.3 版本开始引入,但这一概念在编程世界诞生的黎明之际就有了,最早使用协程的编程语言可以追溯到 1967 年的 Simula 语言。在过去几年间,协程这个概念发展势头迅猛,现已经被诸多主流编程语言采用,比如 Javascript、C#、Python、Ruby 以及 Go 等。Kotlin 协程是基于来自其他语言的

yuantian_shenhai的博客 2347

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

廖雪峰Python教程[完整版]

廖雪峰的python教程整理的,已经排好版了,而且还是完整的,适合小白

初识kotlin协程

Kotlin中的协程是官方定义的一套可以让开发者非常方便地实现并发操作的线程API。......我们可以看到这种串行的加载方式既浪费时间又浪费资源,关键是如果我还需要加载更多的信息,将会嵌套更多层,这种维护起来是相当的困难,所以大家给它取了个昵称 ---回调地狱(Callback Hell)。国外甚至还有同名网站,但是在Kotlin//launch 表示开启一个协程//同步代码块的方式实现异步操作上面的代码暂时不需要看懂,这里只是举个例子,让大家直观感受一下协程中这个巨好用的功能。

allen_xu_2012_new的博客 825

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

C++与协程

协程

weixin_43728590的博客 919

一、初识asyncio协程

初识asyncio协程 一、基本概念 ​ 要想了解学习协程相关知识要先对以下几个概念先行了解: 阻塞 ​ 阻塞状态是指程序未得到某所需计算资源时的挂起状态,简单说就是程序在等待某个操作未执行完前无法执行其他操作。 非阻塞 ​ 非阻塞状态是指在等待某项操作执行完前可以继续运行其他操作,也就是不会被阻塞。 同步 ​ 不同程序单元为了完成某项任务,在执行过程中以某种通信方式保证协调一致,假如...

angbini7012的博客 292

在 Android 开发中使用 Kotlin 协程 (一) -- 初识 Kotlin 协程

前言 最近在研究 Kotlin 协程,发现功能真的超级强大,很有用,而且很好学,如果你正在或计划使用 Kotlin 开发 Android,那么 Kotlin 协程你一定不能错过! 协程是什么? 我们平常接触的都是进程、线程,在开发中使用最多的就是线程,比如主线程、子线程,而且操作系统里最小可操作的单元就是线程,那协程又是什么?协程是比线程更小的单位,但并不是说在操作系统里最小可操作单元就从线程变成...

weixin_34191845的博客 5252

初识协程

--[[ 协程(协同程序): 有自己的堆栈,自己的局部变量,自己的指令指针,与其他协程共享全局变量。 在多处理器的情况下,可以同时运行多个线程,但在指定的某一时刻只有一个协程在运行, 并且这个正在运行的协程在指定的时候挂起。 调用resume函数会将协程由挂起状态转为运行状态 调用yield函数会将协程由运行状态转为挂起状态,并且协程会返回yield函数的参数 ]] co = corout...

Default 190

高性能服务器之初识协程(附带测试用例源码)

著名的协程库有Boost.Coroutine2 ,libco和libgo大家可以去使用或熟悉这对于高性能开发有很大的帮助。下一篇我会写一个有关协程调度的类,做一个有关协程的服务器框架下面是线程,进程和协程的一张关系图,侧面说明协程对高性能的提升的重要性。

m0_54069809的博客 821

Kotlin协程的那些事 ---- 初识协程

协程的基础知识

qq_33235287的博客 1134

Android NDK——初识协程(Coroutine)和libco的简单介绍使用

文章大纲引言一、协程Coroutine概述二、协程的优势三、C语言主流的协程库简介1、协程的切换2、libco2.1、libco概述2.2、libco 的使用说明2.2.1、co_create函数创建并初始化协程对象2.2.1.1、声明一个协程对象类型的指针2.2.1.2、调用co_create函数创建初始化协程对象2.2.2、coctx_swap进行协程上下文切换2.3、libco的简单应用 引言 协程不是进程或线程,其执行过程更类似于子例程,或者说不带返回值的函数调用。 一个程序可以包含多个协程,可以对

最美不过,心中有梦,身旁有你! 1606

python 之 协程 初识 及其优缺点

协程,又称微线程,纤程。英文名Coroutine。 协程是一种用户态的轻量级线程。 线程是系统级别的,它们是由操作系统调度;协程是程序级别的,由程序员根据需要自己调度。我们把一个线程中的一个个函数叫做子程序,那么子程序在执行过程中可以中断去执行别的子程序;别的子程序也可以中断回来继续执行之前的子程序,这就是协程。也就是说同一线程下的一段代码执行着执行着就可以中断,然后跳去执行另一段代码,当再次...

super_little_newbie di boker 1874

Kotlin协程的使用

目录 引入kotlin协程 kotlin协程的三种启动方式 初识协程: 第一种启动方式(runBlocking:T) 第二种启动方式(launch:Job) 第三种启动方式(async/await:Deferred) 实现你的第一个Coroutine程序 浅析Kotlin协程 ——协程是什么?https://blog.youkuaiyun.com/qq_17798399/article/det...

青冘的技术学习博客 1万+

Kotlin Coroutine(协程): 二、初识协程

Kotlin 协程初步认识, runBlocking、launch、async、调度器、withContext、协程理解等

孟老板的博客 433

初识栈与队列

队列是一种常见的数据结构,它遵循先进先出(First-In-First-Out,FIFO)的原则。这意味着最先进入队列的元素将最先被移除。队列通常比喻为排队等待服务的人群,先来的人先被服务,后来的人依次排在后面等待。队列有两个主要操作:1.入队(enqueue):向队列的末尾(队尾)添加一个元素。2.出队(dequeue):从队列的头部(队首)移除一个元素。除此之外,队列还可能包括其他操作,如:​查看队首元素(peek):获取队列的第一个元素,但不将其从队列中移除。​。

2301_79977698的博客 1575

Kotlin协程学习之路(一):初识协程

Kotlin协程学习之路(一):初识协程

RQ997832的专栏 966
上一篇: 一探究竟新一代可观测标准OpenTelemetry
下一篇: 读一本好书|领导梯队
TIGERB_Blog
博客等级 码龄10年 109粉丝 65原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值