最新资讯

  • 【linux】线程概念与控制

【linux】线程概念与控制

2025-05-14 13:01:00 2 阅读

🌈 个人主页:Zfox_
🔥 系列专栏:Linux

目录

  • 一:🔥 线程基本概念
    • 🦋 1-1 什么是线程
    • 🦋 1-2 分⻚式存储管理
      • 1-2-1 虚拟地址和⻚表的由来
      • 1-2-2 ⻚表
      • 1-2-3 ⻚⽬录结构
      • 1-2-4 两级⻚表的地址转换
      • 1-2-5 缺⻚异常
    • 🦋 1-3 线程的优点
    • 🦋 1-4 线程的缺点
  • 二:🔥 Linux进程VS线程
    • 🦋 2-1 进程和线程
    • 🦋 2-2 进程的多个线程共享
  • 三:🔥 Linux线程控制
    • 🦋 3-1 POSIX线程库
    • 🦋 3-2 创建线程
    • 🦋 3-3 线程终⽌
    • 🦋 3-4 线程等待
    • 🦋 3-5 分离线程
  • 四:🔥 线程ID及进程地址空间布局
  • 五:🔥 线程封装
  • 六:🔥线程栈
  • 七:🔥 共勉

一:🔥 线程基本概念

🦋 1-1 什么是线程

  • 在⼀个程序⾥的⼀个执⾏路线就叫做线程(thread)。更准确的定义是:线程是“⼀个进程内部的控制序列”。
  • ⼀切进程⾄少都有⼀个执⾏线程。
  • 在Linux系统中,在CPU眼中,看到的PCB都要⽐传统的进程更加轻量化 。
  • 透过进程虚拟地址空间,可以看到进程的⼤部分资源,将进程资源合理分配给每个执⾏流,就形成了线程执⾏流 。

  • 对于内核来说,线程是CPU调度的基本单位,进程是承担分配系统资源的基本实体
  • Linux内核中,没有真正意义上的线程,是复用PCB(进程控制块)模拟线程的TCB
  • 进程是包含内部所有的 task_struct,虚拟地址空间,页表,以及内存中的代码数据。
  • 对于 CPU 来讲,它并不能区分轻量级进程(线程在 Linux 下的叫法)还是单独的 task_struct (传统的进程)
  • 对于 OS 来讲,它能通过 pid 来区别进程,用 LWP 来区分线程。
  • 当 CPU 要执行线程时,不需要将进程的上下文以及虚拟地址空间、页表进行更换,并且有 cache 的局部性原理,不需要更换数据。

🦋 1-2 分⻚式存储管理

1-2-1 虚拟地址和⻚表的由来

🦁 思考⼀下,如果在没有虚拟内存和分⻚机制的情况下,每⼀个用户程序在物理内存上所对应的空间必须是连续的,如下图:


因为每⼀个程序的代码、数据⻓度都是不⼀样的,按照这样的映射⽅式,物理内存将会被分割成各种离散的、⼤⼩不同的块。经过⼀段运⾏时间之后,有些程序会退出,那么它们占据的物理内存空间可以被回收,导致这些物理内存都是以很多碎⽚的形式存在。

怎么办呢?我们希望操作系统提供给⽤⼾的空间必须是连续的,但是物理内存最好不要连续。此时虚拟内存和分⻚便出现了,如下图所⽰:

把物理内存按照⼀个固定的⻓度的⻚框进⾏分割,有时叫做物理⻚。每个⻚框包含⼀个物理⻚(page)。⼀个⻚的⼤⼩等于⻚框的⼤⼩。⼤多数 32位 体系结构⽀持 4KB 的⻚,⽽ 64位 体系结构⼀般会⽀持 8KB 的⻚。区分⼀⻚和⼀个⻚框是很重要的:

  • 🌳 ⻚框是⼀个存储区域。
  • 🌳 ⽽⻚是⼀个数据块,可以存放在任何⻚框或磁盘中。

有了这种机制,CPU 便并⾮是直接访问物理内存地址,⽽是通过虚拟地址空间来间接的访问物理内存地址。所谓的虚拟地址空间,是操作系统为每⼀个正在执⾏的进程分配的⼀个逻辑地址,在32位机上,其范围从0 ~ 4G-1。

  • 操作系统通过将虚拟地址空间和物理内存地址之间建⽴映射关系,也就是⻚表,这张表上记录了每⼀对⻚和⻚框的映射关系,能让CPU间接的访问物理内存地址。

🦁 总结⼀下,其思想是将虚拟内存下的逻辑地址空间分为若⼲⻚,将物理内存空间分为若⼲⻚框,通过⻚表便能把连续的虚拟内存,映射到若⼲个不连续的物理内存⻚。这样就解决了使⽤连续的物理内存造成的碎⽚问题。

1-2-2 ⻚表

⻚表中的每⼀个表项,指向⼀个物理⻚的开始地址。在 32 位系统中,虚拟内存的最⼤空间是 4GB ,这是每⼀个⽤⼾程序都拥有的虚拟内存空间。既然需要让 4GB 的虚拟内存全部可⽤,那么⻚表中就需要能够表⽰这所有的 4GB 空间,那么就⼀共需要 4GB/4KB = 1048576 个表项。如下图所⽰:


虚拟内存看上去被虚线“分割”成⼀个个单元,其实并不是真的分割,虚拟内存仍然是连续的。这个虚线的单元仅仅表⽰它与⻚表中每⼀个表项的映射关系,并最终映射到相同⼤⼩的⼀个物理内存⻚上。

⻚表中的物理地址,与物理内存之间,是随机的映射关系,哪⾥可⽤就指向哪⾥(物理⻚)。虽然最终使⽤的物理内存是离散的,但是与虚拟内存对应的线性地址是连续的。处理器在访问数据、获取指令时,使⽤的都是线性地址,只要它是连续的就可以了,最终都能够通过⻚表找到实际的物理地址。

在 32 位系统中,地址的⻓度是 4 个字节,那么⻚表中的每⼀个表项就是占⽤ 4 个字节。所以⻚表占据的总空间⼤⼩就是: 1048576*4 = 4MB 的⼤⼩。也就是说映射表⾃⼰本⾝,就要占⽤ 4MB /4KB = 1024 个物理⻚。这会存在哪些问题呢?

  • 回想⼀下,当初为什么使⽤⻚表,就是要将进程划分为⼀个个⻚可以不⽤连续的存放在物理内存中,但是此时⻚表就需要1024个连续的⻚框,似乎和当时的⽬标有点背道⽽驰了…
  • 此外,根据局部性原理可知,很多时候进程在⼀段时间内只需要访问某⼏个⻚就可以正常运⾏了。因此也没有必要⼀次让所有的物理⻚都常驻内存。

解决需要⼤容量⻚表的最好⽅法是:把⻚表看成普通的⽂件,对它进⾏离散分配,即对⻚表再分⻚,由此形成多级⻚表的思想。

为了解决这个问题,可以把这个单⼀⻚表拆分成 1024 个体积更⼩的映射表。如下图所⽰。这样⼀来,1024(每个表中的表项个数) * 1024(表的个数),仍然可以覆盖 4GB 的物理内存空间。

这⾥的每⼀个表,就是真正的⻚表,所以⼀共有 1024 个⻚表。⼀个⻚表⾃⾝占⽤ 4KB ,那么1024 个⻚表⼀共就占⽤了 4MB 的物理内存空间,和之前没差别啊?

从总数上看是这样,但是⼀个应⽤程序是不可能完全使⽤全部的 4GB 空间的,也许只要⼏⼗个⻚表就可以了。例如:⼀个⽤⼾程序的代码段、数据段、栈段,⼀共就需要 10 MB 的空间,那么使⽤ 3 个⻚表就⾜够了。

计算过程:
每⼀个⻚表项指向⼀个 4KB 的物理⻚,那么⼀个⻚表中 1024 个⻚表项,⼀共能覆盖 4MB 的物理内存; 那么 10MB 的程序,向上对⻬取整之后(4MB 的倍数,就是 12 MB),就需要 3 个⻚表就可以了。

1-2-3 ⻚⽬录结构

到⽬前为⽌,每⼀个⻚框都被⼀个⻚表中的⼀个表项来指向了,那么这 1024 个⻚表也需要被管理起来。管理⻚表的表称之为⻚⽬录表,形成⼆级⻚表。如下图所⽰:

  • 所有⻚表的物理地址被⻚⽬录表项指向
  • ⻚⽬录的物理地址被 CR3 寄存器 指向,这个寄存器中,保存了当前正在执⾏任务的⻚⽬录地址。

所以操作系统在加载⽤⼾程序的时候,不仅仅需要为程序内容来分配物理内存,还需要为⽤来保存程序的⻚⽬录和⻚表分配物理内存。

1-2-4 两级⻚表的地址转换

