Linux相关概念和易错知识点(28)(线程控制、Linux下线程的底层)

R语言 | 关联规则 关联规则(AssociationRules)是无监督的机器学习方法,用于知识发现,而非预测。 关联规则的学习器(learner)无需事先对训练数据进行打标签,因为无监督学习没有训练这个步骤。缺点是很难对关联规则学习器进行模型评估,一般都可以通过肉眼观测结果是否合理。 关联规则主要用来发现Pattern,最经典的应用是购物篮分析,当然其他类似于购物篮交易数据的案例也可以应用关联规则进行模式发现。 阅读详情

目录

1.线程控制

(1)pthread和thread库

(2)线程的创建、等待和分离

①线程创建

②线程等待

③线程分离

④线程替换(不可行)

(3)线程的终止和取消

①线程终止

②线程取消

2.Linux下线程的底层

(1)线程ID

(2)资源划分和维护

①新线程栈的动态创建和销毁

②struct pthread的存储

③线性局部存储

④线性栈和mmap

(3)clone

(4)创建线程过程


1.线程控制

(1)pthread和thread库

线程的使用需要pthread库,这是用户级别的、通用的、基于 POSIX 标准的线程库。这个库提供C语言接口,并且具备跨平台性。很明显,它基于操作系统底层的线程机制实现并进一步封装,向上提供了统一的接口。

pthread库的实现是glibc的一部分,所以pthread库是Linux系统自带的线程库。但由于线程库可能不是项目中必需的,而且有可能和其它库引发冲突,因此Linux有可能不会默认链接pthread动态库。如果要使用线程,必须编译时选择-lpthread(不需要选择查找目录、头文件位置,系统能找到)。

除此之外,C++11引入了<thread>,这是C++风格的线程实现,同样具有跨平台性。Linux下使用<thread>需要链接原生线程库-lpthread

(2)线程的创建、等待和分离

①线程创建

当创建成功时返回0,失败时返回错误码。

参数我们不关注第二个,默认填nullptr。第一个参数pthread_t是线程的id,用于唯一标识线程;第三个是让线程线程执行的函数(回调函数),该函数返回值为void*,参数为void*;第四个参数是传入该回调函数的参数。

下面是简单的使用

我们可以看到引用线程库后,就算还没有创建线程,主程序的代码执行可被认为是主线程,可以获取它的唯一标识pthread_t。虽然Linux下都是LWP,但在C语言上封装后就出现了线程的概念。

我们可以认为,线程的存在就是为了执行某一个函数,主线程的存在就是为了执行main函数(执行main函数的线程就是主线程),当main函数结束后代表主线程执行完毕,进而表示整个进程的结束。其余线程也都可以这么理解,它们的生命周期从开始执行函数为始,结束执行函数为终。

主线程也可以新建一个线程并让它运行代码。当主线程结束运行后,程序退出。

但这里有个问题:万一主线程比创建的线程更早结束,就会导致创建的线程没有完成任务。那应该怎么保证主线程在最后退出呢?sleep?这太难控制了,而且效率很低。

②线程等待

成功返回0,错误返回错误码。

假如thread1调用该join函数,其第一个参数是thread2,这就意味着thread1会阻塞在这个函数直到thread2结束,第二个参数是thread1接收thread2的返回值所用。

除此之外,join之后线程的资源(如线程的退出状态、栈等)将会被释放。如果不及时join,这些未释放的资源会一直保留在系统中,直到整个进程结束,就像僵尸进程占用系统资源一样。

下面是对刚才代码的修改

这样主线程就会在等待的线程退出后才退出,并且能够获取到子线程的函数返回值。要特别注意子线程的返回值要求为void*,参数为void*,必须完全对应才能编译通过。如果不对子线程的返回值感兴趣,可以传nullptr作为join函数的第二个参数。

线程等待类似回收僵尸进程,只要join对应线程的id,就算该线程已经结束了,也都能获取到该线程的返回值,并且这个返回值需要通过堆区创建空间。对于所有线程来说,堆区是共享的,谁拿着堆的入口地址,谁就能访问空间。

