文章目录

  • 一 基本概念
    • 1 操作系统的特征
    • 2 操作系统的位置
    • 3 计算机的硬件组成
    • 4 中断与异常
    • 5 系统调用
  • 二 进程管理
    • 1 进程控制块 PCB(Process Control Block)
    • 2 进程的状态与转换
    • 3 进程间的通信
    • 4 线程
    • 5 调度算法
    • 6 死锁
    • 7 PV 操作
  • 三 内存管理
    • 1 内存的非连续分配
    • 2 虚拟内存的概念
    • 3 虚拟内存的实现——局部性原理
    • 4 虚拟内存的页面置换算法
    • 5 抖动和颠簸
  • 四 文件管理
  • 五 I/O管理
    • 1 同步与异步
    • 2 阻塞与非阻塞
    • 3 I/O模型:同步阻塞I/O & 同步非阻塞I/O
    • 4 I/O模型:I/O多路复用
    • 5 Java NIO
      • 5.1 NIO 核心组件:缓冲区
      • 5.2 NIO 核心组件:Channel
      • 5.3 NIO 核心组件:Selector

一 基本概念

1 操作系统的特征

  • 并发:两个或多个事件,在同一时间间隔内发生
  • 共享:系统中的资源可供内存中多个并发执行的进程共同使用,分为互斥共享方式 / 同时访问方式
  • 虚拟:将一个物理上的实体变为若干逻辑上的对应物
  • 异步:进程的执行不是一贯到底,而是以不可预知的速度向前推进

2 操作系统的位置

  • 操作系统是在硬件的基础之上安装的 软件,能够根据用户输入的指令达到 控制硬件 的效果
  • 操作系统相当于是一个中间层,为用户层和硬件提供各自的接口,屏蔽了不同应用和硬件之间的差异,达到统一标准的作用
  • 大部分计算机有两种运行模式:内核态用户态
  • 软件中最基础的部分是操作系统,它运行在 内核态 中,操作系统具有硬件的访问权,可以执行机器能够运行的任何指令;软件的其余部分运行在 用户态

3 计算机的硬件组成

  • 冯诺伊曼体系结构:运算器、控制器、存储器(内存与外存)、输入设备、输出设备
  • 总线(Buses):总线在组件之间来回传输字节信息。通常总线被设计成传送定长的字节块,也就是 字(word)。字中的字节数是一个基本的系统参数,常见的有 4 个字节(32 位)或者 8 个字节(64 位)

4 中断与异常

  • 操作系统内核工作在核心态,用户程序工作在用户态,但用户程序需要使用核心态的功能。中断和异常实现了从用户态到核心态的切换,是用户态转为核心态的唯一途径。发生中断或异常时,运行在用户态的 CPU 会立刻切换成核心态
  • 中断的定义:也称外中断,指来自 CPU 指令以外的事件发生(I/O结束中断、时钟中断等),该类中断通常是与当前指令无关的事件,即它们与当前处理机运行的程序无关
  • 异常的定义:也称内中断或 陷入(trap),指源自 CPU 执行指令内部的事件(程序的非法操作码、地址越界、算术溢出等),对异常的处理一般要依赖于当前程序的运行现场

5 系统调用

  • 用户在程序中调用操作系统提供的一些子功能
  • 凡是与资源有关的操作,都必须通过系统调用的方式向操作系统提出服务请求,并由操作系统的内核程序代为完成
  • 发起系统调用需要用户程序执行陷入(trap)指令(该指令不是特权命令,因为是在用户态使用),即通过异常的方式切换为核心态
  • 从核心态转为用户态需要执行中断返回指令,该指令是特权指令
  • 和网络模型的对应关系:系统调用发生在5->4时

二 进程管理

1 进程控制块 PCB(Process Control Block)

  • 程序段 + 相关数据段 + PCB = 进程实体;PCB 是一种数据结构,是标志进程存在的唯一标志
  • 创建进程,实际上指创建进程实体中的 PCB;撤销进程,实际上指撤销 PCB
  • PCB 是有限的,如果创建进程时申请 PCB 失败,则创建失败

2 进程的状态与转换

状态 含义
创建 进程正在被创建,尚未转到就绪态
就绪 进程获得了除了处理机之外的一切所需资源,一旦得到处理机就可以运行。通常处于就绪态的进程组成就绪队列
运行 进程正在处理机上运行
阻塞 进程正在等待某一事件而暂停运行,如等待某些资源(不包括处理机)或等待I/O完成等,即使处理机空闲也不能运行
结束 进程正在从系统中消失,进程先进入结束态,然后回收释放资源

  • 从运行态变为阻塞态是主动行为,从阻塞态变为就绪态是被动行为(需要其它进程协助)
  • 进程在切换时,运行信息存放在 PCB 中