下⾯以⼀个逻辑地址为例。将逻辑地址( 0000000000,0000000001,11111111111 )转换为物理地址的过程:

  1. 在32位处理器中,采⽤4KB的⻚⼤⼩,则虚拟地址中低12位为⻚偏移,剩下⾼20位给⻚表,分成两级,每个级别占10个bit(10+10)。
  2. CR3 寄存器 读取⻚⽬录起始地址,再根据⼀级⻚号查⻚⽬录表,找到下⼀级⻚表在物理内存中存放位置。
  3. 根据⼆级⻚号查表,找到最终想要访问的内存块号。
  4. 结合⻚内偏移量得到物理地址

  • 注:⼀个物理⻚的地址⼀定是 4KB 对⻬的(最后的 12 位全部为 0 ),所以其实只需要记录物理⻚地址的⾼ 20 位即可。
  • 以上其实就是 MMU 的⼯作流程。MMU(Memory Manage Unit)是⼀种硬件电路,其速度很快,主要⼯作是进⾏内存管理,地址转换只是它承接的业务之⼀。

到这⾥其实还有个问题,MMU要先进⾏两次⻚表查询确定物理地址,在确认了权限等问题后,MMU再将这个物理地址发送到总线,内存收到之后开始读取对应地址的数据并返回。那么当⻚表变为N级时,就变成了N次检索+1次读写。可⻅,⻚表级数越多查询的步骤越多,对于CPU来说等待时间越⻓,效率越低。

让我们现在总结⼀下:单级⻚表对连续内存要求⾼,于是引⼊了多级⻚表,但是多级⻚表也是⼀把双刃剑,在减少连续存储要求且减少存储空间的同时降低了查询效率。

有没有提升效率的办法呢?计算机科学中的所有问题,都可以通过添加⼀个中间层来解决。 MMU 引⼊了新武器,江湖⼈称快表的 TLB (其实,就是缓存)

当 CPU 给 MMU 传新虚拟地址之后, MMU 先去问 TLB 那边有没有,如果有就直接拿到物理地址发到总线给内存,⻬活。但 TLB 容量⽐较⼩,难免发⽣ Cache Miss ,这时候 MMU 还有保底的⽼武器 ⻚表,在⻚表中找到之后 MMU 除了把地址发到总线传给内存,还把这条映射关系给到TLB,让它记录⼀下刷新缓存。

1-2-5 缺⻚异常

设想,CPU 给 MMU 的虚拟地址,在 TLB 和⻚表都没有找到对应的物理⻚,该怎么办呢?其实这就是缺⻚异常 Page Fault ,它是⼀个由硬件中断触发的可以由软件逻辑纠正的错误。

假如⽬标内存⻚在物理内存中没有对应的物理⻚或者存在但⽆对应权限,CPU 就⽆法获取数据,这种情况下CPU 就会报告⼀个缺⻚错误。

由于 CPU 没有数据就⽆法进⾏计算,CPU 罢⼯了用户进程也就出现了缺⻚中断,进程会从用户态切换到内核态,并将缺⻚中断交给内核的 Page Fault Handler 处理。

缺⻚中断会交给 PageFaultHandler 处理,其根据缺⻚中断的不同类型会进⾏不同的处理:

  • Hard Page Fault 也被称为 Major Page Fault ,翻译为 硬缺⻚错误 / 主要缺⻚错误,这时物理内存中没有对应的物理⻚,需要CPU打开磁盘设备读取到物理内存中,再让 MMU 建⽴虚拟地址和物理地址的映射。
  • Soft Page Fault 也被称为 Minor Page Fault ,翻译为 软缺⻚错误 / 次要缺⻚错误,这时物理内存中是存在对应物理⻚的,只不过可能是其他进程调⼊的,发出缺⻚异常的进程不知道⽽已,此时 MMU 只需要建⽴映射即可,⽆需从磁盘读取写⼊内存,⼀般出现在多进程共享内存区域。
  • Invalid Page Fault 翻译为 ⽆效缺⻚错误,⽐如进程访问的内存地址越界访问,⼜⽐如对空指针解引⽤内核就会报 segment fault 错误中断进程直接挂掉。

🦋 1-3 线程的优点

  • 创建⼀个新线程的代价要⽐创建⼀个新进程⼩得多。
  • 与进程之间的切换相⽐,线程之间的切换需要操作系统做的⼯作要少很多。
    • 最主要的区别是 线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下⽂切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
    • 另外⼀个隐藏的损耗是 上下⽂的切换会扰乱处理器的缓存机制。简单的说,⼀旦去切换上下⽂,处理器中所有已经缓存的内存地址⼀瞬间都作废了。还有⼀个显著的区别是当你改变虚拟内存空间的时候,处理的⻚表缓冲 TLB (快表)会被全部刷新,这将导致内存的访问在⼀段时间内相当的低效。但是在线程的切换中,不会出现这个问题,当然还有 硬件 cache。
  • 线程占⽤的资源要⽐进程少很。
  • 能充分利⽤多处理器的可并⾏数量。
  • 在等待慢速 I/O 操作结束的同时,程序可执⾏其他的计算任务 。
  • 计算密集型应⽤,为了能在多处理器系统上运⾏,将计算分解到多个线程中实现。
  • I/O 密集型应⽤,为了提⾼性能,将 I/O 操作重叠。线程可以同时等待不同的 I/O 操作。

🦋 1-4 线程的缺点

  • 性能损失
    • ⼀个很少被外部事件阻塞的计算密集型线程往往⽆法与其它线程共享同⼀个处理器。如果计算密集型线程的数量⽐可⽤的处理器多,那么可能会有较⼤的性能损失,这⾥的性能损失指的是增加了额外的同步和调度开销,⽽可⽤的资源不变。
  • 健壮性降低
    • 编写多线程需要更全⾯更深⼊的考虑,在⼀个多线程程序⾥,因时间分配上的细微偏差或者因共享了不该共享的变量⽽造成不良影响的可能性是很⼤的,换句话说线程之间是缺乏保护的。
  • 缺乏访问控制
    • 进程是访问控制的基本粒度,在⼀个线程中调⽤某些OS函数会对整个进程造成影响。
  • 编程难度提⾼

二:🔥 Linux进程VS线程

🦋 2-1 进程和线程

  • 进程是资源分配的基本单位
  • 线程是调度的基本单位
  • 线程共享进程数据,但也拥有⾃⼰的⼀部分数据:
    • 线程ID
    • ⼀组寄存器
    • errno
    • 信号屏蔽字
    • 调度优先级

🦋 2-2 进程的多个线程共享

同⼀地址空间,因此Text Segment、Data Segment都是共享的,如果定义⼀个函数,在各线程中都可以调⽤,如果定义⼀个全局变量,在各线程中都可以访问到,除此之外,各线程还共享以下进程资源和环境:

  • ⽂件描述符表
  • 每种信号的处理⽅式(SIG_ IGN、SIG_ DFL或者⾃定义的信号处理函数)
  • 当前⼯作⽬录
  • ⽤⼾id和组id
    进程和线程的关系如下图:

    如何看待之前学习的单进程?具有⼀个线程执⾏流的进程!

三:🔥 Linux线程控制

🦋 3-1 POSIX线程库

  • 与线程有关的函数构成了⼀个完整的系列,绝⼤多数函数的名字都是以 “pthread_” 打头的
  • 要使⽤这些函数库,要通过引⼊头⽂件
  • 链接这些线程函数库时要使⽤编译器命令的 “-lpthread” 选项

🦋 3-2 创建线程

功能:创建⼀个新的线程
原型:
	int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *
(*start_routine)(void*), void *arg);

参数:
	thread:返回线程ID
	attr:设置线程的属性,attr为NULL表⽰使⽤默认属性
	start_routine:是个函数地址,线程启动后要执⾏的函数
	arg:传给线程启动函数的参数
	
返回值:成功返回0;失败返回错误码

错误检查:

  • 传统的⼀些函数是,成功返回0,失败返回-1,并且对全局变量 errno 赋值以指⽰错误。
  • pthreads 函数出错时不会设置全局变量 errno(⽽⼤部分其他 POSIX 函数会这样做)。⽽是将错误代码通过返回值返回
  • pthreads 同样也提供了线程内的 errno 变量,以⽀持其它使⽤ errno 的代码。对于 pthreads 函数的错误,建议通过返回值业判定,因为读取返回值要⽐读取线程内的 errno 变量的开销更⼩
#include 
#include 
#include 
#include 
#include 