在线程中虽然名义上进程地址空间被划分给了不同线程,但线程之间可以互相访问,只要有地址。

join成功了,代表此刻数据一定处理完了,这样就可以放心执行后面的代码了。

③线程分离

join有个坏处,就是join的线程必须等待被join的线程执行完后才会继续执行代码,但有的时候线程要做自己的事情,比如主线程承担分配任务的使命。想要不阻塞在join函数,则要将目标线程设置为分离状态,即detach状态。

线程分离出对应的线程后join会失败,join的返回值不是0。之后该线程执行完毕后会自动释放空间,而不需要join。同时,除非极端情况,就算主线程走完了,分离的线程还能执行,进程会在分离线程执行完毕后回收,但要注意一些共享资源的访问问题,有可能分离线程会去访问已被释放的线程的资源。

④线程替换(不可行)

线程能不能程序替换?不可以。只有进程可以程序替换,进程替换会把代码数据全部替换,线程没这个能力,它不能对进程做出操作。但我们可以在线程里面fork,进一步实现程序替换。

(3)线程的终止和取消

①线程终止

主线程return表示整个进程结束,此刻所有线程也都会退出,不管是否执行完毕。新线程return表示该线程退出,其余线程不受影响。

但注意,任何线程在任何地方调用exit均表示进程退出,即所有线程都会终止。相对的,例如pthread_exit((void*)10)这种exit退出方式就相对温和,只是结束相应调用的线程并且交出返回值给等待它的线程,pthread_exit和return等价,平时就用return就好。

②线程取消

线程可以被取消,主线程可以在任何时候取消任何线程的执行,这是被动式退出,不由其它线程决定。

其参数就是被取消的线程id。取消成功返回0,失败返回错误码。

取消一个线程的前提是目标线程已经被启动了,即在create函数之后。但一般不推荐取消线程,因为对应线程的状态不清楚,处理的数据状态也不清楚,因此盲目取消会导致程序混乱。

注意,取消的线程依旧需要被join,这就相当于回收僵尸进程,被取消的线程会返回-1,该返回值其实是宏PTHREAD_CANCELED。

2.Linux下线程的底层

(1)线程ID

线程id(即pthread_t)是一个地址,这个地址本身也有唯一性。

pthread.so库加载到内存中,这个库被经过页表映射到共享区。创建线程的其实是在调用库方法,由于进程内的所有线程共享进程地址空间,因此它们都能执行库的其它方法。

Linux中严格上只有LWP,但用户要用线程,想要用线程接口(create、join等),想要线程的属性(线程的id、优先级、状态、栈大小等),怎么办呢?

用户不关心LWP的属性,他们只关心线程的属性,这就是pthread.so库单独提供的。pthread.so库要维护相关属性,要存储相应的结构体来存储相关属性。这和glibc维护FILE一样,所有文件的属性信息都是由glibc库来进行维护的,当要对某个文件进行操作时,都是调用glibc的函数对其维护下的结构体进行修改。注意,这些结构体由动态库维护(调用动态库的函数对这些结构体进行修改),其结构体的物理内存也在库的里面,下面的pthread.so也是如此。

pthread.so会维护该进程下所有线程的属性。对于一个线程来说,其属性的集合就相当于结构体TCB,线程的id、优先级、状态、栈大小、返回值都在里面保存着的。一般来说,所有线程的属性的结构体整体形成的是一个数组。既然线程属性保存在一个物理内存中,那么理所应当的,其线程id的值就是相应结构体的地址,也就是pthread_t类型,pthread_create的第一个参数获取的值。

我们后续就可以用TCB来描述个由pthread.so库维护的结构体,毕竟它是上层封装,和LWP有区别了。这个时候我们就能理解,为什么对线程操作,都需要传入线程id,本质上对线程的管理都是对维护线程属性的结构体的操作。

(2)资源划分和维护

①新线程栈的动态创建和销毁