3 进程间的通信

  • 共享存储:进程间存在一块可直接访问的共享的空间

  • 消息传递:进程间的数据交换以格式化的消息为单位

  • 管道通信:消息传递的一种特殊方式,管道指 pipe 文件,连接一个读进程和一个写进程

4 线程

  • 线程是轻量级的进程,是最基本的 CPU 执行单元
  • 除了少量的运行必备的资源,线程几乎不拥有系统资源,但可以和同属一个进程的其它线程共享进程拥有的资源
  • 为什么引入线程可以提高系统的并发性:同一进程内的线程相互切换的开销很小,不进行进程的切换;即使线程切换时需要发生进程切换,总体而言进程切换的次数变少了

5 调度算法

  • 衡量调度的一些基本指标:
    周转时间:从作业提交到完成经历的时间
    带权周转时间:作业周转时间和作业实际运行时间的比值
  1. 先来先服务(FCFS)调度算法
    对长作业有利,短作业不利

  2. 短作业优先(SJF)调度算法
    对短作业有利,长作业不利

  3. 优先级调度算法
    为作业分配优先级,每次选择优先级最高的一个或几个作业执行
    可以分为 剥夺式 / 非剥夺式,静态优先级 / 动态优先级

  4. 高响应比优先调度算法
    响应比 = (等待时间+要求服务时间) / 要求服务时间
    是对 FCFS 和 SJF 的综合平衡,每次选择相应比最高的作业投入运行

  5. 时间片轮转调度算法
    时间片的大小对系统性能影响很大:时间片足够大时,退化成先来先服务;时间片太小时,进程切换过于频繁,CPU 利用率降低

  6. 多级反馈队列调度算法*
    是对优先级调度算法和时间片轮转调度算法的综合
    优先级越高的队列,时间片越小
    高优先级队列可以抢占低优先级队列的 CPU,只有在高优先级队列为空时,CPU 才分配给低优先级队列的进程

每隔一段时间,将所有的进程重置到最高优先级的队列中,防止低优先级的进程饥饿,以及进程类型变化导致的不公平对待
相同优先级的进程轮转运行


6 死锁

  • 产生的必要条件
条件 解释
互斥条件 在某段时间内,资源仅被一个进程占有,其它进程请求该资源只能等待
不剥夺条件 进程获得的资源只能主动释放,不能被其它进程强行夺走
请求并保持条件 进程已经持有至少一个资源,再请求新的资源时,对已持有的资源保持不放
循环等待条件 存在进程资源的循环等待链
  • 处理死锁的策略
  1. 死锁预防:破坏产生死锁的产生条件之一
  2. 死锁避免:银行家算法
    进程运行前,先声明对各种资源的最大需求量
    操作系统先测试该进程已占用的资源数与本次申请的资源数是否超过声明的最大需求量,如果超出则拒绝分配
    如果不超出则测试 系统现存的资源能否满足进程需要的最大资源,如果满足则可以分配,否则推迟分配
  3. 死锁检测和解除:资源剥夺、撤销进程、进程回退

7 PV 操作

  • PV 操作是一种实现进程互斥和同步的方法
    P(S):使 S=S-1 ,若 S>=0 ,则该进程继续执行,否则排入等待队列
    V(S):使 S=S+1 ,若 S>0 ,唤醒等待队列中的一个进程
  • 信号量根据作用可以分为 互斥信号量同步信号量
  • 实现互斥的 P 操作一定要放在实现同步的 P 操作之后(先获取资源再获取锁),否则可能会造成死锁;V 操作的顺序可以交换
  1. 生产者-消费者问题
