IO综述·

阻塞模式

读写数据会发生阻塞现象。当用户线程发起IO请求之后,内核会查看数据检查就绪。如果没有就绪就会等待数据就绪。而用户线程会处于阻塞状态,用户线程交出CPU。当数据就绪之后,内核会将数据拷贝到用户线程,并返回结果给用户线程,用户线程才接触block状态,data=socket.read();如果数据没有就绪,就会一直阻塞在read方法。

非阻塞IO模型

当用户线程发起一个 read 操作后,并不需要等待,而是马上就得到了一个结果。如果结果是一个 error 时,它就知道数据还没有准备好,于是它可以再次发送 read 操作。一旦内核中的数据准备 好了,并且又再次收到了用户线程的请求,那么它马上就将数据拷贝到了用户线程,然后返回。 所以事实上,在非阻塞 IO 模型中,用户线程需要不断地询问内核数据是否就绪,也就说非阻塞 IO 不会交出 CPU,而会一直占用 CPU。典型的非阻塞 IO 模型一般如下:

while(true){
data = socket.read(); if(data!= error){ 处理数据
break;
}
}

但是对于非阻塞 IO 就有一个非常严重的问题,在 while 循环中需要不断地去询问内核数据是否就绪,这样会导致 CPU 占用率非常高,因此一般情况下很少使用 while 循环这种方式来读取数据。

同步与异步

从Linux操作系统层面上了解,Linux内核会将所有的外部设备当作一个文件来操作,Linux内核使用file descriptor对本地进行读/写,同样,Linux内核便使用socket file descriptor处理与socket相关的网络读写.I/O涉及两个系统对象,一个调用它的用户进程,另外一个是系统内核

  • 用户进程调用Read方法向内核发起读请求并等待就绪
  • 内核将要读取的数据复制到文件描述符说指向的内核缓冲区.
  • 内核将数据从内核缓存区复制到用户进程空间.
同步I/O

在系统内核准备好处理数据后,还需要等待内核将数据复制到用户进程,才能处理.

异步I/O

用户进程无须关心实际I/O的操作过程,只需在I/O完成后由内核接收通知,I/O操作全部由内核进程执行.

多路复用IO模型

多路复用 IO 模型是目前使用得比较多的模型。Java NIO 实际上就是多路复用 IO。在多路复用 IO 模型中,会有一个线程不断去轮询多个 socket 的状态,只有当 socket 真正有读写事件时,才真 正调用实际的 IO 读写操作。因为在多路复用 IO 模型中,只需要使用一个线程就可以管理多个 socket,系统不需要建立新的进程或者线程,也不必维护这些线程和进程,并且只有在真正有 socket 读写事件进行时,才会使用 IO 资源,所以它大大减少了资源占用。在 Java NIO 中,是通 过 selector.select()去查询每个通道是否有到达事件,如果没有事件,则一直阻塞在那里,因此这 种方式会导致用户线程的阻塞。多路复用 IO 模式,通过一个线程就可以管理多个 socket,只有当 socket 真正有读写事件发生才会占用资源来进行实际的读写操作。因此,多路复用 IO 比较适合连 接数比较多的情况。

另外多路复用IO为何比非阻塞IO模型的效率高是因为在非阻塞IO中,不断询问Socket状态是通过用户线程去进行的,而多路复用IO中是使用内核里面进行轮询socket状态。

不过要注意的是,多路复用 IO 模型是通过轮询的方式来检测是否有事件到达,并且对到达的事件 逐一进行响应。因此对于多路复用 IO 模型来说,一旦事件响应体很大,那么就会导致后续的事件 迟迟得不到处理,并且会影响新的事件轮询。

信号驱动IO模型

在信号驱动IO模型中,当用户线程发起一个IO请求操作时候,会给对应的socket注册一个信号函数,然后用户线程会继执行,当内核数据就绪时候会发送一个信号给用户线程,用户收到信号后,遍在信号函数中调用IO读写操作来进行实际的IO请求操作。

异步IO模型

