操作系统(一)内存管理、线程进程、CPU调度

本文深入探讨了操作系统的核心概念,包括内存管理的分页与分段机制、虚拟内存的覆盖与交换技术、进程的生命周期与状态转换、线程与进程的区别、同步原语如信号量和管程,以及死锁的预防和恢复策略。此外,还介绍了进程间通信的多种方式,如信号、管道、消息队列和共享内存,以及文件系统的基本原理。
摘要由CSDN通过智能技术生成

layout: post
title: 操作系统(一)操作系统
description: 操作系统(一)操作系统
tag: 操作系统


操作系统概述

操作系统的内部组件

  • CPU调度管理
  • 物理内存管理
  • 虚拟内存管理
  • 文件系统管理
  • 中断处理与设备驱动

操作系统内核的特征

  • 并发:在一段时间内可以有多个程序运行(注意与并行的区别,并行要求是在同一时刻有多个程序可以运行,一般要求有多个CPU可以调用才可以并行)
  • 共享:应用可以程序可以访问相同的资源(分为“同时”访问与互斥共享)。
  • 虚拟:利用多道程序设计技术,例如将内存虚拟化为地址空间,将CPU虚拟化为多个进程,让每个用户都觉得有一个计算机专门为他服务。
  • 异步:程序的执行不是一贯到底,而是走走停停,向前推进的速度不可预知 ,但只要运行环境相同,OS需要保证程序运行结果也相同。

操作系统提供的功能

  • 系统控制
  • 管理应用程序
  • 为应用程序提供IO等服务
  • 资源管理、管理外设、分配资源

启动、系统调用、中断和异常

启动:操作系统(OS)起初放置在硬盘(DISK)上,需要BIOS(basic I/O system,基本I/O系统)操作,BIOS使用BootLoader(一个加载程序)把OS从硬盘加载到内存
系统调用:应用程序向操作系统发出指令,调用系统的资源,控制权从用户态转变为系统态,完成指令的效果。

中断:由各种外设或者中断定时器触发的中断效果,系统会保存程序运行的状态,以便后序回复。

异常:控制指令触发了操作系统不希望、不应该执行的内容,引发工作异常。
异常和中断都要相应的类型编码ID

内存分层体系

地址空间与地址生成

物理地址空间——硬件支持的地址空间
逻辑地址空间——一个运行的程序拥有的内存范围
两者可以通过CPU中的MMU(Memory Management Unit)进行映射。
应用程序中的变量和代码的逻辑地址中通过编译器分配和生成。这期间可能经过多种编译转换,如果C语言编译器、汇编语言编译器等等。

连续内存分配

内存碎片问题

在分配单元间(外部碎片)和分配单元中(内部碎片),这些内存碎片空间不能被利用。内存分配时应该尽量规避内存碎片问题。

分区动态分配

分配策略包含:
首次适配:从0开始找到并使用第一个可用的空闲块。它实现简单,但容易产生外部碎片。
最优适配:找到能满足分配要求且溢出空间最少的内存地址段分配。它减小了外部碎片产生的尺寸,但重分配慢,容易产生很多没用的微小碎片。
最差适配:使用空间最大的内存段分配。效率快,但是外部碎片多,易于破碎大的块,以致于后边没用大块可用了。

碎片整理策略:
压缩式分配:将已分配的内存段压缩到一块,使得外部碎片得以合并
交换式分配:将硬盘空间看做内存的备用空间,执行交换处理。

非连续内存

分段管理

分段管理,各个段间进行隔离,保证内存访问安全。
在这里插入图片描述

在这里插入图片描述

分页管理

分段需要段号和段的偏移,分页同样需要页编号和页偏移量,所不同在于,页大小是固定的而段大小是不定的。
物理内存地址就是按照类似页的帧来分配的:
在这里插入图片描述
逻辑地址空间被划分为大小相同的页,分页管理机制下,逻辑地址到物理地址的映射过程:
在这里插入图片描述

TLB,(Translation Look-aside Buffer)