semaphore mutex = 1;  //互斥信号量
semaphore empty = n;  //同步信号量,空闲缓冲区的数量
semaphore full = 0;   //同步信号量,非空缓冲区的数量
producer(){while(1){生成一个产品;P(empty); //消耗一个空闲缓冲区P(mutex);把产品放入缓冲区;V(mutex);V(full)   //增加一个产品}
}
consumer(){while(1){P(full);   //消耗一个产品P(mutex);从缓冲区取出一个产品;V(mutex);V(empty);  //增加一个空闲缓冲区使用产品;}
}
  1. 吃水果问题
  • 桌上有一个盘子,每次能放 n 个水果,妈妈向盘中放苹果,爸爸向盘中放橘子,儿子专等吃盘里的橘子,女儿专等吃盘里的苹果。只要盘子空,妈妈可向盘中放水果,仅当盘中有自己需要的水果时,儿子或女儿可从中取出
  • 因为爸爸和妈妈向盘中放水果需要消耗同一个信号量 empty,所以要加互斥锁
  • 如果仅有一个儿子和一个女儿,在吃水果的时候不需要加互斥锁(消耗的不是同一种水果);如果是多个则需要加
  • 加不加互斥锁,取决于需要进行 P 操作的资源是否存在竞争
mutex = 1  // 互斥量
empty = n  // 同步量
apple = 0  // 同步量
orange = 0  // 同步量mother(){while(1){P(empty);P(mutex);放苹果;V(mutex);V(apple);}
}
father(){while(1){P(empty);P(mutex);放橘子;V(mutex);V(orange);}
}
son(){while(1){P(orange);// P(mutex);吃橘子;// V(mutex);V(empty);}
}
daughter(){while(1){P(apple);// P(mutex);吃苹果;// V(mutex);V(empty);}
}
  1. 抽烟者问题
semaphore offer1=0;  // 为第一个吸烟者提供卷纸和火柴
semaphore offer2=0;  // 为第二个吸烟者提供火柴和烟草
semaphore offer3=0;  // 为第三个吸烟者提供卷纸和烟草
semaphore finish=1;  // 通知供应者继续提供材料provider(){int rand;while(1){P(finish);rand = getRandom()%3;if(rand == 0)V(offer1);else if(rand == 1)V(offer2);elseV(offer3);}
}somker1(){while(1){P(offer1);制作香烟并吸掉;V(finish);}
}
somker2(){while(1){P(offer2);制作香烟并吸掉;V(finish);}
}
somker3(){while(1){P(offer3);制作香烟并吸掉;V(finish);}
}

三 内存管理

1 内存的非连续分配

非连续分类允许一个程序分散地装入不相邻的内存分区,但需要额外的空间存放索引,导致非连续分配的存储密度低于连续分配。非连续分配管理方式可以根据 分区的大小是否固定,分为分页存储管理方式和分段存储管理方式

  1. 分页存储管理方式
  • 将内存空间划分为大小相等且固定的页(页相对于固定分区分配的划分更小),以页作为内存的基本单位;这种思想类似于固定分区分配,但产生的内部碎片(页内碎片)要小得多
  • 使用页表完成逻辑地址到物理地址的映射,页的逻辑地址 = 页号 + 页内偏移量(也可以只给出总偏移量,通过除法和求余得到页号、页内偏移量)
  • 通过快表(TLB)作为缓存的方式解决了每次访问内存时都需要进行逻辑地址到物理地址的转换,转换速度需要足够快的问题;通过多级页表的方式解决了 页表不能太大,否则利用率会降低的问题(但多级页表会增加查询时间)
分页可能产生少量的内部碎片
对于每个页都需要在页表中记录虚拟地址到物理地址的映射,无论该页是否有数据
  1. 分段存储管理方式
  • 使用段表完成逻辑地址到物理地址的映射,段的逻辑地址 = 段号 + 偏移量
  • 和分页存储相比,段的大小是不固定的。因此不能通过只给出偏移量,通过整数除法得到段号,或通过求余得到段内偏移,即 分段管理的地址空间是二维的
分段的问题是内存空间被划分为不规整的部分,容易出现外部碎片问题
需要基址寄存器和界限寄存器记录内存的起始位置和长度
  1. 段页式管理方式
  • 作业的地址空间首先被划分为若干逻辑段,每段再划分为大小相等的页
  • 逻辑地址 = 段号 + 页号 + 页内偏移量
每个段维护一个页表(避免了纯分页导致的问题:未分配的页都出现在页表里)
段的基址寄存器和界限寄存器记录页表的起始位置和长度

2 虚拟内存的概念

  • 虚拟内存的实现基于非连续分配的内存管理方式
  • 虚拟内存让每个程序都认为自己“独占内存”,无需考虑覆盖其它程序内存的情况,同时每个程序的内存(虚拟内存)都对应一个很大的字节数组
  • 每个程序的虚拟内存由两部分空间组成:给这个程序分配的 内存空间,以及分配的 磁盘空间
  • 请求的页在内存中命中时可以直接使用;不命中时通过页面置换算法,将请求页从硬盘空间换入内存空间
  • 操作系统完成由虚拟内存地址到 真实内存地址 或者 磁盘地址 之间的映射工作

3 虚拟内存的实现——局部性原理

  • 局部性原理:空间局部性、时间局部性
  • 虚拟内存基于局部性原理实现:程序装入内存时,将程序的一部分装入内存即可启动程序。程序执行时访问到不在内存中的信息,则操作系统将所需要的部分调入内存,然后执行程序;同样地,将暂时不使用的内容换出到外存。这样用户就能获得比实际内存更大的内存体验,可以运行所需内存大于物理内存的程序

4 虚拟内存的页面置换算法

用于决定虚拟内存 换出 哪页,即内存满但是需要调入新页时,决定从 内存 中调出哪一页到 磁盘的对换区

  1. 最佳置换算法
    选择被淘汰的页面是以后永不使用的页面,或是最长时间内不再被访问的对象
    无法实现,但可以用于评价其它算法
  2. 先进先出(FIFO)页面置换算法
  3. 最近最久未使用(LRU)置换算法
    为每个页面设置一个访问字段,记录页面上次被访问以来经历的时间,每次淘汰经过时间最久的页面
  4. 时钟置换算法(CLOCK)
    简单的 CLOCK 算法仅使用 1 位标志当前页的状态,一种改进做法是引入另外的位标志该页是否是脏页,从而延迟替换脏页的内容(因为替换脏页需要将数据写入外存,代价比较大)

5 抖动和颠簸

  • 刚换出的页面马上又要换入主存,刚换入的页面马上又要换出主存,这种频繁的页面调度行为成为抖动
  • 如果一个进程换页使用的时间多于执行时间,则这个进程处于颠簸
  • 发生抖动的主要原因是,某个进程频繁访问的页面数,高于主存可用的物理页数目

四 文件管理

  • 文件是以计算机硬盘为载体的,存储在计算机上的信息集合。系统运行时,计算机以进程为基本单位进行资源的调度和分配;在用户进行输入、输出时,以文件为基本单位
  • 文件系统用于实现用户对文件的访问、修改、保存、维护管理等要求

五 I/O管理

解读I/O多路复用技术
100%弄明白5种IO模型
I/O多路复用详解

1 同步与异步

  • 同步是指用户发起IO请求后,需要等待或者轮询内核IO操作完成后才能继续执行
  • 异步是指用户线程发起IO请求后继续执行,当内核操作完成后会通知线程或者调用用户线程注册的回调函数

2 阻塞与非阻塞

  • 阻塞是指IO操作需要彻底完成后才返回到用户空间
  • 非阻塞是指IO操作被调用后立即返回给用户一个状态值

3 I/O模型:同步阻塞I/O & 同步非阻塞I/O

同步阻塞I/O

  • 在应用调用 recvfrom 读取数据时,其系统调用直到数据包到达且被复制到应用缓冲区中或者发送错误时才返回
  • 整个请求过程中,用户线程是被阻塞的,对CPU资源利用不够
  • 服务器单线程情况下,无法处理并发的请求;服务器多线程时,为每个请求创建一个线程,造成资源浪费

同步非阻塞I/O

  • 如果缓冲区没有数据的话,就会直接返回一个 EWOULDBLOCK 错误,不会让应用一直等待中。在没有数据的时候会即刻返回错误标识,意味着如果应用要读取数据就需要不断的调用 recvfrom 请求,直到读取到它要的数据为止
  • 用户线程的轮询操作消耗了大量 CPU 资源

4 I/O模型:I/O多路复用

  • I/O = 网络I/O,多路 = 多个TCP连接,复用 = 复用一个或少量线程(很多个网络I/O,复用一个或少量的线程来处理这些文件描述符fd
  • 实现思路是,由一个线程监控多个网络请求(fd,文件描述符,Linux 系统把所有网络请求以 fd 来标识),这样只需要一个或几个线程就可以完成数据状态询问的操作,当有数据准备就绪之后再分配对应的线程去读取数据,节省大量的线程资源
  • 系统提供了一种函数可以同时监控多个 fd 的操作,避免为每个 fd 创建一个对应的监控线程。在 Linux 下包括三种函数:select, poll, epoll
  • 进程将一个或多个 fd 传递给 select,阻塞在 select 操作上,由 select 侦测多个fd是否准备就绪,当有 fd 准备就绪时,select 返回数据可读状态,应用程序再调用 recvfrom 读取数据
  • Reactor 的功能:首先通过 I/O 多路复用监听事件,收到事件后,根据事件类型分配(Dispatch)给某个进程 / 线程进行处理

select poll epoll

select poll epoll
最大连接数 1024(可修改) 无上限 有上限但是非常大
底层实现 数组 链表 红黑树
性能下降的情况 连接数过大 连接数过大 活跃的连接数过大
发生I/O事件的操作 轮询O(n) 轮询O(n) 回调O(1)
内核->用户空间消息传递方式 内存拷贝 内存拷贝 共享内存

select & poll

  • 仅仅知道检测的连接中有 I/O 事件发生了,但不知道是哪些连接,所以只能轮询所有连接
  • 基于轮询,时间复杂度O(n),随着连接数的增加性能急剧下降
  • select 底层采用数组,有最大 fd 数限制1024(可以通过修改宏定义/重新编译内核突破限制,但由于轮询的机制,仍会造成效率降低)
  • poll 底层使用链表,无最大 fd 限制,连接数过大时性能也会显著下降

epoll

  • 事件驱动 Event Poll,会通知哪个连接发生了怎样的 I/O 事件,采用回调而非轮询,时间复杂度O(1)
  • 只关心活跃的连接,不活跃的连接不会造成性能下降,在多个连接都很活跃的情况下会造成性能下降
  • 具有 EPOLLLT(Level Trigger 默认)EPOLLET (Edge Trigger) 两种模式
    • EPOLLLT,发生 I/O 事件时应用程序可以不立即处理事件,下次调用 epoll_wait 时仍然会通知该事件
    • EPOLLET,发生 I/O 事件时应用程序必须立即处理事件,如果这次没有把数据全部读写完(如读写缓冲区太小),那么下次调用 epoll_wait 时,不会再次通知该事件(即每个事件只会通知一次,only once),直到该连接出现第二次可读写事件才会通知,这样系统不会充斥大量不关心的就绪文件描述符

5 Java NIO

  • Java NIO 是从Java 1.4版本开始引入的一个新的IO API,可以替代标准的Java IO API
  • NIO 与原来的 IO 有同样的作用和目的,但使用方式完全不同,NIO 支持面向缓冲区的、基于通道的 IO 操作
  • NIO 在传输数据时,会在输入输出端之间建立通道,然后将数据放入到缓冲区中,缓冲区通过通道来传输数据(即通道负责传输,缓冲区负责存储


IO NIO
面向流(Stream) 面向缓冲区(Buffer),缓冲区的存储结构是数组
仅支持阻塞IO 支持阻塞、非阻塞IO

5.1 NIO 核心组件:缓冲区

  • 缓冲区的核心属性:四个指针 mark <= position <= limit <= capacity
属性 含义
capacity 缓冲区的容量,通过构造函数赋予,一旦设置,无法更改
limit 缓冲区的界限,位于 limit 后的数据不可读写
position 下一个读写位置的索引(类似PC寄存器)
mark 记录当前position的值,position被改变后,可以通过调用 reset() 方法恢复到mark的位置
  • 根据是否直接使用物理内存,缓冲区可以分为非直接缓冲区直接缓冲区

    • 非直接缓冲区使用 JVM 堆内存,会受到 GC 影响
    • 直接缓冲区使用物理内存,不会受 GC 影响,但是申请过大会造成严重内存占用
ByteBuffer.allocate(capacity);  // 获取非直接缓冲区
ByteBuffer.allocateDirect(capacity);  // 获取直接缓冲区

5.2 NIO 核心组件:Channel

  • Channel 表示 IO 源与目标打开的连接
  • 类似于传统的流,只不过 Channel 本身不能直接访问数据,只能与 Buffer 进行交互

5.3 NIO 核心组件:Selector

  • Selector 是 NIO 的核心,利用 Selector 可使一个单独的线程管理多个 Channel
  • 通过调用 Channel 的register方法可以绑定 Selector 并指定监听的事件类型

后端学习 - 操作系统相关推荐

  1. python硬件驱动_从零开始:手把手教你安装深度学习操作系统、驱动和各种python库!...

    原标题:从零开始:手把手教你安装深度学习操作系统.驱动和各种python库! 为了研究强化学习,最近购置了一台基于 Ubuntu 和英伟达 GPU 的深度学习机器.尽管目前在网络中能找到一些环境部署指 ...

  2. 大学四年Java后端学习路线规划,所有私藏资料我都贡献出来了,不看毕业肯定后悔!!!

    一定要走在学校前面自学,规划好自己的时间,按照自己的路线走. 大学四年Java后端学习路线规划,所有私藏资料我都贡献出来了,不看毕业肯定后悔!!! 学习路线与资源方法 一.第一件事,很重要!!! 二. ...

  3. Java——Web后端学习路线

    文章目录 Java后端学习路线 第一部分: Java基础 第二部分: Java高级 第三部分: JavaWEB 第四部分: 主流框架和项目管理 第五部分: 分布式 微服务 并行架构 第六部分 : De ...

  4. 前端转后端学习路线整理

    一.背景 本人是一名 Web 前端开发,技术栈是 Vue 和 React,不会 Node.之前学过,但是因为一些原因(比如没有使用场景,很多概念无法理解,学完就忘等)一直也没有掌握. 因为在 CMS ...

  5. 【后端学习】C++后端校招学习路线(学完必拿后端offer)

    C++后端校招准备以及个人补充面试常考题 文章目录 前言 一.学习方式 二.个人面经总结 什么是内核态和用户态,陷入内核态的方式?什么是中断? 用户态切换到内核态的3种方式 什么是中断? 如何解决缓存 ...

  6. 大厂招聘-校招生/实习生 后端学习路线-Java

    大厂招聘-校招生/实习生 后端学习路线-Java 我是一个Java后端开发人员,校招生,在面试过程中深感Java作为红海,找工作投简历的人那是一个多呀,打个比喻,100人找工作,20个是算法,10个是 ...

  7. 0909 学习操作系统

    在步入大三的学习中 这学期我要学习一门叫操作系统的科目.操作系统是一个非常重要的东西,因为每一个电子设备都需要有一个操作系统才能正常运行.根据我的了解操作系统是管理计算机系统的全部硬件资源包括软件资源 ...

  8. 学习操作系统,都有哪些硬核网站?

    一般很少有人推荐操作系统的网站吧......这几个网站来源于我平常的学习总结,也有一些是来源于网上优秀的回答,希望这几个网站能够助力你对操作系统有更深的认识. studytonight studyto ...

  9. 【java后端学习路线3】SSM+Linux+Git学习指南,985本海硕自学转码

    JAVA后端学习路线 路线总览 javase->Mysql->计算机网络->JavaWeb->Maven(1)->Spring->SpringMVC->Myb ...

最新文章

  1. oracle 常用命令大汇总
  2. 周末免费玩VR!Rift玩家的福利:BlazeRush赛车游戏
  3. python urllib.request 爬虫 数据处理-Python网络爬虫(基于urllib库的get请求页面)
  4. JS动态获取当前时间
  5. 动作分析 姿态估计_单人或多人的人体姿态骨架估计算法概述
  6. [网络安全自学篇] 九十.远控木马详解及APT攻击中的远控和防御
  7. sql server 2012远程链接的方法及步骤
  8. 跨Rollup转账应用HopProtocol将于4月份上线主网
  9. 过滤代码中的html标签
  10. 利用JS实现点击按钮后图片自动切换
  11. 分区表修复工具--DISKFIX
  12. HTML5期末大作业:直播网站设计——仿在线媒体歪秀直播官网模板html源码(11个页面) HTML+CSS+JavaScript 期末作业HTML代码...
  13. 02_Unity小窍门100条(中)
  14. 通过Python实现对xls表格按类别统计计数
  15. sklearn中StandardScaler()
  16. isPrime 判断素数的函数
  17. 2022汽车驾驶员(高级)考试模拟100题及在线模拟考试
  18. 全球及中国混合动力汽车产业投资决策与竞争态势研究报告2022版
  19. 【SpringBoot内容协商】
  20. 我们处于大数据时代,数据是从哪里来的?

热门文章

  1. Matplotlib基础02:散点图、折线图与柱状图
  2. python刷步数程序设计_乐心健康间接修改微信步数-Docker持久运行python脚本
  3. 有关c基础指针需要注意的几个点!
  4. scp时提示【Read-only file system】的解决方案
  5. 设计模式之工厂方法模式(附源码)
  6. 商贸批发进销存管理软件,仓库条码管理,库存管理。采购入库单,供应商档案管理。
  7. 2D灯光 Unity2021
  8. ai如何旋转画布_ai中怎么使用旋转工具制作旋转对称图?ai中使用旋转工具制作旋转对称图的方法...
  9. 数组旋转(上下对称,主对角线对称)
  10. 学术-几何:黑森错觉