异步IO模型才是最理想的IO模型,在异步模型中,当用户线程发起read操作之后,立刻就可以开始做其他的事情,而另一方面,从内核的角度,当他受到一异步读的时候,他会立刻返回,说明read请求已经成功发起,因此不会对用户线程产生任何block。然后,内核会等待数据准备完成,然后拷贝到用户线程。当这一切都完成之后,内核会给用户线程发送一个信号,告诉他read完毕。在异步 IO 模型中,IO 操作的两个阶段都不会阻塞用户线程,这两个阶段都是由内核自动完 成,然后发送一个信号告知用户线程操作已完成。当用户线程接收到信号表示数据 已经就绪,然后需要用户线程调用 IO 函数进行实际的读写操作;而在异步 IO 模型中,收到信号 表示 IO 操作已经完成,不需要再在用户线程中调用 IO 函数进行实际的读写操作。

注意,异步 IO 是需要操作系统的底层支持,在 Java 7 中,提供了 Asynchronous IO。

Java IO包

Java Nio(Non-blocking I/O,在Java领域,也称为New I/O)

是一种同步非阻塞的I/O模型,也是I/O多路复用的基础,已经被越来越多地应用到大型应用服务器,成为解决高并发与大量连接、I/O处理问题的有效方式。

  • Channel 通道
  • Buffer 缓冲区
  • Selector

传统IO基于字符流和字节流,而NIO是面向缓冲区的。

Nio网络模型

NIO缓冲区

Java IO 面向流意味着每次从流中读一个或多个字节,直至读取所有字节,它们没有被缓存在任何 地方。此外,它不能前后移动流中的数据。如果需要前后移动从流中读取的数据,需要先将它缓 存到一个缓冲区。NIO 的缓冲导向方法不同。数据读取到一个它稍后处理的缓冲区,需要时可在 缓冲区中前后移动。这就增加了处理过程中的灵活性。 IO 的各种流是阻塞的。这意味着,当一个线程调用 read() 或 write()时,该线程被阻塞,直到有 一些数据被读取,或数据完全写入。该线程在此期间不能再干任何事情了。 NIO 的非阻塞模式, 使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可 用时,就什么都不会获取。而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以 继续做其他的事情。 非阻塞写也是如此。一个线程请求写入一些数据到某通道,但不需要等待它 完全写入,这个线程同时可以去做别的事情。 线程通常将非阻塞 IO 的空闲时间用于在其它通道上。

channel

首先说一下 Channel,国内大多翻译成“通道”。Channel 和 IO 中的 Stream(流)是差不多一个 等级的。只不过 Stream 是单向的,譬如:InputStream, OutputStream,而 Channel 是双向。

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

>这里看名字就可以猜出个所以然来:分别可以对应文件 IO、UDP 和 TCP(Server 和 Client)。

Buffer

Buffer,故名思意,缓冲区,实际上是一个容器,是一个连续数组。Channel 提供从文件、 网络读取数据的渠道,但是读取或写入的数据都必须经由 Buffer。

Selector

Selector 类是 NIO 的核心类,Selector 能够检测多个注册的通道上是否有事件发生,如果有事 件发生,便获取事件然后针对每个事件进行相应的响应处理。这样一来,只是用一个单线程就可 以管理多个通道,也就是管理多个连接。这样使得只有在连接真正有读写事件发生时,才会调用 函数来进行读写,就大大地减少了系统开销,并且不必为每个连接都创建一个线程,不用去维护 多个线程,并且避免了多线程之间的上下文切换导致的开销。

用一个死循环选择就绪的事件,会执行系统调用(Linux 2.6之前是select、poll,2.6之后是epoll,Windows是IOCP),还会阻塞的等待新事件的到来。新事件到来的时候,会在selector上注册标记位,标示可读、可写或者有连接到来。

注意,select是阻塞的,无论是通过操作系统的通知(epoll)还是不停的轮询(select,poll),这个函数是阻塞的。所以你可以放心大胆地在一个while(true)里面调用这个函数而不用担心CPU空转。

一个Selector可以同时注册、监听和轮询成千上百个Channel,一个用于处理I/O的线程可以同时并发处理多个客户端的连接,具体数量取决于进程可以的最大文件句柄数.由于处理I/O线程数大幅度减少.所以缺少竞争CPU.

最常见的Selector监听时间有以下几种

  • 客户端连接服务端事件,对应的SelectorKey为OP_CONNNECT
  • 服务端接收客户端连接事件,对应的SelectorKey为OP_ACCEPT
  • 读事件,对应的SelectorKey为OP_READ
  • 写事件,对应的SelectorKey为OP_WRITE

Proactor与Reactor模型