页表可能非常大,且导致访问一个内存单元需要两次内存访问(一次用于获取页表项(因为页表也在内存中),一次用于访问数据)。
TLB利用缓存的思想,将经常使用的页表项存在TLB缓冲中,这就极大的缩减了页表查询的耗用时间。
在这里插入图片描述

多级页表

在这里插入图片描述

反向页表

页表是利用逻辑页的页编号找到物理帧的页帧号。反向页表则是利用物理页的页帧号来查找对应的逻辑页的编号。在反向页表中通过哈希算法搜索每个页对应的帧号。
在这里插入图片描述

虚拟内存

存储器的种类层次:
在这里插入图片描述
在这里插入图片描述

覆盖技术

把程序按照其自身的逻辑结构,划分为若干个功能上相对独立的程序模块,那些不会同时执行的模块共享同一块内存,分时运行
在这里插入图片描述
缺点。

  • 程序员设计开销
  • IO开销

交换技术

将暂时不能运行的程序送到外存,从而获得空闲内存空间。
在这里插入图片描述

虚拟内存管理技术(虚存技术)

  • 虚存技术能像覆盖技术那样不把所有内容都放在内存中,但它无须程序员干涉。
  • 能像交换技术那样,实现进程在内存与外存之间的交换,且可用做得更好,只对进程的部分内容在内存和外存间交换。
    程序编写时最好能够满足时间局部性(一条指令的一次执行和下次执行都集中在一个较短的时期内)和空间局部性(当前指令和临近几条指令对于数据访问都集中在一个较小区域内)。
    举例来讲:
    数组是按行存储的,因此下边两种写法,方法2一行一行的赋值是更好的。
    在这里插入图片描述
    大部分虚拟存储系统都采用虚拟页式存储管理技术,在基本页式管理基础上,增加了请求调页和页面置换功能。
    基本思路是:当一个用户程序调入内存运行时,不是将该程序的所有页面都装入内存,而是装入部分的页面即可启动程序。
    运行过程中,如果发现运行的程序或要访问的数据不在内存,则向系统发出缺页中断请求,系统在处理这个中断时,将外存中相应的页面调入内存。

在这里插入图片描述

在这里插入图片描述

虚存管理的页面置换算法

最优页面置换算法:当一个缺页中断发生时,对于保存在内存中的每一个逻辑页面,计算它的下一次访问之前,还需等待多长时间,从中选择等待时间最长的那个,作为被置换的页面。最优置换是一种理想情况,实际上操作系统无从知道每个页面需要等待多久才可再次被访问,它只能作为一种参考。

先进先出算法(FIFO):选择在内存中驻留时间最长的页面并淘汰之。这样做实现简单,但性能较差,调出页可能是经常要访问的页。

最近最久未使用算法(LRU):缺页中断发生时,选择最久未使用的页面并淘汰之。这种算法的缺点是开销比较大。
在这里插入图片描述

时钟页面置换算法:它是对先进先出和最近最久未使用算法的组合优化。
1、需要用到页表项当中的访问位,当一个页面被装入内存时,把该位初始化为0,然后如果这个页面被访问,则把该位置为1.
2、把各个页面组织成闭环链表(类似钟表面),把指针指向最老的页面(最先进来的)
3、当发生一个缺页中断时,考察所指向的最老的页面,若它的访问位是0,立即淘汰,若访问位是1,则把该位置为0,然后指针往下移动一格,如此下去,直到找到被淘汰的页面。
在这里插入图片描述
二次机会法或称enhanced clock 算法:同时使用脏位(就是写位,如果有写操作,则为1,否则是0)和使用位来指导置换。所谓页面置换,就是要将硬盘存放读到内存中,如果只是读操作,则不需要后边再把内存写入到硬盘中,而如果有写操作,后续就还需要把更改后的内容写回到硬盘中。通过脏位的指示可以减少写回的次数,从而提高效率。
在这种机制下,写位为1和使用位为1的页面会在时钟双向链表中被扫描两次,因此被形象的称为二次机会法,二次机会法将给予访问频繁(used为1)和写操作频繁(脏位为1)的内存,更多的机会留在内存中。