当主线程开始执行时,它就会保证自己有一个栈,即主线程栈,这个栈通常是在程序启动时由操作系统预先分配好的,它的大小一般由操作系统和编译环境决定。当程序运行完毕后,这个栈才会销毁。主线程栈的位置就在进程地址空间的栈区。

除此之外,其它所有子线程的栈都是动态创建的。为什么叫动态创建?只有当调用create函数,创建子线程时,这个栈才会被创建。同理,只要这个线程被join(或分离线程执行完毕),那么它的栈就会被销毁。因此,子线程栈的动态性在于它可以在程序运行途中创建和销毁,而主线程栈是一进程序就创建,结束才销毁。

②struct pthread的存储

struct pthread(TCB)被pthread.so库维护,就意味着从物理内存的角度上看,一个系统的所有线程的属性都存在pthread.so库的内部。当pthread.so被映射到虚拟内存中后,就存放有所有线程的TCB,当我们对某个线程进行操作时,都是对这个库中某个线程属性的修改。同理,FILE也是如此,glibc这个库中维护了整个系统所有的FILE结构体。

struct pthread里面主要含有两个属性,线性局部存储和线性栈的属性,除此之外还一定包括LWP的属性task_struct,就像FILE里面一定有文件描述符fd。

③线性局部存储

对于代码全局区的变量来说,每个线程都可修改它们的值,并且修改之后其它线程都能同时感受到这个变化。但有的时候我们不希望这样,这就要用到编译性关键字__thread,用它修饰变量后(如__thread int a = 10),每个线程都会独立地得到该数据,不会互相影响,这就叫线程局部存储。

线性局部存储的数据是线程私有的数据,错误码error的私有性质就是用线程局部存储实现的,这样就能正确标识每个线程的错误了,而不会相互影响。

__thread只能修饰内置类型,结构体不行。局部存储的变量会单独存到TCB的一个属性里,因此对一些高频访问效率较高,不用到线性栈里面去找。

④线性栈和mmap

后面会提到的mmap区域就是指的共享区。

前面已经说过主线程栈在栈区,可以动态增长。新线程的栈在共享区,也就是mmap区域被开辟,并且它有一个最大容量值,即最多开辟8MB(随系统,用完就没了,不像主线程栈可以向下增长),这些栈的起始地址、容量信息都会被存入线程的属性结构体TCB中。我们还应知道,mmap还是一个系统调用,充当开辟空间的作用,存在mmap区域的新线程栈就是由mmap系统调用申请的。malloc的底层也是mmap。

线程的栈区原则上是独立的,每个线程各自用各自的,但实际上是共享的,因为没有权限限制,加上所有线程看到的都是同一个进程地址空间,所以只要拿到别的线程的栈空间地址,就能访问。堆区、数据段同理。

到此我们可以总结一下,线性栈和线性局部存储都由struct pthread(TCB)管理,都存在共享区,其中栈是由mmap系统调用单独在mmap(共享)区开辟空间。这些TCB最终都由pthread.so维护,事实上,pthread.so维护了整个系统的线程属性struct pthread。

(3)clone

这个系统调用的几个参数需要声明

fn是函数入口地址,arg是函数的参数包,轻量级进程就会根据这两个参数执行对应函数。stack是LWP占用的独立的栈的地址,LWP会在指定栈保存变量。flags是标志位,区分不同的操作。

我们从参数就能理解,调用pthread_create就需要先后调用mmap、clone。先是由mmap开辟空间,更新TCB,再由clone来执行。在系统层这是新建了一个LWP并执行代码,而在用户层看来,这就是创建了一个新的线程。

我们还可以了解一下clone,它也是vfork和fork的底层调用,clone是更底层的系统调用,我们用户无法直接调用。

(4)创建线程过程

当程序开始运行时,主线程栈在栈区创建并向下增长。当要创建新线程时,会创建struct pthread,会调用系统调用mmap在mmap(共享)区域动态创建栈且栈的大小不可增长,之后由pthread.so维护的线程的属性struct pthread会进行更新,之后通过clone系统调用创建好一个LWP,并将pthread.so维护的struct pthread的地址返回给用户作为id。之后用户的所有操作本质都是调用pthread.so的函数对struct pthread的属性进行修改。