Reactor模型

 涉及到事件分发器的两种模式称为:Reactor和Proactor。 Reactor模式是基于同步I/O的,而Proactor模式是和异步I/O相关的。在Reactor模式中,事件分发器等待某个事件或者可应用或个操作的状态发生(比如文件描述符可读写,或者是socket可读写),事件分发器就把这个事件传给事先注册的事件处理函数或者回调函数,由后者来做实际的读写操作。

在Reactor模式中,事件分离在Socket读写操作准备就绪之后,会将就绪事件传递给相应的处理器由其完成实际的读写工作.在NIO事件分离器有Selector担任,它负责查询I/O是否就绪,并在I/O就绪后调用预先注册的相关处理器进行处理

在Reactor中实现读
  • 注册读就绪事件和相应的事件处理器。
  • 事件分发器等待事件。
  • 事件到来,激活分发器,分发器调用事件对应的处理器。
  • 事件处理器完成实际的读操作,处理读到的数据,注册新的事件,然后返还控制权。
Proactor模型

在Proactor模式中,事件处理者(或者代由事件分发器发起)直接发起一个异步读写操作(相当于请求),而实际的工作是由操作系统来完成的。发起时,需要提供的参数包括用于存放读到数据的缓存区、读的数据大小或用于存放外发数据的缓存区,以及这个请求完后的回调函数等信息。事件分发器得知了这个请求,它默默等待这个请求的完成,然后转发完成事件给相应的事件处理者或者回调。举例来说,在Windows上事件处理者投递了一个异步IO操作(称为overlapped技术),事件分发器等IO Complete事件完成。这种异步模式的典型实现是基于操作系统底层异步API的,所以我们可称之为“系统级别”的或者“真正意义上”的异步,因为具体的读写是由操作系统代劳的。

在Proactor中实现读:
  • 处理器发起异步读操作(注意:操作系统必须支持异步IO)。在这种情况下,处理器无视IO就绪事件,它关注的是完成事件。
  • 事件分发器等待操作完成事件。
  • 在分发器等待过程中,操作系统利用并行的内核线程执行实际的读操作,并将结果数据存入用户自定义缓冲区,最后通知事件分发器读操作完成。
  • 事件分发器呼唤处理器。
  • 事件处理器处理用户自定义缓冲区中的数据,然后启动一个新的异步操作,并将控制权返回事件分发器。

两个模式的相同点,都是对某个I/O事件的事件通知(即告诉某个模块,这个I/O操作可以进行或已经完成)。在结构上,两者也有相同点:事件分发器负责提交IO操作(异步)、查询设备是否可操作(同步),然后当条件满足时,就回调handler;不同点在于,异步情况下(Proactor),当回调handler时,表示I/O操作已经完成;同步情况下(Reactor),回调handler时,表示I/O设备可以进行某个操作(can read 或 can write)。

Java中的IO

Java中的IO分为BIO、NIO和AIO,Java目前并不支持异步I/O,BIO对应的阻塞同步IO,NIO和AIO对应非阻塞同步I/O.

NIO

ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
serverChannel.socket().bind(new InetSocketAddress(port));
Selector selector = Selector.open();
serverChannel.register(selector,SelectionKey.OP_ACCEPT);

只需向通道注册,SelectionKey.OP_ACCEPT事件即可,当OP_ACCEPT事件未达到的时候,selector.select()将一直阻塞.

下面使用NIO处理同步非阻塞I/O请求的服务端代码