在这里插入图片描述
最不常用算法:当缺页中断时,选择访问次数最少的那个页面并淘汰之。

抖动问题

如果分配给一个进程的物理页面太少,不能包含整个工作集,即常驻工作集,那么进程就会造成很多的缺页中断,需要频繁在内存与外存之间替换页面,从而促使进程的运行速度变得很慢,我们把这种状态称为“抖动”。
抖动产生的原因:随着驻留内存的进程数目增加,分配给每个进程的物理页面数不断减小,缺页率不断上升。OS需要选择一个适当的进程数目和进行需要的帧数,以便于在并发水平和缺页率之间达到一个平衡。

在这里插入图片描述

进程

进程:一个具有一定独立功能的程序在一个数据集合上的一次动态执行过程。
进程是动态的,程序是静态的,进程是暂时的,程序是永久的,进程是程序的执行,进程拥有核心态与用户态。

进程的特点

进程的特点:

  • 动态性:可动态创建,结束
  • 并发性:进程可以独立调度并占用处理机制运行,并发并行。
  • 独立性:不同进程工作互不影响
  • 制约性:因访问共享数据/资源、或进程间发送同步而产生制约。

进程的控制结构

进程控制块(PCB,process control block):操作系统控制进程运行所用的信息集合,操作系统用PCB来描述进程的基本情况以及运行变化的过程,PCB是进程存在的唯一标志
PCB包含一下三大类信息:

  • 进程标识信息:如本进程的标识,本进程的产生者(父进程)标识,用户标识。
  • 处理机状态信息保存区:保存进程的运行现场信息,如用户可见寄存器、控制和状态寄存器、栈指针。
  • 进程控制信息:如调度和状态信息、进程间通信信息、存储管理信息、进程所用资源、有关数据结构连接信息。
    PCB一般采用链表来组织

进程的生命周期

  • 进程创建:引起进程创建的3个主要事件:
    • 系统初始化;
    • 用户请求创建一个新进程;
    • 正在运行的进程执行了创建进程的系统调用;
  • 进程运行:CPU内核选择一个就绪的进程,让他占用处理机并执行
  • 进程等待(阻塞):在某些情况下,进程可能没有办法直接运行。进程只能自己阻塞自己,因为只有进程自身才能知道何时需要等待某种事件的发生
    • 请求并等待系统服务,因为无法马上完成
    • 启动某种操作,无法马上完成
    • 需要的数据没有到达
  • 进程唤醒:被阻塞的进程需要的资源可被满足或者阻塞进程等待的事件到达时进程被唤醒。进程只能被别的进程或OS唤醒
  • 进程结束:以下情况进程结束
    • 正常退出(自愿的)
    • 错误退出(自愿的)
    • 致命错误(强制性的)
    • 被其他进程杀死(强制性的)

进程的状态变化模型

进程拥有3个基本的状态:运行态、就绪态、阻塞态。可以由运行与就绪可以相互转换、运行可转为阻塞、阻塞可转为就绪。

在这里插入图片描述

在这里插入图片描述

进程挂起

进程在挂起状态意味着进程没有占用内存空间,而是映像在磁盘上
在这里插入图片描述
挂起的进程分为两类:

  • 阻塞挂起状态:进程在外存并等待某事件出现;
  • 就绪挂起状态:进程在外存,但只要进入内存即可运行。

把一个进程从内存转为外存或者说挂起,可以分为以下几种情况:

  • 阻塞到阻塞挂起:没有进程处于就绪状态或者就绪进程要求更多内存资源时会将已经阻塞的进程挂起,即从阻塞到阻塞挂起。
  • 就绪到就绪挂起:当有优先级阻塞进程和低优先级就绪时,系统会选择挂起低优先级就绪进程,即从就绪到就绪挂起。
  • 运行到就绪挂起:对抢先式分时系统,当有高优先级阻塞挂起进程因事件出现而进入就绪挂起时,系统可能会把运行进程转为挂起,即从运行到就绪挂起。
  • 当有阻塞挂起进程因相关事件出现时,系统会把阻塞挂起进程转为就绪挂起进程。即从阻塞挂起到就绪挂起。

