Go 协程为什么比进程和线程占用的系统资源低?

文章探讨了进程、线程和协程在CPU资源及内存占用上的差异,重点强调了Go协程由于在用户态切换的低切换成本和小内存占用而具有优势。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

进程是一个程序在执行时所占据的独立虚拟内存空间,Linux为每个进程分配一个虚拟内存空间,包括栈、未使用的内存、堆、BSS、DATA和TEXT等。

线程可以看作是轻量级的进程,多个线程在一个进程中“共生”,每个线程拥有独立的栈空间,共享其他虚拟内存空间,因此线程间通信比较简单,也就是可以通过共享内存进行通信。

进程和线程都是CPU的执行单元,它们在内核态进行切换,切换成本较高。

协程是用户态的一种虚拟执行单元,在用户态切换执行流程,切换成本较低。


切换执行单元的成本

我们通过介绍线程和协程的切换流程,讲述为什么在内核态切换的成本较高,而在用户态切换的成本较低?

因为进程和线程都是内核态切换,并且进程切换成本比线程切换成本更高,所以只介绍线程切换和协程切换的切换成本。

内核态切换 - 线程

在了解线程在内核态切换之前,我们先了解一下什么是 CPU 时间片,在操作系统中,我们会安装很多软件,并且我们会同时使用多个软件,而 CPU 资源有限。

为了让多个软件可以在操作系统中同时运行,CPU 分成一个个的时间片,在每个时间片中运行一个软件的一个线程,因为时间片非常短,所以我们会感觉多个软件在同时运行。

在编写代码时,我们为了可以让程序被分配到更多的 CPU 资源,可以多创建一些线程,用于提升程序运行的效率。需要注意的是,线程并不是创建越多越好。

因为 CPU 在内核态切换执行单元(线程)时,会有时间成本,在进行切换执行单元时,需要保存寄存器中的数据,将原执行单元的状态保存,切换操作也会占用 CPU 资源(时间片),从而减少了供线程运行的 CPU 资源(时间片)。

除了时间成本之外,还会有性能开销,系统内核调度线程,需要用户空间和内核空间切换,因为只有拥有最高权限的内核空间才可以调度线程,限于篇幅,我们不再展开叙述。

用户态切换 - 协程

因为通过创建线程(执行单元),为程序争取更多的 CPU 资源,在线程切换时也会浪费 CPU 资源(时间成本),所以可以将执行单元不再在内核态运行,改为在用户态运行,也就是协程。

协程的切换成本较低,是因为切换比较简单,并且是在用户态进行切换,切换的时间成本较低(纳秒级),只需将当前协程的 CPU 寄存器的状态先保存起来,然后将需要 CPU 资源的协程的 CPU 寄存器的状态加载到 CPU 寄存器中。

关于 Go 协程的调度,我们在之前的文章中介绍过,此处不再赘述。


内存占用

除了 CPU 资源有限之外,内存资源也是有限的,所以我们还需要了解进程、线程、协程的内存占用。

读者朋友们应该知道 32 位操作系统只支持 4G 内存的内存条,这是因为进程在 32 位操作系统中最多只能占用 4G 内存,而在 64 位操作系统中可以占用更多内存。

线程占用内存一般是 10MB,不同的操作系统版本之间有些差异,区间在 4M - 64M。

协程占用内存最小,一个协程占用 2KB 左右的内存。


 4 

总结

本文我们主要介绍为什么 Go 协程比进程和线程占用的系统资源低,通过进程、线程、协程的 CPU 资源和内存占用的比较,发现无论是在切换时消耗的 CPU 资源(时间片),还是内存占用,Go 协程都有明显优势。

一句话总结就是 Go 协程的切换成本和内存占用比线程和进程都低。

需要注意的是,Go 协程占用系统资源低,并不代表可以无限创建 Go 协程。

参考资料:

  1. https://www.geeksforgeeks.org/time-slicing-in-cpu-scheduling/

  2. Virtual Memory