tun虚拟网卡没有安装解决之道 目录 描述 过程 一、sstap 会提示你虚拟网卡没有安装; 二、还有会是这样的:病毒和威胁防护中无法启动;引擎不可以用等等,以及会显示需要IT管理员去处理;等等报错的信息; 系统升级地方会显示:Windows Defender Antivirus 的安全智能更新 - KB2267602 (版本 1.305.2147.0) - 错误 0xc8000442 Windows Defender报错; 三、还有就是会出现你搜自己家的WIFI搜不到,电脑端都不会显示WIFI名字; 解决方... 阅读详情

相关推荐

对工时评估的思考:当AI颠覆了coding效率

在AI时代,“人日”作为软件工时度量衡已然失效。开发的价值核心正从“编码实现”转向“深度思考”与“精准沟通”——即如何将人类的模糊需求精确地传达给AI。当最有价值的“思想工作”变得最难量化时,传统的项目管理范式随之动摇。我们必须超越对过程的算计,开始探索衡量思想、沟通与业务价值的新标尺。

架构师刘7的博客 1017

【Linux】解锁线程基本概念和线程控制,步入多线程学习的大门

本文主要讲解了线程的基本概念和线程的控制相关知识

可涵的博客 3650

Android 16(API 36)状态栏颜色适配全攻略:从沉浸式到自定义背景

本文全面解析Android 16(API 36)强制沉浸式状态栏带来的适配挑战,提供了从FrameLayout包裹法到动态修改Window背景色的核心解决方案。针对传统状态栏颜色设置失效问题,详细阐述了如何利用WindowInsets API实现自定义背景,并涵盖全屏模式、Fragment切换及第三方ROM兼容性等实战场景,为开发者提供一站式适配指南。

weixin_29227425的博客 594

Linux多线程【线程控制】

线程是进程内部的一个执行流,作为CPU运行的基本单位,对于线程的合理控制与任务的执行效率息息相关,因此掌握线程基本操作(线程控制)是很有必要的

北 海的博客 2371

MATLAB安装

Matlab 2016a 安装包及破解教程百度云分享链接: 链接:https://pan.baidu.com/s/1i6BgD8p       密码:17gg   Matlab安装教程: 1、下载文件,得到R2016a_win64.part1.rar、R2016a_win64.part2.rar两部分安装文件和Matlab 2016a Win64 Crack破解文件。 2、解压安装文件(解...

qq_36734094的博客 17万+

手教你如何使用Simulink和相关工具箱模拟和实现无人机的多机接力信号中继的建模

扩大通信范围:单架无人机的通信能力有限,通过多机接力的方式可以显著扩展通信覆盖范围。提高可靠性:当某架无人机出现故障或电量不足时,其他无人机可以接管其任务,确保通信不中断。适应复杂环境:在山区、森林等复杂地形中,多机接力可以绕过障碍物,建立稳定的通信链路。使用Communications Toolbox中的模块来模拟无线通信链路。例如,可以使用模块模拟噪声信道,或者使用模块计算信号衰减。matlab深色版本。

MHD0815的博客 766

Linux(线程控制)

那我们现在思考一个问题,为什么thread_join的第二个返回值类型为void**,原因是当新线程退出时如果我们使用void类型,就返回不了(void它是没有大小的,OS根本不知道开辟多少空间给它),但是void*它是有大小的(32位为4字节,64位为8字节),为了能够成功传参这里采用的void**类型。第四个参数:我们给线程起的名字,这个参数就是我们传递给第三个函数指针的参数(回调函数),这个参数的类型为void*,意思是我们可以传递任意类型的参数。(4)等待线程是要等待线程退出,那要是线程不退出?

2401_87257864的博客 1265

分享一些基于微信小程序开发的小游戏源码