解挂/激活一个进程,即把进程从外存转到内存可能有以下几种情况:

  • 就绪挂起到就绪:没有就绪进程或者挂起就绪进程优先级高于就绪进程时,会从挂起就绪到就绪。
  • 阻塞挂起到阻塞:当一个进程释放足够内存时,系统会把一个高优先级阻塞挂起(系统认为很快会出现所等待的事件)进程转为阻塞进程。

操作系统维护一组队列用来表示系统中所有进程的当前状态,不同的状态分别用不同队列表示(就绪队列、各类阻塞队列)
每个PCB都根据它的状态加入到相应的队列,每个进程状态改变时,它的PCB从一个状态队列脱离加入另一个队列

在这里插入图片描述

线程

线程:线程是进程当中的一条执行流程。进程把一组相关的资源(包括地址空间中的代码段、数据段、打开的文件等)组合起来,构成了一个资源平台(环境),而线程是在进程这个资源平台上的一条线性的执行流程,简称线程。
线程简答讲 = 进程 - 共享资源
线程的优点是:

  • 一个进程可以同时存在多个线程
  • 各个线程之间可以并发执行
  • 各个线程之间共享地址空间和文件资源。

线程的缺点:一个线程崩溃会导致所属进程的所有线程崩溃。

线程与进程的比较

  • 进程是资源分配单位,线程是CPU调度单位;
  • 进程拥有一个完整的资源平台,而线程只独享必不可少的资源如寄存器和栈;
  • 线程同样有就绪、阻塞和执行三种基本状态,同样具有状态之间的转换关系;
  • 线程能减少并发执行的时间和空间开销;这是因为:
    • 线程的创建时间和终止时间比进程短
    • 同一进程内的线程切换时间比进程短
    • 由于同一进程的各线程间共享内存和文件资源,可直接进行不通过内核的通信。

线程的实现

线程的实现主要有三种方式:

  • 用户线程:在用户空间实现
  • 内核线程:在内核实现
  • 轻量级进程:在内核实现,支持用户线程
    在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

进程控制

进程切换:
在这里插入图片描述
在这里插入图片描述
进程加载和执行:
在这里插入图片描述
进程的等待和终止:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

CPU调度

CPU调用原则需要考虑以下因素:
在这里插入图片描述
在这里插入图片描述

调度算法

在这里插入图片描述
FCFS先来先服务的算法思路实现简单,但假如先来的是执行耗时的事件,则后边事件的等待时间就会很长,因此又引入了SRT短进程优先的策略。这个策略的平均等待时间会缩短很多。但可能会出现连续的短任务流的出现使得长任务饥饿,长任务的平均等待时间显著增加。为解决这个问题可以考虑HRRN最高响应比优先算法,响应比的计算公式为R = (W(等待时间) + S(执行时间)/ S(执行时间)),R越大说明等待时间越长,可以优先执行。存在的问题是无法有效的获知程序的执行时间。
时间片轮询算法:每个进程分配一个运行的时间片,如下边例子中的20,时间片内任务执行完毕后,则时间片提前结束。时间片大小对于性能有很大的影响,时间片过小则进程切换的开销会大,时间片过大则平均等待时间会长。
在这里插入图片描述
在这里插入图片描述
多级反馈队列(MFQ):任务等待时间越长,则设置更高的优先级,任务执行时间越长则优先级越低。通过反馈动态调节。

公平共享调度:有时候需要满足一台计算机多个用户使用,在用户级上应该公平共享的分配计算资源。

实时调度

在这里插入图片描述

在这里插入图片描述

多处理器调度

多个CPU并行调度:
在这里插入图片描述

优先级反转

在下图的例子中,T1-T3优先级依次下降,T3从t1到t2后执行了访问共享资源,但此时到t3时刻被优先级更高的T1进程打断,转而执行T1,T1执行到t4时刻,发现自己也需要刚刚T1访问的那个恭喜资源,于是切换为T3,让它释放那个占用的共享资源,但是在释放期间t5时刻,T1被优先级更高的T2再次打断,转而执行T2,等待T2执行完毕,才会到T3释放共享资源,释放完毕后,执行T1。这期间,优先级更低的T2抢在了优先级更高的T1之前完成了任务,称为优先级反转。造成优先级反转的主要原因是低进程影响了高优先级进程的运行,解决方案是T3在释放共享资源时应该继承T1的优先级,以避免被进程T2抢占。
在这里插入图片描述