void *rout(void *arg)
{
    for (;;)
    {
        printf("I'am thread 1
");
        sleep(1);
    }
}

int main(void)
{
    pthread_t tid;
    int ret;
    if ((ret = pthread_create(&tid, NULL, rout, NULL)) != 0)
    {
        fprintf(stderr, "pthread_create : %s
", strerror(ret));
        exit(EXIT_FAILURE);
    }
    for (;;)
    {
        printf("I'am main thread
");
        sleep(1);
    }
    return 0;
}

输出:
I'am main thread
I'am thread 1
#include 
// 获取线程ID
pthread_t pthread_self(void);

打印出来的 tid 是通过 pthread 库中有函数 pthread_self 得到的,它返回⼀个 pthread_t 类型的变量,指代的是调⽤ pthread_self 函数的线程的 “ID”。

怎么理解这个“ID”呢?这个“ID”是 pthread 库给每个线程定义的进程内唯⼀标识,是 pthread 库维持的。

由于每个进程有⾃⼰独⽴的内存空间,故此“ID”的作⽤域是进程级⽽⾮系统级(内核不认识)。

其实 pthread 库也是通过内核提供的系统调⽤(例如clone)来创建线程的,⽽内核会为每个线程创建系统全局唯⼀的“ID”来唯⼀标识这个线程。

使⽤PS命令查看线程信息

运⾏代码后执⾏:

$ ps -aL | head -1 && ps -aL | grep mythread
    PID     LWP  TTY        TIME  CMD
2711838 2711838 pts/235 00:00:00 mythread
2711838 2711839 pts/235 00:00:00 mythread

-L 选项:打印线程信息

LWP 是什么呢?LWP 得到的是真正的线程ID。之前使⽤ pthread_self 得到的这个数实际上是⼀个地址,在虚拟地址空间上的⼀个地址,通过这个地址,可以找到关于这个线程的基本信息,包括线程ID,线程栈,寄存器等属性。

在 ps -aL 得到的线程ID,有⼀个线程ID和进程ID相同,这个线程就是主线程,主线程的栈在虚拟地址空间的栈上,⽽其他线程的栈在是在共享区(堆栈之间),因为 pthread 系列函数都是 pthread库 提供给我们的。⽽pthread库 是在共享区的。所以除了主线程之外的其他线程的栈都在共享区。

🦋 3-3 线程终⽌

如果需要只终⽌某个线程⽽不终⽌整个进程,可以有三种⽅法:

  1. 从线程函数return。这种⽅法对主线程不适⽤,从main函数return相当于调⽤exit。
  2. 线程可以调⽤pthread_ exit终⽌⾃⼰。
  3. ⼀个线程可以调⽤pthread_ cancel终⽌同⼀进程中的另⼀个线程。

pthread_exit 函数

功能:线程终⽌

原型:
void pthread_exit(void *value_ptr);

参数:
value_ptr:value_ptr不要指向⼀个局部变量。

返回值:
⽆返回值,跟进程⼀样,线程结束的时候⽆法返回到它的调⽤者(⾃⾝)

需要注意,pthread_exit或者return返回的指针所指向的内存单元必须是全局的或者是⽤malloc分配的,不能在线程函数的栈上分配,因为当其它线程得到这个返回指针时线程函数已经退出了。

pthread_cancel 函数

功能:取消⼀个执⾏中的线程

原型:
int pthread_cancel(pthread_t thread);

参数:
thread:线程ID

返回值:成功返回0;失败返回错误码

🦋 3-4 线程等待

为什么需要线程等待?

  • 已经退出的线程,其空间没有被释放,仍然在进程的地址空间内。
  • 创建新的线程不会复⽤刚才退出线程的地址空间。
功能:等待线程结束

原型
int pthread_join(pthread_t thread, void **value_ptr);

参数:
thread:线程ID
value_ptr:它指向⼀个指针,后者指向线程的返回值

返回值:成功返回0;失败返回错误码

调⽤该函数的线程将挂起等待,直到 id 为 thread 的线程终⽌。thread 线程以不同的⽅法终⽌,通过 pthread_join 得到的终⽌状态是不同的,总结如下:

  1. 如果 thread 线程通过 return 返回, value_ ptr 所指向的单元⾥存放的是 thread 线程函数的返回值。
  2. 如果 thread 线程被别的线程调⽤ pthread_ cancel 异常终掉, value_ ptr 所指向的单元⾥存放的是常
    数 PTHREAD_ CANCELED。
  3. 如果 thread 线程是⾃⼰调⽤ pthread_exit 终⽌的, value_ptr 所指向的单元存放的是传给 pthread_exit 的参数。
  4. 如果对 thread 线程的终⽌状态不感兴趣,可以传 NULL 给 value_ ptr 参数。
#include 
#include 
#include 
#include 
#include 

void *thread1(void *arg)
{
    printf("thread 1 returning ... 
");
    int *p = (int *)malloc(sizeof(int));
    *p = 1;
    return (void *)p;
}

void *thread2(void *arg)
{
    printf("thread 2 exiting ...
");
    int *p = (int *)malloc(sizeof(int));
    *p = 2;
    pthread_exit((void *)p);
}

void *thread3(void *arg)
{
    while (1)
    { //
        printf("thread 3 is running ...
");
        sleep(1);
    }

    return NULL;
}

int main(void)
{
    pthread_t tid;
    void *ret;
    // thread 1 return
    pthread_create(&tid, NULL, thread1, NULL);
    pthread_join(tid, &ret);
    printf("thread return, thread id %X, return code:%d
", tid, *(int *)ret);
    free(ret);
    // thread 2 exit
    pthread_create(&tid, NULL, thread2, NULL);
    pthread_join(tid, &ret);
    printf("thread return, thread id %X, return code:%d
", tid, *(int *)ret);
    free(ret);
    // thread 3 cancel by other
    pthread_create(&tid, NULL, thread3, NULL);
    sleep(3);
    pthread_cancel(tid);
    pthread_join(tid, &ret);
    if (ret == PTHREAD_CANCELED)
        printf("thread return, thread id %X, return code:PTHREAD_CANCELED
",tid);
    else
        printf("thread return, thread id %X, return code:NULL
", tid);

    return 0;
}

输出:

[root@localhost linux]# ./a.out
thread 1 returning ...
thread return, thread id 5AA79700, return code:1
thread 2 exiting ...
thread return, thread id 5AA79700, return code:2
thread 3 is running ...
thread 3 is running ...
thread 3 is running ...
thread return, thread id 5AA79700, return code:PTHREAD_CANCELED

🦋 3-5 分离线程

  • 默认情况下,新创建的线程是 joinable 的,线程退出后,需要对其进⾏ pthread_join 操作,否则⽆法释放资源,从⽽造成系统泄漏。
  • 如果不关⼼线程的返回值,join 是⼀种负担,这个时候,我们可以告诉系统,当线程退出时,⾃动释放线程资源。
int pthread_detach(pthread_t thread);

可以是线程组内其他线程对⽬标线程进⾏分离,也可以是线程⾃⼰分离:

pthread_detach(pthread_self());

joinable和分离是冲突的,⼀个线程不能既是joinable⼜是分离的。

#include 
#include 
#include 
#include 
#include 

void *thread_run(void *arg)
{
    pthread_detach(pthread_self());
    printf("%s
", (char *)arg);
    return NULL;
}

int main(void)
{
    pthread_t tid;
    if (pthread_create(&tid, NULL, thread_run, (void*)"thread1 run...") != 0)
    {
        printf("create thread error
");
        return 1;
    }
    int ret = 0;
    sleep(1); // 很重要,要让线程先分离,再等待
    if (pthread_join(tid, NULL) == 0)
    {
        printf("pthread wait success
");
        ret = 0;
    }
    else
    {
        printf("pthread wait failed
");
        ret = 1;
    }
    return ret;
}

输出:

thread1 run...
pthread wait failed

四:🔥 线程ID及进程地址空间布局

  • pthread_ create函数会产⽣⼀个线程ID,存放在第⼀个参数指向的地址中。该线程ID和前⾯说的线程ID不是⼀回事。
  • 前⾯讲的线程ID属于进程调度的范畴。因为线程是轻量级进程,是操作系统调度器的最⼩单位,所以需要⼀个数值来唯⼀表⽰该线程。
  • pthread_ create函数第⼀个参数指向⼀个虚拟内存单元,该内存单元的地址即为新创建线程的线程ID,属于NPTL线程库的范畴。线程库的后续操作,就是根据该线程ID来操作线程的。
  • 线程库NPTL提供了pthread_ self函数,可以获得线程⾃⾝的ID:
 pthread_t pthread_self(void);

pthread_t 到底是什么类型呢?取决于实现。对于Linux⽬前实现的NPTL实现⽽⾔,pthread_t类型的线程ID,本质就是⼀个进程地址空间上的⼀个地址。

五:🔥 线程封装

Thread.hpp

#ifndef _THREAD_HPP__
#define _THREAD_HPP__

#include 
#include 
#include 
#include 
#include 
#include 

namespace ThreadModule
{
    using func_t = std::function<void()>;
    static int number = 1;
    enum class TSTATUS
    {
        NEW,
        RUNNING,
        STOP
    };

    class Thread
    {
    private:
        // 成员方法 this
        static void *Routine(void *args)
        {
            Thread *t = static_cast<Thread *>(args); // 传入this指针
            t->_status = TSTATUS::RUNNING;
            t->_func();
            return nullptr;
        }

        void EnableDetach() { _joinable = false; }

    public:
        Thread(func_t func)
            : _func(func), _status(TSTATUS::NEW), _joinable(true)
        {
            _name = "Thread-" + std::to_string(number++);
            _pid = getpid();
        }

        bool Start()
        {
            if (_status != TSTATUS::RUNNING)
            {
                int n = ::pthread_create(&_tid, nullptr, Routine, this);
                if (n != 0)
                    return false;
                return true;
            }
            return false;
        }

        bool Stop()
        {
            if (_status == TSTATUS::RUNNING)
            {
                int n = ::pthread_cancel(_tid);
                if (n != 0)
                    return false;
                _status = TSTATUS::STOP;
                return true;
            }
            return false;
        }

        bool Join()
        {
            if (_joinable)
            {
                int n = ::pthread_join(_tid, nullptr);
                if (n != 0)
                    return false;
                _status = TSTATUS::STOP;
                return true;
            }
            return false;
        }

        void Detach()
        {
            EnableDetach();
            pthread_detach(_tid);
        }

        bool IsJoinable() { return _joinable; }
        std::string Name() { return _name; }

        ~Thread()
        {
        }

    private:
        std::string _name;
        pthread_t _tid;
        pid_t _pid;
        bool _joinable; // 是否是分离的,默认不是
        func_t _func;
        TSTATUS _status;
    };
}

#endif

main.cc

#include "Thread.hpp"
#include 
#include 

#define NUM 10

using thread_ptr_t = std::shared_ptr<ThreadModule::Thread>;

int main()
{
    // 先描述,再组织!
    std::unordered_map<std::string, thread_ptr_t> threads;
    // 创建多线程
    for (int i = 0; i < NUM; i++)
    {
        thread_ptr_t t = std::make_shared<ThreadModule::Thread>([](){
            while(true)
            {
                std::cout << "hello world" << std::endl;
                sleep(1);
            } 
        });
        threads[t->Name()] = t;
    }

    for(auto &thread:threads)
    {
        thread.second->Start();
    }

    for(auto &thread:threads)
    {
        thread.second->Join();
    }
    
    return 0;
}
// 运⾏结果查询
$ ps -aL
   PID      LWP  TTY       	TIME CMD
195828   195828  pts/1 	00:00:00 main
195828   195829  pts/1 	00:00:00 Thread-0
195828   195830  pts/1 	00:00:00 Thread-1

六:🔥线程栈

虽然 Linux 将线程和进程不加区分的统⼀到了 task_struct ,但是对待其地址空间的 stack 还是
有些区别的。

  • 对于 Linux 进程或者说主线程,简单理解就是main函数的栈空间,在fork的时候,实际上就是复制了⽗亲的 stack 空间地址,然后写时拷⻉(cow)以及动态增⻓。如果扩充超出该上限则栈溢出会报段错误(发送段错误信号给该进程)。进程栈是唯⼀可以访问未映射⻚⽽不⼀定会发⽣段错误⸺超出扩充上限才报。
  • 然⽽对于主线程⽣成的⼦线程⽽⾔,其 stack 将不再是向下⽣⻓的,⽽是事先固定下来的。线程栈⼀般是调⽤glibc/uclibc等的 pthread 库接⼝ pthread_create 创建的线程,在⽂件映射区(或称之为共享区)。其中使⽤ mmap 系统调⽤,这个可以从 glibc 的 nptl/allocatestack.c 中的 allocate_stack 函数中看到:
mem = mmap (NULL, size, prot,
		MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);

此调⽤中的 size 参数的获取很是复杂,你可以⼿⼯传⼊stack的⼤⼩,也可以使⽤默认的,⼀般⽽⾔就是默认的 8M 。这些都不重要,重要的是,这种stack不能动态增⻓,⼀旦⽤尽就没了,这是和⽣成进程的fork不同的地⽅。在glibc中通过mmap得到了stack之后,底层将调⽤ sys_clone 系统调⽤:

int sys_clone(struct pt_regs *regs)
{
    unsigned long clone_flags;
    unsigned long newsp;
    int __user *parent_tidptr, *child_tidptr;
    clone_flags = regs->bx;
    // 获取了mmap得到的线程的stack指针
    newsp = regs->cx;
    parent_tidptr = (int __user *)regs->dx;
    child_tidptr = (int __user *)regs->di;
    if (!newsp)
        newsp = regs->sp;
    return do_fork(clone_flags, newsp, regs, 0, parent_tidptr, child_tidptr);
}

因此,对于⼦线程的 stack ,它其实是在进程的地址空间中map出来的⼀块内存区域,原则上是线程私有的,但是同⼀个进程的所有线程⽣成的时候,是会浅拷⻉⽣成者的 task_struct 的很多字段,如果愿意,其它线程也还是可以访问到的,于是⼀定要注意。

七:🔥 共勉

以上就是我对 【linux】线程概念与控制 的理解,觉得这篇博客对你有帮助的,可以点赞收藏关注支持一波~😉

本文地址:https://www.vps345.com/10477.html

搜索文章

Tags

PV计算 带宽计算 流量带宽 服务器带宽 上行带宽 上行速率 什么是上行带宽? CC攻击 攻击怎么办 流量攻击 DDOS攻击 服务器被攻击怎么办 源IP docker 容器 运维 java-rabbitmq java 服务器安全 网络安全策略 防御服务器攻击 安全威胁和解决方案 程序员博客保护 数据保护 安全最佳实践 服务器 linux 游戏 云计算 网络工程师 网络管理 软考 2024 2024年上半年 下午真题 答案 Deepseek Deepseek-R1 大模型 私有化部署 推理模型 deepseek DeepSeek-R1 API接口 物联网 ubuntu 人工智能 redis c语言 英语 Ollama Qwen2.5-coder 离线部署 ddos android 网络安全 web安全 RTSP xop RTP RTSPServer 推流 视频 DNS php 开发语言 javascript 前端 chrome edge 神经网络 深度学习 计算机视觉 卷积神经网络 mysql adb 进程 操作系统 进程控制 Ubuntu django CH340 单片机 嵌入式硬件 串口驱动 CH341 uart 485 centos gcc centos 7 网络 tcp/ip 网络协议 ip协议 macos windows conda 阿里云 llama 算法 opencv 自然语言处理 语言模型 ai nlp ssh 数据库系统 游戏引擎 学习 YOLO pytorch vscode python yolov5 harmonyos 华为 机器学习 部署 MCP jvm 虚拟机 经验分享 笔记 学习方法 模型联网 API CherryStudio 计算机外设 bug https AIGC 人工智能生成内容 Dify 数据分析 vue.js spring boot nginx 开发环境 arm开发 架构 负载均衡 tomcat 数据库 oracle 关系型 安全 分布式 科技 个人开发 网络结构图 pycharm ide C 环境变量 进程地址空间 c++ 并查集 leetcode 策略模式 udp 本地环回 bind asm node.js virtualenv dify 知识库 本地化部署 Ubuntu DeepSeek DeepSeek Ubuntu DeepSeek 本地部署 DeepSeek 知识库 DeepSeek 私有化知识库 本地部署 DeepSeek DeepSeek 私有化部署 USB转串口 ui vnc 后端 typescript 鸿蒙 eureka fastapi mcp mcp-proxy mcp-inspector fastapi-mcp agent sse debian PVE live555 rtsp rtp word图片自动上传 word一键转存 复制word图片 复制word图文 复制word公式 粘贴word图文 粘贴word公式 ffmpeg 音视频 视频编解码 pip fstab gnu 高级IO epoll kafka AI大模型 大模型技术 本地部署大模型 apache ubuntu 18.04 安装教程 云原生 ip grub 版本升级 扩容 计算机网络 kubernetes 自动化 jenkins gitee springsecurity6 oauth2 授权服务器 前后端分离 智能路由器 外网访问 内网穿透 端口映射 outlook 错误代码2603 无网络连接 2603 像素流送api 像素流送UE4 像素流送卡顿 像素流送并发支持 目标跟踪 目标检测 OpenVINO 推理应用 jellyfin nas mcu prometheus grafana Doris搭建 docker搭建Doris Doris搭建过程 linux搭建Doris Doris搭建详细步骤 Doris部署 Linux PID LDAP 安卓模拟器 mac rag ragflow ollama 大模型部署 集成学习 集成测试 spring filezilla 无法连接服务器 连接被服务器拒绝 vsftpd 331/530 kylin llm opengl ip命令 新增网卡 新增IP 启动网卡 多线程 快捷键 旋转屏幕 自动操作 pygame github ubuntu20.04 Linux 开机黑屏 uni-app 上传视频文件到服务器 uniApp本地上传视频并预览 uniapp移动端h5网页 uniapp微信小程序上传视频 uniapp app端视频上传 uniapp uview组件库 axure 富文本编辑器 VMware 数据结构 链表 cursor Linux 维护模式 华为认证 交换机 开源 华为云 pillow xcode 监控k8s集群 集群内prometheus websocket ssh漏洞 ssh9.9p2 CVE-2025-23419 小程序 微信小程序域名配置 微信小程序服务器域名 微信小程序合法域名 小程序配置业务域名 微信小程序需要域名吗 微信小程序添加域名 C# MQTTS 双向认证 emqx oracle fusion oracle中间件 TCP WebServer 大数据 spark hive devops elasticsearch 性能优化 transformer perf GPU环境配置 Ubuntu22 CUDA PyTorch Anaconda安装 Flask FastAPI Waitress Gunicorn uWSGI Uvicorn spring cloud cpu 内存 实时 使用 机器人 多线程服务器 Linux网络编程 媒体 dubbo 面试 课程设计 json 运维开发 聚类 程序 编程 性能分析 实时音视频 实时互动 sublime text 编辑器 compose 持续部署 vim HarmonyOS Next AI编程 dell服务器 IIS .net core Hosting Bundle .NET Framework vs2022 UEFI Legacy MBR GPT U盘安装操作系统 windows 服务器安装 虚拟现实 JAVA Java c# qt QT 5.12.12 QT开发环境 Ubuntu18.04 EtherNet/IP串口网关 EIP转RS485 EIP转Modbus EtherNet/IP网关协议 EIP转RS485网关 EIP串口服务器 进程优先级 调度队列 进程切换 yum docker-compose docker compose Alexnet matplotlib fonts-noto-cjk protobuf 序列化和反序列化 安装 zotero WebDAV 同步失败 代理模式 腾讯云 maven svn Ubuntu20.04 GLIBC 2.35 宝塔面板访问不了 宝塔面板网站访问不了 宝塔面板怎么配置网站能访问 宝塔面板配置ip访问 宝塔面板配置域名访问教程 宝塔面板配置教程 audio vue音乐播放器 vue播放音频文件 Audio音频播放器自定义样式 播放暂停进度条音量调节快进快退 自定义audio覆盖默认样式 cron crontab日志 虚拟显示器 远程控制 ue4 着色器 ue5 虚幻 react.js 前端面试题 AI git 思科 openvpn server openvpn配置教程 centos安装openvpn arm Kali Linux 黑客 渗透测试 信息收集 信息与通信 远程连接 rdp 实验 linux内核 程序人生 flutter Google pay Apple pay jar 离线部署dify numpy 远程工作 intellij idea lvm 磁盘挂载 磁盘分区 k8s rpc vue3 大文件分片上传断点续传及进度条 如何批量上传超大文件并显示进度 axios大文件切片上传详细教 node服务器合并切片 vue3大文件上传报错提示错误 vu大文件秒传跨域报错cors 命令 visual studio code intellij-idea postgresql 微信小程序 bash VMware安装Ubuntu Ubuntu安装k8s 创意 社区 ruoyi VMware创建虚拟机 源码剖析 rtsp实现步骤 流媒体开发 vr gpu算力 IM即时通讯 QQ 微信 企业微信 剪切板对通 HTML FORMAT webdav shell Hive环境搭建 hive3环境 Hive远程模式 大模型入门 大模型教程 GPU DeepSeek Hyper-V WinRM TrustedHosts tcp DevEco Studio 串口服务器 指令 redhat 防火墙 端口号 开放端口 访问列表 golang C++ WSL2 Ubuntu22.04 虚拟化 开发人员主页 stm32 1024程序员节 程序员 KylinV10 麒麟操作系统 Vmware unix WLAN 压测 ECS ESP32 camera Arduino 电子信息 chatgpt oneapi 大模型微调 软件工程 软件构建 rabbitmq SSH Xterminal WebUI DeepSeek V3 Linux无人智慧超市 LInux多线程服务器 QT项目 LInux项目 单片机项目 沙盒 AI-native gpt Agent LLM CrewAI edge浏览器 http pdf efficientVIT YOLOv8替换主干网络 TOLOv8 ros 话题通信 服务通信 iftop 网络流量监控 网络药理学 生信 生物信息学 gromacs 分子动力学模拟 MD 动力学模拟 ssl 驱动开发 database ecmascript nextjs react reactjs flask vmware 卡死 RustDesk自建服务器 rustdesk服务器 docker rustdesk HTML audio 控件组件 vue3 audio音乐播放器 Audio标签自定义样式默认 vue3播放音频文件音效音乐 自定义audio播放器样式 播放暂停调整声音大小下载文件 回显服务器 UDP的API使用 ai小智 语音助手 ai小智配网 ai小智教程 智能硬件 esp32语音助手 diy语音助手 burp suite 抓包 remote-ssh open webui MacOS录屏软件 rancher sql 缓存 EtherCAT转Modbus ECT转Modbus协议 EtherCAT转485网关 ECT转Modbus串口网关 EtherCAT转485协议 ECT转Modbus网关 powerpoint 升级 CVE-2024-7347 漏洞 Dell R750XS 统信 NFS iot odoo 服务器动作 Server action deepseek-r1 大模型本地部署 nftables oceanbase 传统数据库升级 银行 notepad 温湿度数据上传到服务器 Arduino HTTP 华为od hadoop VSCode postman 测试工具 aws FTP 服务器 wsl ipython rust java-ee YOLOv8 NPU Atlas800 A300I pro asi_bench rtsp服务器 rtsp server android rtsp服务 安卓rtsp服务器 移动端rtsp服务 大牛直播SDK 私有化 本地部署 安卓 智慧农业 开源鸿蒙 团队开发 灵办AI matlab 电脑 远程桌面 远程服务 Trae IDE AI 原生集成开发环境 Trae AI 政务 分布式系统 监控运维 Prometheus Grafana mount挂载磁盘 wrong fs type LVM挂载磁盘 Centos7.9 硬件工程 嵌入式实习 深度求索 私域 CPU 使用率 系统监控工具 linux 命令 express p2p 7z 软件需求 agi Cline 主从复制 etcd 数据安全 RBAC selenium 网络爬虫 浪潮信息 AI服务器 报错 多层架构 解耦 服务器无法访问 ip地址无法访问 无法访问宝塔面板 宝塔面板打不开 嵌入式 linux驱动开发 WSL2 上安装 Ubuntu SRS 流媒体 直播 .netcore GaN HEMT 氮化镓 单粒子烧毁 辐射损伤 辐照效应 openEuler 教程 环境搭建 Maven 微信分享 Image wxopensdk zabbix mariadb Mac内存不够用怎么办 vite 区块链 虚拟局域网 rust腐蚀 rc.local 开机自启 systemd 麒麟 webrtc Docker Desktop 云电竞 云电脑 todesk milvus MacMini Mac 迷你主机 mini Apple C语言 centos-root /dev/mapper yum clean all df -h / du -sh maxkb ARG 前端框架 读写锁 html jupyter 游戏程序 ios 互信 fork wait waitpid exit vSphere vCenter 软件定义数据中心 sddc 3d 数学建模 计算生物学 生物信息 基因组 list CPU 主板 电源 网卡 ocr 大语言模型 LLMs jmeter 软件测试 stm32项目 国产数据库 瀚高数据库 数据迁移 下载安装 onlyoffice gitlab unity vue gateway Clion Nova ResharperC++引擎 Centos7 远程开发 python2 ubuntu24.04 VMware安装mocOS macOS系统安装 硬件架构 系统架构 elk 产品经理 microsoft 进程信号 eclipse 热榜 Cursor TrueLicense ollama下载加速 asp.net大文件上传 asp.net大文件上传下载 asp.net大文件上传源码 ASP.NET断点续传 visual studio yolov8 ROS2 豆瓣 追剧助手 迅雷 android studio web3.py unity3d 银河麒麟 kylin v10 麒麟 v10 系统安全 王者荣耀 termux CentOS 系统 黑苹果 硬件 设备 PCI-Express ux 鲲鹏 昇腾 npu springboot 设置代理 实用教程 iperf3 带宽测试 sysctl.conf vm.nr_hugepages 权限 代码调试 ipdb adobe 微服务 systemctl ci/cd composer alias unalias 别名 AutoDL HCIE 数通 string模拟实现 深拷贝 浅拷贝 经典的string类问题 三个swap fd 文件描述符 飞牛nas fnos 压力测试 僵尸进程 log4j 中间件 iis MQTT协议 消息服务器 代码 n8n 工作流 视觉检测 DevOps 软件交付 数据驱动 应用场景 mysql离线安装 ubuntu22.04 mysql8.0 db Ubuntu 22.04 MySql 算家云 算力租赁 大模型压力测试 EvalScope minicom 串口调试工具 diskgenius 直播推流 sqlite3 rsyslog 机柜 1U 2U ArkUI ArkTS 移动端开发 qps 高并发 .net 远程看看 远程协助 mq rocketmq ROS 自动驾驶 Redis Desktop 国产操作系统 虚拟机安装 kind flash-attention 5G 3GPP 卫星通信 chatbox ansible r语言 云耀服务器 npm 正则表达式 Mac软件 React Next.js 开源框架 物联网开发 MQTT DigitalOcean GPU服务器购买 GPU服务器哪里有 GPU服务器 sqlserver Linux的权限 雨云 NPS html5 css less 监控k8s 监控kubernetes harmonyOS面试题 统信UOS bonding 链路聚合 交互 宝塔面板无法访问 IPMI 大屏端 Docker Hub docker pull 镜像源 daemon.json shell脚本免交互 expect linux免交互 GameFramework HybridCLR Unity编辑器扩展 自动化工具 GCC aarch64 编译安装 HPC 爬虫 数据集 消息队列 zip unzip windows日志 ebpf uprobe 无人机 AD域 智能体 autogen openai coze 域名服务 DHCP 符号链接 配置 图文教程 VMware虚拟机 macOS系统安装教程 macOS最新版 虚拟机安装macOS Sequoia WebVM 基础环境 自动化运维 流水线 脚本式流水线 ufw visualstudio cuda CLion c/c++ 串口 Windsurf c 磁盘监控 CUPS 打印机 Qt5 ESXi netty Qualcomm WoS Python QNN AppBuilder go armbian u-boot linux上传下载 JDK LInux Windows MS Materials file server http server web server Samba NAS 模拟实现 状态管理的 UDP 服务器 Arduino RTOS WSL win11 无法解析服务器的名称或地址 nohup 异步执行 进程程序替换 execl函数 execv函数 execvp函数 execvpe函数 putenv函数 gitea ftp okhttp 硅基流动 ChatBox top Linux top top命令详解 top命令重点 top常用参数 文件系统 路径解析 gradle RAGFLOW docker run 数据卷挂载 交互模式 镜像下载 freebsd ros1 Noetic 20.04 apt 安装 自动化编程 openwrt 加解密 Yakit yaklang threejs 3D jdk ssrf 失效的访问控制 嵌入式系统开发 图像处理 执法记录仪 智能安全帽 smarteye vscode1.86 1.86版本 ssh远程连接 监控 UOS 统信操作系统 LLM Web APP Streamlit ros2 moveit 机器人运动 mybatis 搜索引擎 全文检索 图搜索算法 xrdp mongodb 环境配置 DIFY HP Anyware 通信工程 毕业 Mermaid 可视化图表 自动化生成 llama3 Chatglm 开源大模型 iBMC UltraISO wireshark 显示过滤器 ICMP Wireshark安装 copilot linux环境变量 环境迁移 实战案例 export import save load 迁移镜像 rime bcompare Beyond Compare 高德地图 鸿蒙接入高德地图 HarmonyOS5.0 Cookie 低代码 拓扑图 内网渗透 靶机渗透 IO openstack Xen KVM Docker Compose 服务器扩容没有扩容成功 匿名管道 命名管道 智能手机 矩阵 一切皆文件 隐藏文件 隐藏目录 管理器 通配符 nac 802.1 portal linux安装配置 kali 共享文件夹 playbook 剧本 客户端与服务端通信 tar TRAE 键盘 视频平台 录像 视频转发 性能测试 视频流 存储 windwos防火墙 defender防火墙 win防火墙白名单 防火墙白名单效果 防火墙只允许指定应用上网 防火墙允许指定上网其它禁止 中兴光猫 换光猫 网络桥接 自己换光猫 clickhouse 工业4.0 KingBase 博客 fpga开发 磁盘清理 nvm 鸿蒙系统 selete v10 软件 框架搭建 yaml Ultralytics 可视化 数据挖掘 弹性计算 云服务器 裸金属服务器 弹性裸金属服务器 retry 重试机制 tcpdump Kylin-Server 服务器安装 大模型应用 Termux googlecloud 常用命令 文本命令 目录命令 网卡的名称修改 eth0 ens33 Web服务器 多线程下载工具 网络编程 PYTHON SSH 密钥生成 SSH 公钥 私钥 生成 mock mock server 模拟服务器 mock服务器 Postman内置变量 Postman随机数据 x64 SIGSEGV SSE xmm0 frp 迁移指南 腾讯云大模型知识引擎 网络用户购物行为分析可视化平台 大数据毕业设计 SWAT 配置文件 服务管理 网络共享 docker搭建pg docker搭建pgsql pg授权 postgresql使用 postgresql搭建 IIS服务器 IIS性能 日志监控 云原生开发 K8S k8s管理系统 服务器配置 Linux的基础指令 P2P HDLC 软链接 硬链接 autoware 开发 高效I/O ArkTs docker搭建nacos详解 docker部署nacos docker安装nacos 腾讯云搭建nacos centos7搭建nacos Invalid Host allowedHosts 毕设 Docker Kubernetes YOLOv12 NVML nvidia-smi 客户端 kamailio sip VoIP 大数据平台 burpsuite 安全工具 mac安全工具 burp安装教程 渗透工具 webstorm ubuntu24.04.1 宠物 毕业设计 免费学习 宠物领养 宠物平台 CosyVoice ping++ 强制清理 强制删除 mac废纸篓 firewalld Chatbox 功能测试 自动化测试 samba xml su sudo excel 在线预览 xlsx xls文件 在浏览器直接打开解析xls表格 前端实现vue3打开excel 文件地址url或接口文档流二进 程序员创富 seatunnel PX4 MAVROS 四旋翼无人机 dns AI写作 glibc 云服务 MCP server C/S ragflow 源码启动 计算机系统 工具 Portainer搭建 Portainer使用 Portainer使用详解 Portainer详解 Portainer portainer next.js 部署next.js 远程 执行 sshpass 操作 H3C vmamba cudnn anaconda 移动云 安防软件 springcloud Reactor 设计模式 个人博客 kotlin iphone arkUI 网易邮箱大师 Ubuntu共享文件夹 共享目录 Linux共享文件夹 信号 内核 OpenManus firefox 版本 显示器 curl wget VMware Tools vmware tools安装 vmwaretools安装步骤 vmwaretools安装失败 vmware tool安装步骤 vm tools安装步骤 vm tools安装后不能拖 vmware tools安装步骤 miniapp 真机调试 调试 debug 断点 网络API请求调试方法 HiCar CarLife+ CarPlay QT RK3588 安装MySQL 浏览器自动化 安全威胁分析 云桌面 微软 AD域控 证书服务器 计算机 系统开发 binder 车载系统 framework 源码环境 xpath定位元素 bat 内网环境 本地部署AI大模型 Claude Desktop Claude MCP Windows Cli MCP 多产物 源代码 图形化界面 黑客技术 跨域 Reactor反应堆 URL Open WebUI api web pyqt HarmonyOS OpenHarmony 小游戏 五子棋 RoboVLM 通用机器人策略 VLA设计哲学 vlm fot robot 视觉语言动作模型 具身智能 sdkman 管道 华为OD 华为OD机试真题 可以组成网络的服务器 wps cpp-httplib 网工 opensearch helm 服务器主板 AI芯片 图形渲染 MI300x 群晖 WebRTC linux 命令 sed 命令 网络文件系统 游戏服务器 TrinityCore 魔兽世界 zerotier nvidia k8s部署 MySQL8.0 高可用集群(1主2从) 相机 聊天服务器 套接字 Socket 自动化任务管理 职场和发展 CentOS Stream 命令行 基础入门 vasp安装 单例模式 prompt easyui langchain perl SSH 服务 SSH Server OpenSSH Server 蓝桥杯 蓝耘科技 元生代平台工作流 ComfyUI Docker快速入门 桌面环境 混合开发 环境安装 code-server SVN Server tortoise svn 文件分享 pppoe radius yum源切换 更换国内yum源 音乐服务器 Navidrome 音流 雨云服务器 token sas stable diffusion 能力提升 面试宝典 技术 IT信息化 源码 推荐算法 webgl Xinference RAGFlow 服务器部署ai模型 信息可视化 echarts 高效日志打印 串口通信日志 服务器日志 系统状态监控日志 异常记录日志 考研 在线office 网页设计 远程过程调用 Windows环境 gaussdb 京东云 玩游戏 saltstack 金仓数据库 2025 征文 数据库平替用金仓 相差8小时 UTC 时间 文心一言 银河麒麟操作系统 国产化 risc-v 安全架构 数据可视化 ELF加载 minio 免费域名 域名解析 三级等保 服务器审计日志备份 laravel docker命令大全 其他 DocFlow chrome 浏览器下载 chrome 下载安装 谷歌浏览器下载 beautifulsoup bootstrap 服务器繁忙 VM搭建win2012 win2012应急响应靶机搭建 攻击者获取服务器权限 上传wakaung病毒 应急响应并溯源 挖矿病毒处置 应急响应综合性靶场 EMQX 通信协议 rclone AList fnOS openjdk macbook 边缘计算 联想开天P90Z装win10 向日葵 宕机切换 服务器宕机 宝塔 飞牛NAS 飞牛OS MacBook Pro 孤岛惊魂4 终端工具 远程工具 cnn 邮件APP 免费软件 小番茄C盘清理 便捷易用C盘清理工具 小番茄C盘清理的优势尽显何处? 教你深度体验小番茄C盘清理 C盘变红?!不知所措? C盘瘦身后电脑会发生什么变化? 大文件秒传跨域报错cors Ubuntu Server Ubuntu 22.04.5 RTMP 应用层 ShapeFile GeoJSON Nginx 影刀 #影刀RPA# DOIT 四博智联 强化学习 实习 IMM 数据库架构 数据管理 数据治理 数据编织 数据虚拟化 Xshell 数码 联机 僵尸毁灭工程 游戏联机 开服 信号处理 idm 大模型面经 大模型学习 safari AnythingLLM AnythingLLM安装 IP 地址 MobaXterm 文件传输 历史版本 下载 单一职责原则 etl java-zookeeper Linux24.04 deepin Minecraft opcua opcda KEPServer安装 thingsboard DenseNet 笔灵AI AI工具 iDRAC R720xd powerbi kvm qemu libvirt rpa 树莓派 VNC AI作画 可信计算技术 密码学 LVM lvresize 磁盘扩容 pvcreate 自定义客户端 SAS dba can 线程池 XFS xfs文件系统损坏 I_O error 考试 make命令 makefile文件 ftp服务 文件上传 电视剧收视率分析与可视化平台 日志分析 系统取证 uv 智能电视 ubuntu24 vivado24 FunASR ASR 镜像 蓝桥杯C++组 Playwright 用户缓冲区 ssh远程登录 支付 微信支付 开放平台 virtualbox Wi-Fi SenseVoice 输入法 Spring Security k8s集群资源管理 EasyConnect env 变量 论文阅读 AimRT cocoapods 流式接口 css3 esp32 蓝牙 Tabs组件 TabContent TabBar TabsController 导航页签栏 滚动导航栏 Attention NLP lsb_release /etc/issue /proc/version uname -r 查看ubuntu版本 技能大赛 scikit-learn jetty undertow SysBench 基准测试 服务器时间 Kali 渗透 mamba Vmamba 软件卸载 系统清理 telnet 远程登录 桥接模式 windows虚拟机 虚拟机联网 kernel MNN Qwen 免密 登录 公钥 私钥 SSL证书 dity make eNSP 网络规划 VLAN 企业网络 es6 qt6.3 g726 深度优先 图论 并集查找 换根法 树上倍增 RAG 检索增强生成 文档解析 大模型垂直应用 语音识别 llama.cpp k8s资源监控 annotations自动化 自动化监控 监控service 监控jvm Linux Vim 宝塔面板 医疗APP开发 app开发 searxng Dell HPE 联想 浪潮 trea idea Docker引擎已经停止 Docker无法使用 WSL进度一直是0 镜像加速地址 ECT转485串口服务器 ECT转Modbus485协议 ECT转Modbus串口服务器 python3.11 ajax 模拟器 产测工具框架 IMX6ULL 管理框架 scapy 测试用例 asp.net上传文件夹 asp.net上传大文件 .net core断点续传 .net mvc断点续传 dash 聊天室 localhost firewall cfssl 思科模拟器 Cisco Typore 算力 健康医疗 互联网医院 微信公众平台 VPN wireguard muduo X11 Xming ollama api ollama外网访问 飞牛 HistoryServer Spark YARN jobhistory photoshop Linux find grep 银河麒麟服务器操作系统 系统激活 uni-file-picker 拍摄从相册选择 uni.uploadFile H5上传图片 微信小程序上传图片 RAID RAID技术 磁盘 Obsidian Dataview 社交电子 代理服务器 多端开发 智慧分发 应用生态 鸿蒙OS AP配网 AK配网 小程序AP配网和AK配网教程 WIFI设备配网小程序UDP开 SEO ceph 显示管理器 lightdm gdm 跨平台 AI代码编辑器 网络攻击模型 Linux awk awk函数 awk结构 awk内置变量 awk参数 awk脚本 awk详解 lb 协议 vscode 1.86 网站搭建 serv00 服务器部署 本地拉取打包 直流充电桩 充电桩 微信开放平台 微信公众号配置 换源 国内源 Debian 单元测试 7-zip Linux权限 权限命令 特殊权限 交叉编译 banner Webserver 异步 gpt-3 CNNs 图像分类 conda配置 conda镜像源 NAT转发 NAT Server pyicu 显卡驱动 lighttpd安装 Ubuntu配置 Windows安装 服务器优化 容器技术 crosstool-ng rustdesk 毕昇JDK 序列化反序列化 jina 数据采集 Crawlee 致远OA OA服务器 服务器磁盘扩容 MAC SecureCRT IPv4 子网掩码 公网IP 私有IP openssl 游戏机 netlink libnl3 wsl2 金融 Netty 即时通信 NIO 稳定性 看门狗 tensorflow trae nuxt3 大版本升 升级Ubuntu系统 Isaac Sim 虚拟仿真 计算机科学与技术 底层实现 半虚拟化 硬件虚拟化 Hypervisor Web应用服务器 ruby 行情服务器 股票交易 速度慢 切换 股票量化接口 股票API接口 线程 micropython mqtt Ardupilot vpn 分析解读 sublime text3 服务器管理 配置教程 网站管理 双系统 GRUB引导 Linux技巧 业界资讯 Office 模拟退火算法 springboot远程调试 java项目远程debug docker远程debug java项目远程调试 springboot远程 田俊楠 mosquitto 工具分享 联网 easyconnect 代理 initramfs Linux内核 Grub 代码托管服务 lio-sam SLAM Linux环境 csrf pyside6 界面 umeditor粘贴word ueditor粘贴word ueditor复制word ueditor上传word图片 ueditor导入word ueditor导入pdf ueditor导入ppt AISphereButler hibernate UDP UOS1070e W5500 OLED u8g2 TCP服务器 chfs ubuntu 16.04 小艺 Pura X 多进程 cmake FTP服务器 Carla 智能驾驶 Java Applet URL操作 服务器建立 Socket编程 网络文件读取 新盘添加 partedUtil ukui 麒麟kylinos openeuler fast nacos workflow linuxdeployqt 打包部署程序 appimagetool Pyppeteer 动静态库 图片增强 增强数据 华为证书 HarmonyOS认证 华为证书考试 网络穿透 办公自动化 pdf教程 火绒安全 VPS Nuxt.js rnn 恒源云 VR手套 数据手套 动捕手套 动捕数据手套 arcgis 小智 反向代理 上传视频至服务器代码 vue3批量上传多个视频并预览 如何实现将本地视频上传到网页 element plu视频上传 ant design vue vue3本地上传视频及预览移除 OD机试真题 服务器能耗统计 DeepSeek行业应用 Heroku 网站部署 Ark-TS语言 big data CORS uniapp 大模型训练/推理 推理问题 mindie cmos pyautogui ShenTong DBeaver ACL 流量控制 基本ACL 规则配置 开机自启动 智能音箱 智能家居 IPMITOOL BMC 硬件管理 空间 查错 av1 电视盒子 机顶盒ROM 魔百盒刷机 分子对接 autodock mgltools PDB PubChem sonoma 自动更新 Kylin OS XCC Lenovo Echarts图表 折线图 柱状图 异步动态数据 鸿蒙开发 可视化效果 neo4j 数据仓库 数据库开发 繁忙 解决办法 替代网站 汇总推荐 AI推理 CDN 重启 排查 系统重启 日志 原因 bot 端口测试 实时内核 nohup后台启动 keepalived ArcTS ArcUI GridItem wsgiref Web 服务器网关接口 服务网格 istio nfs js SSL 域名 skynet DeepSeek r1 cd 目录切换 grep 代码规范 chrome devtools chromedriver 服务器数据恢复 数据恢复 存储数据恢复 raid5数据恢复 磁盘阵列数据恢复 HarmonyOS NEXT 原生鸿蒙 embedding 高效远程协作 TrustViewer体验 跨设备操作便利 智能远程控制 sudo原理 su切换 自学笔记 小米 澎湃OS Android apt Node-Red 编程工具 流编程 iTerm2 终端 java-rocketmq 机架式服务器 1U工控机 国产工控机 Jellyfin 同步 备份 建站 LORA 阻塞队列 生产者消费者模型 服务器崩坏原因 生活 deep learning NLP模型 安全漏洞 信息安全 端口 查看 ss pthread h.264 移动开发 支持向量机 语法 项目部署到linux服务器 项目部署过程 超融合 iNode Macos 内存管理 word 文件存储服务器组件 open Euler dde react native post.io 企业邮箱 搭建邮箱 显卡驱动持久化 GPU持久化 捆绑 链接 谷歌浏览器 youtube google gmail docker部署翻译组件 docker部署deepl docker搭建deepl java对接deepl 翻译组件使用 视频监控 网站 kubeless prometheus数据采集 prometheus数据模型 prometheus特点 企业网络规划 华为eNSP shard 动态规划 Qwen3 qwen3 32b vllm xfce GeneCards OMIM TTD regedit 开机启动 chromium dpi NFC 近场通讯 智能门锁 client-go k8s二次开发 whistle 崖山数据库 YashanDB SystemV IPv6 IPv6测试 IPv6测速 IPv6检测 IPv6查询 搭建个人相关服务器 商用密码产品体系 Radius MDK 嵌入式开发工具 论文笔记 玩机技巧 软件分享 软件图标 swoole OpenCore linux子系统 忘记密码 阿里云ECS qt5 客户端开发 无桌面 做raid 装系统 多个客户端访问 IO多路复用 TCP相关API 机械臂 rtc IP配置 netplan 架构与原理 智能体开发 怎么卸载MySQL MySQL怎么卸载干净 MySQL卸载重新安装教程 MySQL5.7卸载 Linux卸载MySQL8.0 如何卸载MySQL教程 MySQL卸载与安装 移动魔百盒 IDEA docker部署Python 录音麦克风权限判断检测 录音功能 录音文件mp3播放 小程序实现录音及播放功能 RecorderManager 解决录音报错播放没声音问题 gunicorn tailscale derp derper 中转 autodl Python基础 Python教程 Python技巧 triton 模型分析 webpack 线性代数 电商平台 C++软件实战问题排查经验分享 0xfeeefeee 0xcdcdcdcd 动态库加载失败 程序启动失败 程序运行权限 标准用户权限与管理员权限 Unity Dedicated Server Host Client 无头主机 Claude 欧标 OCPP 软件开发 信任链 lua vue-i18n 国际化多语言 vue2中英文切换详细教程 如何动态加载i18n语言包 把语言json放到服务器调用 前端调用api获取语言配置文件 llamafactory 微调 docker search VGG网络 卷积层 池化层 锁屏不生效 figma 音乐库 System V共享内存 进程通信 deepseek r1 源代码管理 增强现实 沉浸式体验 技术实现 案例分析 AR 僵尸世界大战 游戏服务器搭建 es GoogLeNet IPv4/IPv6双栈 双栈技术 网路规划设计 ensp综合实验 IPv4过渡IPv6 IPv4与IPv6 midjourney mac设置host iventoy VmWare OpenEuler 信创 信创终端 中科方德 开源软件 sentinel 问题解决 对比 meld DiffMerge 佛山戴尔服务器维修 佛山三水服务器维修 GIS 遥感 WebGIS USB网络共享 wordpress 无法访问wordpess后台 打开网站页面错乱 linux宝塔面板 wordpress更换服务器 c/s 用户管理 虚幻引擎 华为机试 浏览器开发 AI浏览器 干货分享 黑客工具 密码爆破 技术共享 我的世界 我的世界联机 navicat Charles NVM Node Yarn PM2 Sealos 打不开xxx软件 无法检查其是否包含恶意软件 glm4 arkTs 我的世界服务器搭建 计算机学习路线 编程语言选择 烟花代码 烟花 元旦 性能调优 安全代理 本地知识库部署 DeepSeek R1 模型 Helm k8s集群 massa sui aptos sei 进程间通信 流量运营 ISO镜像作为本地源 Erlang OTP gen_server 热代码交换 事务语义 minecraft 备份SQL Server数据库 数据库备份 傲梅企业备份网络版 Maxkb RAG技术 本地知识库 网络建设与运维 网络搭建 神州数码 神州数码云平台 云平台 网页服务器 web服务器 GPU训练 dns是什么 如何设置电脑dns dns应该如何设置 xss PPI String Cytoscape CytoHubba Ubuntu 24.04.1 轻量级服务器 材料工程 hugo kerberos AI员工 抗锯齿 postgres Dify重启后重新初始化 裸机装机 linux磁盘分区 裸机安装linux 裸机安装ubuntu 裸机安装kali 裸机 多路转接 知识图谱 券商 股票交易接口api 类型 特点 deekseek Zoertier 内网组网 AI agent TCP协议 状态模式 授时服务 北斗授时 可执行程序 嵌入式Linux IPC Logstash 日志采集 EMUI 回退 降级 Qwen2.5-VL VS Code NVIDIA hexo ecm bpm 负载测试 小智AI服务端 xiaozhi TTS AD 域管理 nosql dock 加速 mysql安装报错 windows拒绝安装 游戏开发 proxy模式 OpenSSH 磁盘镜像 服务器镜像 服务器实时复制 实时文件备份 web3 区块链项目 rsync 星河版 Apache Beam 批流统一 案例展示 数据分区 容错机制 vsxsrv 备选 调用 示例 银河麒麟桌面操作系统 软考设计师 中级设计师 SQL 软件设计师 Ubuntu 24 常用命令 Ubuntu 24 Ubuntu vi 异常处理 win服务器架设 windows server HTTP 服务器控制 ESP32 DeepSeek EVE-NG 弹性服务器 查询数据库服务IP地址 SQL Server 分布式训练 卸载 列表 查看显卡进程 fuser ArtTS IO模型 comfyui comfyui教程 seleium 教育电商 智能合约 哈希算法 子系统 sqlite pgpool qt项目 qt项目实战 qt教程 MacOS 国标28181 监控接入 语音广播 流程 SIP SDP 根服务器 WINCC 电路仿真 multisim 硬件工程师 硬件工程师学习 电路图 电路分析 仪器仪表 项目部署 ubuntu安装 linux入门小白 银河麒麟高级服务器 外接硬盘 Kylin 服务器ssl异常解决 软负载 GRE 需求分析 规格说明书 deepseak 豆包 KIMI 腾讯元宝 deployment daemonset statefulset cronjob 内网服务器 内网代理 内网通信 mm-wiki搭建 linux搭建mm-wiki mm-wiki搭建与使用 mm-wiki使用 mm-wiki详解 Alist mount 挂载 网盘 冯诺依曼体系 servlet ranger MySQL8.0 飞书 hosts g++ g++13 hosts文件管理工具 IMX317 MIPI H265 VCU resolv.conf WireGuard 异地组网 运维监控 可用性测试 集群管理 Headless Linux Trae叒更新了? xshell termius iterm2 程序化交易 量化交易 高频交易 端口聚合 windows11 wpf 达梦 DM8 hdc 钉钉 mcp服务器 client close zookeeper flink csrutil mac恢复模式进入方法 恢复模式 Anolis nginx安装 linux插件下载 抓包工具 极限编程 合成模型 扩散模型 图像生成 junit 配置原理 分布式账本 共识算法 docker desktop image ai工具 ldap paddle 接口优化 sequoiaDB tidb Bug解决 Qt platform OpenCV 华为鸿蒙系统 ArkTS语言 Component 生命周期 条件渲染 Image图片组件 静态IP AzureDataStudio 设备树 火山引擎 解决方案 macOS 框架 yashandb Putty 花生壳 RK3568 李心怡 EMC存储 NetApp存储 fiddler 充电桩平台 充电桩开源平台 cpolar caddy mvc yum换源 qwen2vl 线程同步 线程互斥 条件变量 电脑桌面出现linux图标 电脑桌面linux图标删除不了 电脑桌面Liunx图标删不掉 linux图标删不掉 百度云 矩池云 数据下载 数据传输 免费 solr 粘包问题 Unity插件 数字证书 签署证书 easyTier 组网 node brew 搜狗输入法 中文输入法 带外管理 pycharm安装 UFW cocos2d 3dcoat 服务器正确解析请求体 输入系统 金仓数据库概述 金仓数据库的产品优化提案 MVS 海康威视相机 DrissionPage 计算虚拟化 弹性裸金属 物理地址 页表 虚拟地址 watchtower 桌面快捷方式 元服务 应用上架 PyQt PySide6 飞腾处理器 solidworks安装 lrzsz 风扇控制软件 finebi HAProxy ABAP authing 海康 vm Masshunter 质谱采集分析软件 使用教程 科研软件 存储维护 AI Agent 字节智能运维 大模型推理 网络原理 创业创新 北亚数据恢复 oracle数据恢复 西门子PLC 通讯 Python 视频爬取教程 Python 视频爬取 Python 视频教程 Qt QModbus RDP 大大通 第三代半导体 碳化硅 mybase ardunio BLE 网络库 logstash Linux的基础开发工具 pythonai PlaywrightMCP MinIO 鸿蒙NEXT 向量数据库 安装部署 milvus安装 命令模式 欧拉系统 设计规范 archlinux kde plasma vCenter服务器 ESXi主机 监控与管理 故障排除 日志记录 零售 VM虚拟机 homeassistant 脚本 跨域请求 anythingllm open-webui docker国内镜像 android-studio element-ui 上传视频并预览视频 vue上传本地视频及进度条功能 vue2选择视频上传到服务器 upload上传视频组件插件 批量上传视频 限制单个上传视频 Github加速 Mac上Github加速 Chrome浏览器插件 知行EDI 电子数据交换 知行之桥 EDI STL win向maOS迁移数据 三次握手 gru d3d12 webview AWS CAD瓦片化 栅格瓦片 矢量瓦片 Web可视化 DWG解析 金字塔模型 华为昇腾910b3 deepseek-v3 ktransformers harmonyosnext finalsheel 提示词 web开发 #STC8 #STM32 ECS服务器 PTrade QMT 量化股票 qtcreator 进程池实现 Bandizip Mac解压 Mac压缩 压缩菜单 SoC Metastore Catalog gitee go miniconda swift 接口返回 fpga pxe mapreduce 定义 核心特点 优缺点 适用场景 java毕业设计 微信小程序医院预约挂号 医院预约 医院预约挂号 小程序挂号 GRANT REVOKE 工厂方法模式 匿名FTP 邮件传输代理 SSL支持 chroot监狱技术 bert OS 红黑树封装map和set 智能问答 Spring AI Milvus 导航栏 bigdata pyscenic 生信教程 数据库管理 OpenManage 概率论 mcp协议 go-zero Async注解 ubantu 漏洞报告生成 mujoco 事件驱动 AI提示词优化 模板 泛型编程 动态库 GCC编译器 -fPIC -shared aac notepad++ OpenGL