小游戏小程序是运行在微信生态系统(或其他支持小程序的平台)中的轻量级应用程序,专为休闲游戏体验而设计的小程序子类别,小游戏小程序通常体积较小,加载速度快,用户无需等待长时间即可开始游戏。用户无需下载或安装单独的游戏应用程序,即可通过小程序入口直接进入游戏,同时小游戏小程序通常包含排行榜、分享功能等社交元素,可以增强游戏体验并促进用户参与。game.js:游戏页面的逻辑文件,包含页面数据、生命周期函数、事件处理函数等。game.wxml:游戏页面的结构文件,定义了页面的布局和组件。

xinshangdian的博客 6224

【Linux篇章】线程操控术2:如何让代码如千军万马听你号令(精讲线程控制)

 本篇文章承接上篇关于线程概念;在此篇中;会讲到线程相关使用;比如创建,终止;等待;分离等相关函数接口;以及linux的线程底层是怎么实现的;那些接口函数是怎么封装的;线程id和轻量级进程之间关系;地址空间分布等等再到最后自己模拟实现简单版的线程封装;故本篇文章主要带大家了解linux线程如何使用;底层又是怎么样的;从而对线程有更加清晰地认识。

羑悻的博客. 3244

华为OD面试八股真题 - 操作系统 - 1

华为OD技术面,操作系统相关八股真题提供详细解答,这篇博客主要涉及于进程和线程相关知识点

qq_45776114的博客 1633

语音识别:PyAudio、SoundDevice、Vosk、openai-whisper、Argos-Translate、FunASR(Python)

本文介绍了Python中常用的音频处理库PyAudio,详细解析了其核心功能、核心概念和典型工作流程。PyAudio是一个跨平台的音频I/O库,支持音频录制、播放、实时流处理和低延迟操作。核心概念包括PyAudio类(管理音频设备和流)和Stream类(代表音频流)。文章重点讲解了open()方法的关键参数、回调函数的使用方式以及典型的工作流程,包括设备查询、流配置和资源释放。PyAudio适用于需要实时音频处理的场景,如语音识别和音频效果处理。

算法工程师 705

Linux线程控制(28)

加油,我们继续,争取在五月份之前把 Linux 给干完虽然我也不知道行不行~线程是进程内部的一个执行流,作为 CPU 运行的基本单位,合理控制线程与任务的执行效率息息相关,因此掌握线程基本操作(线程控制)是很有必要的加油加油!!!

tan180°的博客 979

STM32F103C8T6驱动DHT11温湿度传感器程序

本程序基于 STM32F103C8T6 微控制器,通过 GPIO 模拟单总线通信时序实现对 DHT11 温湿度传感器的数据采集,主机采用拉低数据线的方式触发传感器启动,并通过检测高电平持续时间区分逻辑“0”和“1”,依次读取湿度和温度的整数数据及校验位,经过校验后将有效结果存入缓存数组,实现对环境温湿度的周期性测量与处理。

加斯顿工程师的博客 200

[ Linux ] 线程控制(线程创建,等待,终止)

[ Linux ] 线程控制(线程创建,等待,终止)

优快云博客 1390

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Linux线程控制全解析

之前我们介绍了线程的基本概念,对线程有了一个初步的认识,这篇文章我们就来介绍一下线程控制相关话题。

qq_64443929的博客 884

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

Linux | 为什么要有线程?| 线程概念 | 线程控制 | 线程ID的实质

Linux线程概念介绍,线程控制讲解,线程相关知识点讲解

凡事都是靠练习 953

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Linux —— 线程控制(1)

3.4 pthread_join,是在线程库中等待线程的一个函数。

2302_80045891的博客 442

Linux--线程控制--线程相关函数--tid--0109 10

线程控制--线程相关函数--tid

qq_68741368的博客 870

Linux多线程

生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。在编写多线程的时候,有一种情况是十分常见的。的功能和内容实际上是差不多的,所以在Linux下线程直接复用了进程的PCB和调度代码,这样不仅对Linux的开发者来说省了很多事,而且对于后期的维护也非常简单的,只要进程没问题线程就一定没问题,使用起来也更加可靠,增强了代码的健壮性。