另外一种解决方案称为优先级天花板的思路:
在这里插入图片描述

同步

personA与personB之间对于面包的处理没有同步!!!
在这里插入图片描述

在这里插入图片描述

临界区\互斥\死锁\饥饿\锁

在这里插入图片描述
临界区的设计原则:

  • 互斥:同一时间临界区最多存在一个线程
  • Progress:如果一个线程想要进入临界区,那么它最终会成功
  • 有限等待:如果一个线程i处于入口区,那么在i的请求被接受之前,其他线程进入临界区的时间是有限制的
  • 无忙等待(可选):如果一个进程在等待进入临界区,那么在它可以进入之前会被挂起

使用锁来编写临界区

在这里插入图片描述

// 测试target的结果,测试完毕将其赋值为true
bool TestAndSet(bool *target){
	bool rv = *target;
	*target = true;
	return rv;
}
// 交换a和b的值
void Exchange(bool *a, bool *b){
	bool temp = *a;
	*a = *b;
	*b = temp;
}

TestAndSet与Exchange被封装为整体的机器语句,成为原子指令。
采用这两个函数设计锁进入临近区和退出临近区:
在进入临界区时:如果锁被释放,那么testandset读取到0,并将值设置为1,锁被置为忙并且需要等待完成。如果锁处于忙状态,那么testandset读取到1,并将值设置为1,不改变锁的状态并且需要循环(自旋spin,即等待)。
当占用临界区的进程结束,调用release,将value设置为0,那么循环就可以退出了。

class Lock{
// value用于设置状态位
	int value = 0;
}
Lock::Acauire(){
	while(TestAndSet(value)){
	}; // 盲等
}

Lock:: Release(){
	value = 0;
}

信号量和管程

信号量

信号量是一个整形数据,他有自增和自减两种操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

管程(monitor)

信号量机制是一种进程同步机制,但每个要访问临界资源的进程都必须自备同步操作wait(S)和signal(S)。这样大量同步操作分散到各个进程中,可能会导致系统管理问题和死锁,在解决上述问题的过程中,便产生了新的进程同步工具——管程

管程是包含了一系列共享变量以及针对这些变量操作的函数的组合,它有一个锁,有0或多个条件变量。
在这里插入图片描述

管程相对于锁又增加了很多的条件变量,用于确定某些共享资源是否得到满足。进入管程后就可以操作各种条件变量,当某些条件无法得到满足时,会进入等待(wait),当再次满足时会唤醒(signal)。
在这里插入图片描述

在这里插入图片描述
管程是一种程序设计语言的结构成分,它和信号量有同等的表达能力,从语言的角度看,管程主要有以下特性:

  • 模块化,即管程是一个基本程序单位,可以单独编译;
  • 抽象数据类型,指管程中不仅有数据,而且有对数据的操作;
  • 信息屏蔽,指管程中的数据结构只能被管程中的过程访问,这些过程也是在管程内部定义的,供管程外的进程调用,而管程中的数据结构以及过程(函数)的具体实现外部不可见。

经典同步问题

读者-写者问题

在这里插入图片描述
读写的操作是共享的,而读与写是互斥的。

// writer
sem_wait(writemutex);
	write;
sem_post(writemutex);


// reader
sem_wait(countmutex);
if(Rcount == 0){
	sem_wait(writemutex); // 把写者挂起后再增加Rcount
	++Rcount;
}
sem_post(countmutex);

read;

sem_wait(countmutex);
--Rcount;
if(Rcount == 0)
	sem_post(writemutex); //如果没有读者了,唤醒写者
sem_post(countmutex)		

哲学家用餐问题