<think>嗯,用户之前问了关于pstree输出的问题,现在又想知道有没有办法查看完整的进程线程协程关系的命令。我需要先理解用户的需求。他可能是在调试或者优化自己的服务,比如那个service-api,想知道资源使用情况或者并发结构。 首先,进程线程在Linux中比较容易查看,但协程(比如Goroutine或Python的asyncio)是用户态的,不在内核中显示,所以常规命令可能看不到。用户可能需要结合不同工具来获取完整的关系。 接下来,我需要回忆常用的命令。进程树可以用pstree、ps,线程可以用ps -T或者top -H。协程的话,不同语言有不同方法,比如Go的pprof,或者Python的asyncio内省工具。用户可能需要分步骤的方法,先看进程,再看线程,最后处理协程。 还要考虑用户的使用环境,比如是否在容器中,或者有没有权限问题。例如,某些命令可能需要sudo权限。另外,用户可能不太熟悉这些工具,需要给出具体的例子,比如命令参数如何写,输出如何解读。 用户可能想了解整个应用的并发结构,包括各个层次的并发模型。可能需要解释进程线程协程之间的关系,以及为什么协程在系统级工具中看不到。同时,需要推荐一些高级工具,比如htop,或者可视化工具,比如gdb的线程查看,或者特定语言的调试工具。 还要注意用户可能对某些概念混淆,比如协程线程的区别,是否需要简单说明。例如,协程由应用程序自己调度,不占用OS线程,所以用ps等命令看不到。这时候需要指导用户使用语言特定的工具来查看协程。 最后,可能需要给出一个总结性的步骤,帮助用户一步步排查,或者推荐进一步的资源,比如strace、perf等工具,或者文档链接。确保回答全面,覆盖进程线程协程,并且提供验证方法,避免遗漏。</think>在 Linux 系统中,**进程(Process)、线程(Thread)、协程(Coroutine/Goroutine)** 的关系可以通过不同层级的工具查看,但需注意以下几点: --- ### **1. 进程线程的关系** #### **(1)系统级查看(内核视角)** - **`pstree`** 显示进程树,线程以 `{进程名}` 形式标记(花括号表示线程): ```bash pstree -p <PID> # 查看进程及其线程树 ``` - **`ps`** 查看进程线程的详细信息: ```bash ps -eLf # 列出所有进程线程(`LWP` 列为线程ID) ps -T -p <PID> # 查看指定进程的所有线程(`SPID` 为线程ID) ``` - **`top`** 实时监控线程级资源占用: ```bash top -H -p <PID> # 显示指定进程线程级 CPU/内存使用 ``` - **`htop`**(需安装) 更友好的交互式查看(按 `F2` 设置显示线程): ```bash htop --tree -p <PID> # 树状显示进程线程 ``` --- ### **2. 协程(用户态轻量级线程)** 协程(如 GoGoroutine、Python 的 `asyncio`)**不由内核管理**,因此系统级工具(如 `ps`、`top`)无法直接查看。需借助语言或框架提供的工具: #### **(1)Go 语言Goroutine)** - **内置 `pprof` 工具** 在代码中启用 HTTP 调试接口,通过浏览器查看实时 Goroutine 堆栈: ```bash curl http://localhost:<debug_port>/debug/pprof/goroutine?debug=2 ``` - **命令行工具 `go tool pprof`** ```bash go tool pprof http://localhost:<debug_port>/debug/pprof/goroutine ``` #### **(2)Python(asyncio)** - **异步任务内省** 在代码中输出当前运行的任务: ```python import asyncio tasks = asyncio.all_tasks() print(f"Running tasks: {len(tasks)}") ``` - **调试工具 `aiomonitor`** 安装后嵌入代码,通过命令行实时监控协程: ```bash pip install aiomonitor ``` --- ### **3. 完整关系整合** #### **(1)可视化工具** - **`gdb` 调试器** 附加到进程并查看线程协程(需程序符号表支持): ```bash gdb -p <PID> (gdb) info threads # 查看所有线程 (gdb) thread apply all bt # 查看所有线程的调用栈 ``` - **`bpftrace` 或 `perf`** 通过内核级追踪工具分析线程协程行为(需较高权限): ```bash perf trace -p <PID> # 追踪进程的系统调用 ``` #### **(2)容器环境** 如果进程运行在容器中,需进入容器命名空间后再执行上述命令: ```bash nsenter -t <容器PID> -m -u -i -n -p # 进入容器命名空间 ``` --- ### **4. 总结** | **层级** | **工具** | **覆盖范围** | |----------|---------------------------|----------------------------------| | 进程 | `pstree`, `ps`, `htop` | 父子进程关系、资源占用 | | 线程 | `ps -T`, `top -H`, `gdb` | 线程ID、调用栈、资源监控 | | 协程 | 语言工具(如 `pprof`) | 协程数量、堆栈跟踪、阻塞分析 | --- ### **5. 示例流程** 1. **定位进程** ```bash ps aux | grep service-api # 获取进程PID ``` 2. **查看线程** ```bash ps -T -p <PID> # 列出线程 ``` 3. **分析协程(以 Go 为例)** ```bash curl http://localhost:6060/debug/pprof/goroutine?debug=2 ``` --- 如果需要更深入分析,可以结合 **`strace`(系统调用追踪)** 或 **`perf`(性能分析)** 工具。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

北岛末巷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值