Merrill_Rosie的博客 982

【Linux系统】入门线程:线程介绍与线程控制

本文摘要: 线程是进程内部的执行流,Linux中实际为轻量级进程,通过pthread库实现线程管理。线程共享进程资源(地址空间、文件描述符等),但拥有独立栈帧、局部存储等特性。相比进程,线程创建和切换开销更小,但缺乏保护机制。文章详细介绍了线程控制相关API,包括创建(pthread_create)、终止(pthread_exit)、等待(pthread_join)和分离(pthread_detach)等操作。特别指出线程ID本质是地址空间中的内存地址,并解释了线程局部存储机制。

记录我一步步的学习历程,期待无限进步,向星举目! 1622

Linux线程(二): 线程控制之创建

阐述了线程相关核心概念与实现机制。首先从硬件层面解析了TLB(快表)的工作原理及其在地址转换中的加速作用,深入剖析了缺页中断的触发条件与处理流程。其次,对比了进程与线程的资源共享特性,重点讲解了POSIX线程库的使用方法,包括线程创建、属性设置等关键API。针对不同应用场景,文章详细区分了计算密集型和I/O密集型任务的特点及线程配置策略,并揭示了Linux线程的本质实为轻量级进程(LWP)。最后,通过实例代码演示了多线程编程实践,同时指出了线程过多可能导致的问题及优化方向。

原来是猿的成长足迹 349

Linux下线程相关知识总结

一、线程概念 1、什么是线程 (1)线程,有时被称为轻量级进程(Lightweight Process,LWP),是程序执行流的最小单元。一个标准的线程由线程ID,当前指令指针(PC),寄存器集合和堆栈组成。 (2)线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。...

everthing willl be ok 387

Linux线程——线程概念理解

本文主要介绍了Linux系统下的线程概念及其实现机制。文章首先从宏观角度解释了线程与进程的关系:进程是分配系统资源的实体,而线程是CPU调度的基本单位。在Linux中,线程通过共享进程地址空间的多个task_struct实现,本质上是用进程模拟线程(轻量级进程)。这种设计与Windows等系统不同(后者使用独立的TCB结构),能减少代码冗余。 文章重点阐述了Linux线程的核心特性:共享进程地址空间作为资源访问窗口,但各线程拥有独立的执行流。通过图解说明了多线程如何共享同一虚拟地址空间,并讨论了线程间资源共

2301_79705195的博客 926

Linux的线程概念与控制

首先Linux内核不区分"进程"和"线程",统一用task_struct管理。CPU处理的是一个个task_struct。进程1个或多个task_struct(PCB) +代码和数据线程(轻量级进程) =1个task_struct(PCB) (它通过指针共享了其所属进程的代码和数据我们之前讲的传统的进程是一个主线程。一个进程的多个task_struct(PCB),被认为是多个轻量级进程,主轻量级进程的id即lwp(light weight peocess)与进程的pid相同。

博客 | 复习知识点 + 练习 + 最终的领悟 | 全力以赴 1427

【Linux】线程概念与线程控制

线程的概念与控制,页表相关内容

weixin_69380220的博客 1287

linux多线程求和_linux的线程与多线程

了解到linux下的进程后,就会想知道linux下的线程到底是如何实现的,进程是针对资源调度的,linux下是资源调度的基本单位,既然已经有了进程,多进程的概念,那么为什么会有线程要被使用与诞生呢?1.线程概念;线程就是进程中的一条执行流,是CPU调度的基本单位,在liunx下是一个轻量级进程Linux下的线程是通过pcb实现的,pcb是程序运行的动态描述,通过这个描述,系统实现程序运行的调度,一...

weixin_39966740的博客 111
上一篇: Linux相关概念和易错知识点(27)(认识线程、页表与进程地址空间、线程资源划分)
下一篇: Linux相关概念和易错知识点(29)(线程相关性质和操作、线程封装)
-SGlow-
博客等级 码龄3年 1513粉丝 118原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值