在这里插入图片描述
思路1:采用互斥的思想,每个哲学家拿起叉子就进入临界区。
在这里插入图片描述
思路2:避免单个叉子在手的资源浪费现象,即要么不要叉子,要么同时拿起两把叉子。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

死锁问题

在这里插入图片描述
在这里插入图片描述

死锁出现的特征

死锁可能出现如果四个条件同时成立:

  • 有资源互斥:在一个时间只能有一个进程使用资源。
  • 持有并等待:进程保持至少一个资源正在等待获取其他进程持有的额外资源。
  • 无抢占:一个资源只能在进程已经完成了它的任务之后被自愿释放。
  • 循环等待:存在等待进程集合{P0,P1,……PN},P0正在等待P1的资源、P1正在等待P2,……PN-1在等待PN而PN正在等待P0。

在这里插入图片描述

死锁处理方法

  • 死锁预防:确保系统永远不会进入死锁
  • 死锁恢复:运行系统进入死锁,寻找恢复方法
  • 忽略死锁:忽略该问题
    在这里插入图片描述
    死锁预防的思路
    在这里插入图片描述
    在这里插入图片描述
    死锁避免的思路:
    在这里插入图片描述
银行家算法

银行家算法(Banker’s Algorithm)是一个死锁避免的著名算法,是由艾兹格·迪杰斯特拉在1965年为T.H.E系统设计的一种避免死锁产生的算法。它以银行借贷系统的分配策略为基础。判断并保证系统的安全运行。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果基于银行家算法得到的结果是safe则不会出现死锁,如果返回结果是unsafe则有可能出现死锁

死锁检测与恢复

死锁的检测与银行家算法思路很像:
在这里插入图片描述
死锁检测算法的开销很大,而且它借鉴了银行家算法的思路,需要提前知道各个进程最大使用资源,但这些信息可能是无法获取到的。
死锁恢复的思路:
在这里插入图片描述

进程间通信(interprocess communication,IPC)

IPC概述

在这里插入图片描述
IPC按照通信的信道类型分为:

  • 直接通信:进程间自建链路,直接收发
  • 间接通信:两进程共享消息队列,有消息队列或者缓冲区作为中介传递,间接通信需要操作系统的介入。

IPC按照通信对于进程的影响可以分为:

  • 阻塞式:消息传递时会阻塞进程,等待消息传递完毕再运行,是同步的
  • 非阻塞式:消息传递不会阻塞进程,是异步的。

信号、管道、消息队列、共享内存

信号:
在这里插入图片描述
管道:将一个进程的输出作为另一个进程的输入。例如下边这个例子,ls | more,ls列出,more显示更多,more是ls的子进程。
在这里插入图片描述
在这里插入图片描述

消息队列:管道实现借助于父子关系,且传递的是字节流,消息队列则可以存储结构型数据,且无需进程间具备父子关系。
共享内存:信号、管道、消息队列都是间接通信,共享内存是直接通信方式。
在这里插入图片描述
在这里插入图片描述

文件系统和文件

文件系统、管理、属性、文件头、文件描述符

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,一些涉及底层的程序编写往往会围绕着文件描述符展开。但是文件描述符这一概念往往只适用于UNIX、Linux这样的操作系统。
在这里插入图片描述
通文件描述符查到文件表中存储到的文件信息。

在这里插入图片描述
在这里插入图片描述

目录、文件别名、文件系统种类

在这里插入图片描述
目录需要满足的操作:
在这里插入图片描述
文件名遍历:
在这里插入图片描述

文件别名:
文件名与文件的链接有硬链接(多个文件项指向一个文件)和软链接(以“快捷方式”指向其他文件)之分。
在这里插入图片描述
硬链接会使得引用计数减一(只有当引用计数减少为0时,文件真正删除),软链接则会使得连接变为空指针。
在这里插入图片描述
文件系统种类:
在这里插入图片描述

虚拟文件系统

操作系统通过虚拟文件系统层,对于文件系统抽象封装,使得上层用户可以简化对于文件的操作。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

多磁盘管理

磁盘分为不同的扇区,磁头移动访问不同扇区。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以将奇偶检验磁盘均匀分布在各个盘内:
在这里插入图片描述

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值