while(!stopped()){selector.select();Iterator<SelectionKey> selectorKeys = selector.selectedKeys().iterators();while(selectionKeys.hasNext()){SelectionKey key = selectionKeys.next();selectionsKeys.remove();if(key.isAcceptable()){SeverSocketChannel server  = (ServerSocketChannel) key.channel();SocketSocketChannel channel = server.accept();channel.configureBlocking(false);channel.register(selector,SelectionKey.OP_READ);}else if(key.isReadable()){//通过buffer处理读操作}}
}
客户端代码

SocketChannel channel = SocketChannel.open();
channel.configureBlocking(false);
Selector selector = Selector.open();
channel.connect(new InetSocketAddress(serverIP,serverPort));
channel.register(selector,SelectionKey.OP_CONNECT))

同步非阻塞的I/O

while(!stopped()){selector.select();Iterator<SelectionKey> selectorKeys = selector.selectedKeys().iterators();while(selectionKeys.hasNext()){SelectionKey key = selectionKeys.next();selectionsKeys.remove();if(key.isConnectable()){SocketChannel server  = (SocketChannel) key.channel();if(channel.isConnectionPending()){channel.finishConnect();}channel.configureBlocking(false);channel.register(selector,SelectionKey.OP_READ);}else if(key.isReadable()){//通过buffer处理读操作}}
}

AIO

Java 7推出了NIO.2 也是AIO.AIO采用Proactor模式实现I/O多路复用技术的另一种模式,它主要是用于异步I/O处理.Proactor模式与Reactor模式类似,他们都是使用事件分离器分离读/写与任务分发.相对Reactor模式更牛逼,它不用关心如何处理读/写事件,而是由操作系统将读/写执行完通知回调函数,回调方法只关系自己需要处理.Reactor模式的回调方法是在读/写操作执行之前被调用,由应用开发这者负责读写事件,而Proactor模式的回调则是在读/写操作完毕后被调用.

AIO在window和Linux有不同的实现,windows的中iocp支持真正的异步I/O,Linux的I/O模型还是使用poll或epoll,并将API封装成为异步I/O的样子.本质还是同步非阻塞的I/O

AIO有两种使用方式:

  • 简单的将来式

将来式使用java.util.concurrent.Future对结果进行访问.在提交一个I/O请求之后返回一个Future对象,然后通过检查Future的状态得到操作完成或者失败.调用Future的get是阻塞当前进程或获取消息,所以不推荐用

  • 复杂的回调式

回调是AIO的推荐方式,NIO2提供java.niochannels.CompletionHandler作为回调接口,该接口定义了completed和failed的方法,用于应用开发这自己覆盖并实现业务逻辑.I/O操作结束之后,系统会自动调用CompletionHandler的completed或failed方法来结束回调.

AIO处理同步非阻塞I/O请求的服务端核心代码

AsynchronousChannelGroup channelGroup = AsynchronousChannelGroup.withThreadPool(Executors.newFixedThreadPool(10));
final AsynchronousServerSocketChannel severChannel = AsynchronousServerSocketChannel.open(channelGroup).bind(
new InetSocketAddress(port));
severChannel.accept(null,new CompletionHandler<AsynchronousSocketChannel,Void>(){@Overridepublic void completed(AsynchronousSocketChannel channel,Void attachement){ByteBuffer buffer = ByteBuffer.allocate(1024);Future<Integter> future = channel.read(buffer);//执行业务逻辑serverChannel.accept(null,this);}@Overridepublic void failed(Throwable exc,Void attachment){exec.printStackTrace();}
}

没有Selector的轮询需要处理,AIO采用AsynchronousChannelGroup的线程池处理事务,这些事务主要包括等待I/O事件,处理数据以及分发至各种的回调函数.通过匿名内部类的方式注册事件回调方法,覆盖completed方法用于处理I/O的后续业务逻辑,方法最后再调用accept方法接受下一次请求,覆盖failed方法用于处理I/O中产生的错误.

AIO客户端代码更加简单

AsynchronousSocketChannel channelClient = AsynchronousSocketChannel.open();
channelClient.connect(new InetSocketAddress(serverIp,serverPort)).get();
ByteBuffer buffer = ByteBuffer.allocate(1024);
channelClient.read(buffer,null, new CompletionHandler<Integer,Void>(){@Overridepublic void completed(Integer result,Void attachment){//执行业务逻辑}@Overridepublic void failed(Throwable exc,Void attachment){exc.printStackTrace();}
}

AIO虽然在编程接口上比起NIO简单,但是由于其使用的IO模型与NIO是一样的,由于AIO出现的事件较晚,但是没有带来多少性能提升.

总结

  • 阻塞和非阻塞、同步与异步都是I/O的不同维度
  • 同步I/O和异步I/O针对的是内核,而阻塞I/O和非阻塞I/O针对的是调用它的函数.
  • 同步I/O在实际中比较常用的,select、poll、epoll是Linux系统使用最经常使用的I/O多路复用机制.I/O多路复用机制.I/O多路复制可以监视多个描述符,一旦某个描述符读/写操作就绪,便可以通知程序进行相应的读写/写操作.他们都需要在读/写事件就绪后再进行读/写操作,内核向用户进程复制数据的过程仍然是阻塞的.但异步无须自己负责读/写操作,它负责把数据从内核复制到用户空间.
  • 总体来说判断同步I/O还是异步I/O.主要关注内核数据从内核复制到空间.
  • NIO采用Reactor模式来时实现I/O操作.

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/867.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KMP算法(Python)

进阶的做法就是KMP算法&#xff0c;当然暴力也能ac。 KMP主要用一个nex列表&#xff0c;nex[i]存储&#xff08;模式串needle中&#xff09;从第0个到i个字符串s中的一个相等前后缀的最大长度。比如说对于aabaa来说&#xff0c;最大长度应该是&#xff08;前缀aa&#xff09;和…

Linux下SPI设备驱动实验:验证读写SPI设备中数据的函数功能

一. 简介 前面文章实现了 SPI设备驱动框架&#xff0c;并在此基础上添加了字符设备驱动框架&#xff0c;实现了读 / 写SPI设备中数据的函数&#xff0c;文章如下&#xff1a; Linux下SPI设备驱动实验&#xff1a;向SPI驱动框架中加入字符设备驱动框架代码-CSDN博客 Linux下…

算法打卡day51|单调栈篇02| Leetcode 503.下一个更大元素II、42. 接雨水

算法题 Leetcode 503.下一个更大元素II 题目链接:503.下一个更大元素II 大佬视频讲解&#xff1a;503.下一个更大元素II视频讲解 个人思路 这道题和之前496.下一个更大元素 I 差不多&#xff0c;只是这道题需要循环数组&#xff0c;那就在遍历的过程中模拟走两遍nums就行&a…

本地主机搭建服务器后如何让外网访问?快解析内网端口映射

本地主机搭建应用、部署服务器后&#xff0c;在局域网内是可以直接通过计算机内网IP网络地址进行连接访问的&#xff0c;但在外网电脑和设备如何访问呢&#xff1f;由于内网环境下&#xff0c;无法提供公网IP使用&#xff0c;外网访问内网就需要一个内外网转换的介质。这里介绍…

在PostgreSQL中如何创建和使用自定义函数,包括内置语言(如PL/pgSQL)和外部语言(如Python、C等)?

文章目录 一、使用内置语言 PL/pgSQL 创建自定义函数示例代码使用方法 二、使用外部语言 Python 创建自定义函数安装 PL/Python 扩展示例代码使用方法 三、使用外部语言 C 创建自定义函数编写 C 代码编译为共享库在 PostgreSQL 中注册函数注意事项 总结 PostgreSQL 是一个强大的…

CSS基础:table的4个标签的样式详解(6000字长文!附案例)

你好&#xff0c;我是云桃桃。 一个希望帮助更多朋友快速入门 WEB 前端的程序媛。 云桃桃-大专生&#xff0c;一枚程序媛&#xff0c;感谢关注。回复 “前端基础题”&#xff0c;可免费获得前端基础 100 题汇总&#xff0c;回复 “前端工具”&#xff0c;可获取 Web 开发工具合…

记一次中间件宕机以后持续请求导致应用OOM的排查思路(server.max-http-header-size属性配置不当的严重后果)

一、背景 最近有一次在系统并发比较高的时候&#xff0c;数据库突然发生了故障&#xff0c;导致大量请求失败&#xff0c;在数据库宕机不久&#xff0c;通过应用日志可以看到系统发生了OOM。 二、排查 初次看到这个现象的时候&#xff0c;我还是有点懵逼的&#xff0c;数据库…

项目二:学会使用python爬虫请求库(小白入门级)

上一章已经了解python爬虫的基本知识&#xff0c;这一次让我们一起来学会如何使用python请求库爬取目标网站的信息。当然这次爬虫之旅相信我能给你带来不一样的体验。 目录 一、安装requests 库 简介 安装 步骤 1.requests的基本使用3步骤 2.查看所使用编码 3.设置编码…

Redis中BitMap在钉钉机器人中的应用

性能分析 数据库中有1000w用户&#xff0c;每个用户签到一次&#xff0c;对应两个字段 连续签到多少次 、签到时间。 签到时间字段占用10个字节&#xff0c;连续签到多少天 占用5个字节&#xff08;假设一个用户能活100年&#xff0c;每天都签到&#xff0c;一个用户最多签到3…

前端框架技术革新历程:从原生DOM操作、数据双向绑定到虚拟DOM等框架原理深度解析,Web开发与用户体验的共赢

前端的发展与前端框架的发展相辅相成&#xff0c;形成了相互驱动、共同演进的关系。前端技术的进步不仅催生了前端框架的产生&#xff0c;也为其发展提供了源源不断的动力。 前端的发展 前端&#xff0c;即Web前端&#xff0c;是指在创建Web应用程序或网站过程中负责用户界面…

使用Azure AI Search和LlamaIndex构建高级RAG应用

RAG 是一种将公司信息合并到基于大型语言模型 &#xff08;LLM&#xff09; 的应用程序中的常用方法。借助 RAG&#xff0c;AI 应用程序可以近乎实时地访问最新信息&#xff0c;团队可以保持对其数据的控制。 在 RAG 中&#xff0c;您可以评估和修改各个阶段以改进结果&#x…

2024年思维100春季线上赛今天开赛,来做历年真题700道感受下难度

今天是2024年4月20日&#xff0c;下午13点开始2024年春季思维100活动第一阶段的线上比赛就按年级正式开始了&#xff0c;先祝愿所有的参赛选手考出好成绩&#xff01; 这次没有参赛的孩子们也可以来做做历年的思维100真题&#xff0c;看看思维100考什么、怎么考、难度怎么样。如…

ProgressFlowmon的confluence接口存在任意命令执行漏洞(CVE-2024-2389)

声明&#xff1a; 本文仅用于技术交流&#xff0c;请勿用于非法用途 由于传播、利用此文所提供的信息而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#xff0c;文章作者不为此承担任何责任。 简介 ProgressFlowmon是一整套用于网络映射、应用程序性能…

C++ | Leetcode C++题解之第29题两数相除

题目&#xff1a; 题解&#xff1a; class Solution { public:int divide(int dividend, int divisor) {// 考虑被除数为最小值的情况if (dividend INT_MIN) {if (divisor 1) {return INT_MIN;}if (divisor -1) {return INT_MAX;}}// 考虑除数为最小值的情况if (divisor I…

【计算机组成原理】运算方法和运算器

数据与文字的表示方法 1. 数据格式1.1 定点数表示方法1.1.1 定点小数1.1.2 定点整数 1.2 浮点数表示方法1.2.1 浮点数表示1.2.2 浮点数的规格化1.2.2.1 尾数为原码表示的规格化1.2.2.2 尾数为补码表示的规格化 1.2.3 IEEE754标准⭐ 1.3 十进制数串的表示方法1.3.1 字符串形式1.…

Tensorflow2.0笔记 - 使用卷积神经网络层做CIFA100数据集训练(类VGG13)

本笔记记录CNN做CIFAR100数据集的训练相关内容&#xff0c;代码中使用了类似VGG13的网络结构&#xff0c;做了两个Sequetial&#xff08;CNN和全连接层&#xff09;&#xff0c;没有用Flatten层而是用reshape操作做CNN和全连接层的中转操作。由于网络层次较深&#xff0c;参数量…

centos搭建yum源

目录 1.createrepo简介 2.repo搭建思路 3.安装 4.使用 1.createrepo简介 createrepo 是一个用于创建 RPM 包的工具&#xff0c;它可以帮助你创建一个本地的 YUM 仓库。createrepo 并不是用于运行 YUM 仓库服务的软件&#xff0c;而是用来生成仓库的元数据&#xff0c;使得…

区分软件成熟度模型集成的五个等级

概念讲解 软件成熟度模型集成&#xff08;CMMI&#xff0c;Capability Maturity Model Integration&#xff09;是一种评估和改进软件开发过程的模型。CMMI的五个成熟度等级分别是&#xff1a; 初始级&#xff08;Level 1 - Initial&#xff09;&#xff1a;在这个等级&#x…

【Taro3踩坑日记】找不到sass的类型定义文件

问题截图如下&#xff1a;找不到sass的类型定义文件 解决办法&#xff1a; 1、npm i types/sass1.43.1 2、然后配置 TypeScript 编译选项&#xff1a;确保 TypeScript 编译器能够识别 Sass 文件&#xff0c;并正确处理它们。

PyTorch的核心概念

&#x1f49d;&#x1f49d;&#x1f49d;欢迎莅临我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」…