Linux/Unix常见IO模型

news2025/1/24 4:52:34

阻塞(Blocking I/O)非阻塞(Non-Blocking I/O)IO多路复用(I/O Multiplexing)信号驱动 I/O(Signal Driven I/O)(不常用)和异步(Asynchronous I/O)。网络IO操作主要涉及到内核进程,其主要分为两个过程:

  • 内核等待数据可操作(可读或可写)——阻塞与非阻塞
  • 内核与进程之间数据的拷贝——同步与异步

基础概念

在这里插入图片描述
I/O请求可以分为两个阶段,分别为调用阶段和执行阶段。

  • 第一个阶段为I/O调用阶段,即用户进程向内核发起系统调用
  • 第二个阶段为I/O执行阶段。此时,内核等待I/O请求处理完成返回。该阶段分为两个过程:首先等待数据就绪,并写入内核缓冲区;随后将内核缓冲区数据拷贝至用户态缓冲区

① 阻塞(Blocking)和非阻塞(Non-blocking)

阻塞和非阻塞发生在内核等待数据可操作(可读或可写)时,指做事时是否需要等待应答。

  • 阻塞: 内核检查数据不可操作,则不立即返回
  • 非阻塞: 内核检查数据不可操作,则立即返回

② 同步(Synchronous)和异步(Asynchronous)

同步和异步发生在内核与进程交互时,进程触发IO操作后是否需要等待或轮询查看结果。

  • 同步: 触发IO操作 → 等待或轮询查看结果
  • 异步: 触发IO操作 → 直接返回去做其它事,IO处理完后内核主动通知进程

阻塞I/O(BIO)

阻塞IO情况下,当用户调用read后,用户线程会被阻塞,等内核数据准备好并且数据从内核缓冲区拷贝到用户态缓存区后read才会返回。阻塞分两个阶段:

  • 等待CPU把数据从磁盘读到内核缓冲区
  • 等待CPU把数据从内核缓冲区拷贝到用户缓冲区
    在这里插入图片描述
    应用进程向内核发起 I/O 请求,发起调用的线程一直等待内核返回结果。一次完整的 I/O 请求称为BIO(Blocking IO,阻塞 I/O),所以 BIO 在实现异步操作时,只能使用多线程模型,一个请求对应一个线程。但是,线程的资源是有限且宝贵的,创建过多的线程会增加线程切换的开销。

非阻塞I/O(NIO)

非阻塞的 read 请求在数据未准备好的情况下立即返回,可以继续往下执行,此时应用程序不断轮询内核,询问数据是否准备好,当数据没有准备好时,内核立即返回EWOULDBLOCK错误。直到数据准备好后,内核将数据拷贝到应用程序缓冲区,read 请求才获取到结果。

注意:这里最后一次 read 调用获取数据的过程,是一个同步的过程,是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程
在这里插入图片描述
注意,这里最后一次 read 调用,获取数据的过程,是一个同步的过程,是需要等待的过程。这里的同步指的是内核态的数据拷贝到用户程序的缓存区这个过程。

应用进程向内核发起 I/O 请求后不再会同步等待结果,而是会立即返回,通过轮询的方式获取请求结果。NIO 相比 BIO 虽然大幅提升了性能,但是轮询过程中大量的系统调用导致上下文切换开销很大。所以,单独使用非阻塞 I/O 时效率并不高,而且随着并发量的提升,非阻塞 I/O 会存在严重的性能浪费。

I/O多路复用

非阻塞情况下无可用数据时,应用程序每次轮询内核看数据是否准备好了也耗费CPU,能否不让它轮询,当内核缓冲区数据准备好了,以事件通知当机制告知应用进程数据准备好了呢?应用进程在没有收到数据准备好的事件通知信号时可以忙写其他的工作。此时IO多路复用就派上用场了。像select、poll、epoll 都是I/O多路复用的具体的实现。
在这里插入图片描述
多路复用实现了一个线程处理多个 I/O 句柄的操作。多路指的是多个数据通道,复用指的是使用一个或多个固定线程来处理每一个 Socket。select、poll、epoll 都是 I/O 多路复用的具体实现,线程一次 select 调用可以获取内核态中多个数据通道的数据状态。多路复用解决了同步阻塞 I/O 和同步非阻塞 I/O 的问题,是一种非常高效的 I/O 模型。

信号驱动I/O

无论 readsend阻塞I/O,还是 非阻塞I/O 都是同步调用。因为在 read 调用时,内核将数据从内核空间拷贝到用户空间的过程都是需要等待的,即这个过程是同步的,如果内核实现的拷贝效率不高,read 调用就会在这个同步过程中等待比较长的时间。
在这里插入图片描述
信号驱动 I/O 并不常用,它是一种半异步的 I/O 模型。在使用信号驱动 I/O 时,当数据准备就绪后,内核通过发送一个 SIGIO 信号通知应用进程,应用进程就可以开始读取数据了。

异步I/O

真正的异步 I/O 是内核数据准备好数据从内核态拷贝到用户态 这两个过程都不用等待。

当我们发起 aio_read (异步 I/O) 之后,就立即返回,内核自动将数据从内核空间拷贝到用户空间,这个拷贝过程同样是异步的,内核自动完成的,和前面的同步操作不一样,应用程序并不需要主动发起拷贝动作。过程如下图:
在这里插入图片描述
异步 I/O 最重要的一点是从内核缓冲区拷贝数据到用户态缓冲区的过程也是由系统异步完成,应用进程只需要在指定的数组中引用数据即可。异步 I/O 与信号驱动 I/O 这种半异步模式的主要区别:信号驱动 I/O 由内核通知何时可以开始一个 I/O 操作,而异步 I/O 由内核通知 I/O 操作何时已经完成。

Reactor模式

Reactor 模式 即 I/O 多路复用监听事件,收到事件后根据事件类型分配(Dispatch)给某个进程/线程。其主要由 Reactor处理资源池 两个核心部分组成:

  • Reactor:负责监听和分发事件。事件类型包含连接事件、读写事件
  • 处理资源池:负责处理事件。如:read -> 业务逻辑 -> send

Reactor 模式是灵活多变的,可以应对不同的业务场景,灵活在于:

  • Reactor 的数量可以只有一个,也可以有多个
  • 处理资源池可以是单个进程/线程,也可以是多个进程/线程

将上面的两个因素排列组设一下,理论上就可以有 4 种方案选择:

  • 单 Reactor 单进程/线程
  • 单 Reactor 多进程/线程
  • 多 Reactor 单进程/线程:相比 单Reactor单进程/线程 方案不仅复杂而且没有性能优势,因此可以忽略
  • 多 Reactor 多进程/线程

单Reactor单进程/单线程

一般来说,C 语言实现的是单Reactor单进程的方案,因为 C 语编写完的程序,运行后就是一个独立的进程,不需要在进程中再创建线程。而 Java 语言实现的是「单 Reactor 单线程」的方案,因为 Java 程序是跑在 Java 虚拟机这个进程上面的,虚拟机中有很多线程,我们写的 Java 程序只是其中的一个线程而已。以下是「单 Reactor单进程」的方案示意图:
在这里插入图片描述
可以看到进程里有 ReactorAcceptorHandler 这三个对象:

  • Reactor 对象的作用是监听和分发事件
  • Acceptor 对象的作用是获取连接
  • Handler 对象的作用是处理业务

对象里的 selectacceptreadsend 是系统调用函数,dispatch业务处理 是需要完成的操作,其中 dispatch 是分发事件操作。

工作流程

  • Reactor 对象通过 select (IO多路复用接口) 监听事件,收到事件后通过 dispatch 进行分发,具体分发给 Acceptor 对象还是 Handler 对象,还要看收到的事件类型
  • 如果是连接建立的事件,则交由 Acceptor 对象进行处理,Acceptor 对象会通过 accept 方法 获取连接,并创建一个 Handler 对象来处理后续的响应事件
  • 如果不是连接建立事件, 则交由当前连接对应的 Handler 对象来进行响应
  • Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程

优缺点

  • 优点

    • 因为全部工作都在同一个进程内完成,所以实现起来比较简单
    • 不需要考虑进程间通信,也不用担心多进程竞争
  • 缺点

    • 因为只有一个进程,无法充分利用 多核 CPU 的性能
    • Handler对象在业务处理时,整个进程是无法处理其它连接事件,如果业务处理耗时比较长,那么就造成响应的延迟

使用场景

单Reactor单进程的方案不适用计算机密集型的场景只适用于业务处理非常快速的场景。如:Redis 是由 C 语言实现的,它采用的正是「单Reactor单进程」的方案,因为 Redis 业务处理主要是在内存中完成,操作的速度是很快的,性能瓶颈不在 CPU 上,所以 Redis 对于命令的处理是单进程的方案。

单Reactor多线程/多进程

如果要克服单 Reactor 单线程/单进程方案的缺点,那么就需要引入多线程/多进程,这样就产生了单Reactor多线程/多进程的方案。具体方案的示意图如下:
在这里插入图片描述
工作流程

  • Reactor 对象通过 select (IO 多路复用接口) 监听事件,收到事件后通过 dispatch 进行分发,具体分发给 Acceptor 对象还是 Handler 对象,还要看收到的事件类型
  • 如果是连接建立的事件,则交由 Acceptor 对象进行处理,Acceptor 对象会通过 accept 方法获取连接,并创建一个 Handler 对象来处理后续的响应事件
  • 如果不是连接建立事件, 则交由当前连接对应的 Handler 对象来进行响应
  • Handler 对象不再负责业务处理,只负责数据的接收和发送,Handler 对象通过 read 读取到数据后,会将数据发给子线程里的 Processor 对象进行业务处理
  • 子线程里的 Processor 对象就进行业务处理,处理完后,将结果发给主线程中的 Handler 对象,接着由 Handler 通过 send 方法将响应结果发送给 client

单Reator多线程

  • 优势:能够充分利用多核 CPU 的能力

  • 缺点:带来了多线程竞争资源问题(如需加互斥锁解决)

单Reactor多进程

  • 缺点
    • 需要考虑子进程和父进程的双向通信
    • 进程间通信远比线程间通信复杂

另外,单Reactor 的模式还有个问题,因为一个 Reactor 对象承担所有事件的 监听响应 ,而且只在主线程中运行,在面对瞬间高并发的场景时,容易成为性能瓶颈。

多Reactor多进程/多线程

要解决 单Reactor 的问题,就是将 单Reactor 实现成 多Reactor,这样就产生了 多Reactor多进程/线程 方案。其方案的示意图如下(以线程为例):
在这里插入图片描述
工作流程

  • 主线程中的 MainReactor 对象通过 select 监控连接建立事件,收到事件后通过 Acceptor 对象中的 accept 获取连接,将新的连接分配给某个子线程
  • 子线程中的 SubReactor 对象将 MainReactor 对象分配的连接加入 select 继续进行监听,并创建一个 Handler 用于处理连接的响应事件
  • 如果有新的事件发生时,SubReactor 对象会调用当前连接对应的 Handler 对象来进行响应
  • Handler 对象通过 read -> 业务处理 -> send 的流程来完成完整的业务流程

方案优势

多Reactor多线程 的方案虽然看起来复杂的,但是实际实现时比 单Reactor多线程的方案要简单的多,原因如下:

  • 分工明确:主线程只负责接收新连接,子线程负责完成后续的业务处理
  • 主线程和子线程的交互很简单:主线程只需要把新连接传给子线程,子线程无须返回数据,直接就可以在子线程将处理结果发送给客户端

应用场景

  • 多Reactor多线程:开源软件 NettyMemcache

  • 多Reactor多进程:开源软件 Nginx。不过 Nginx 方案与标准的多Reactor多进程有些差异,具体差异:

    • 主进程仅用来初始化 socket,并没有创建 mainReactor 来 accept 连接,而由子进程的 Reactor 来 accept 连接
    • 通过锁来控制一次只有一个子进程进行 accept(防止出现惊群现象),子进程 accept 新连接后就放到自己的 Reactor 进行处理,不会再分配给其他子进程

Proactor模式

Reactor 和 Proactor 的区别

  • Reactor 是非阻塞同步网络模式,感知的是就绪可读写事件
    • 在每次感知到有事件发生(比如可读就绪事件)后,就需要应用进程主动调用 read 方法来完成数据的读取,也就是要应用进程主动将 socket 接收缓存中的数据读到应用进程内存中,这个过程是同步的,读取完数据后应用进程才能处理数据
    • 简单理解:来了事件(有新连接、有数据可读、有数据可写)操作系统通知应用进程,让应用进程来处理(从驱动读取到内核以及从内核读取到用户空间)
  • Proactor 是异步网络模式, 感知的是已完成的读写事件
    • 在发起异步读写请求时,需要传入数据缓冲区的地址(用来存放结果数据)等信息,这样系统内核才可以自动帮我们把数据的读写工作完成,这里的读写工作全程由操作系统来做,并不需要像 Reactor 那样还需要应用进程主动发起 read/write 来读写数据,操作系统完成读写工作后,就会通知应用进程直接处理数据
    • 简单理解:来了事件(有新连接、有数据可读、有数据可写)操作系统来处理(从驱动读取到内核,从内核读取到用户空间),处理完再通知应用进程

无论是 Reactor,还是 Proactor,都是一种基于「事件分发」的网络编程模式,区别在于 Reactor 模式是基于「待完成」的 I/O 事件,而 Proactor 模式则是基于「已完成」的 I/O 事件

Proactor 模式的示意图如下:
在这里插入图片描述
工作流程

  • Proactor Initiator 负责创建 Proactor 和 Handler 对象,并将 Proactor 和 Handler 都通过
  • Asynchronous Operation Processor 注册到内核
  • Asynchronous Operation Processor 负责处理注册请求,并处理 I/O 操作;
  • Asynchronous Operation Processor 完成 I/O 操作后通知 Proactor
  • Proactor 根据不同的事件类型回调不同的 Handler 进行业务处理
  • Handler 完成业务处理

平台支持

  • Linux:在 Linux 下的 异步I/O 是不完善的,aio 系列函数是由 POSIX 定义的异步操作接口,不是真正的操作系统级别支持的,而是在用户空间模拟出来的异步。并且仅仅支持基于本地文件的 aio 异步操作,网络编程中的 socket 是不支持的,这也使得基于 Linux 的高性能网络程序都是使用 Reactor 方案
  • Windows :在 Windows 下实现了一套完整的支持 socket 的异步编程接口,这套接口就是 IOCP,是由操作系统级别实现的 异步I/O,真正意义上 异步I/O,因此在 Windows 里实现高性能网络程序可以使用效率更高的 Proactor 方案。

select/poll/epoll

在这里插入图片描述
注意遍历相当于查看所有的位置,回调相当于查看对应的位置。

select

在这里插入图片描述
select 本质上是通过设置或者检查存放 fd 标志位的数据结构来进行下一步处理。

缺点

  • 单个进程可监视的fd数量被限制。能监听端口的数量有限,数值存在文件:cat /proc/sys/fs/file-max
  • 需要维护一个用来存放大量fd的数据结构。这样会使得用户空间和内核空间在传递该结构时复制开销大
  • 对fd进行扫描时是线性扫描fd剧增后,IO效率较低,因为每次调用都对fd进行线性扫描遍历,所以随着fd的增加会造成遍历速度慢的性能问题
  • select()函数的超时参数在返回时也是未定义的。考虑到可移植性,每次在超时之后在下一次进入到select之前都需要重新设置超时参数

优点

  • select()的可移植性更好。在某些Unix系统上不支持poll()
  • select()对于超时值提供了更好的精度:微秒。而poll是毫秒

poll

在这里插入图片描述
poll本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态,如果设备就绪则在设备等待队列中加入一项并继续遍历,如果遍历完所有fd后没有发现就绪设备,则挂起当前进程,直到设备就绪或者主动超时,被唤醒后它又要再次遍历fd。这个过程经历了多次无谓的遍历。poll还有一个特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。

缺点

  • 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义
  • 与select一样,poll返回后,需要轮询pollfd来获取就绪的描述符

优点

  • poll() 不要求开发者计算最大文件描述符加一的大小
  • poll() 在应付大数目的文件描述符的时候速度更快,相比于select
  • 它没有最大连接数的限制,原因是它是基于链表来存储的

epoll

在这里插入图片描述
epoll支持水平触发和边缘触发,最大的特点在于边缘触发,它只告诉进程哪些fd刚刚变为就需态,并且只会通知一次。还有一个特点是,epoll使用“事件”的就绪通知方式,通过epoll_ctl注册fd,一旦该fd就绪,内核就会采用类似callback的回调机制来激活该fd,epoll_wait便可以收到通知。

优点

  • 支持一个进程打开大数目的socket描述符(FD)

    select最不能忍受的是一个进程所打开的FD是有一定限制的,由FD_SETSIZE设置,默认值是1024/2048。对于那些需要支持的上万连接数目的IM服务器来说显然太少了。这时候你一是可以选择修改这个宏然后重新编译内核。不过 epoll则没有这个限制,它所支持的FD上限是最大可以打开文件的数目,这个数字一般远大于2048,举个例子,在1GB内存的机器上大约是10万左右,具体数目可以cat /proc/sys/fs/file-max察看,一般来说这个数目和系统内存关系很大。

  • IO效率不随FD数目增加而线性下降

    传统的select/poll另一个致命弱点就是当你拥有一个很大的socket集合,不过由于网络延时,任一时间只有部分的socket是"活跃"的,但是select/poll每次调用都会线性扫描全部的集合,导致效率呈现线性下降。但是epoll不存在这个问题,它只会对"活跃"的socket进行操作—这是因为在内核实现中epoll是根据每个fd上面的callback函数实现的。那么,只有"活跃"的socket才会主动的去调用 callback函数,其他idle状态socket则不会,在这点上,epoll实现了一个"伪"AIO,因为这时候推动力在Linux内核。

  • 使用mmap加速内核与用户空间的消息传递

    这点实际上涉及到epoll的具体实现了。无论是select,poll还是epoll都需要内核把FD消息通知给用户空间,如何避免不必要的内存拷贝就很重要,在这点上,epoll是通过内核与用户空间mmap同一块内存实现的。

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

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

相关文章

智能家居“落地者”:三翼鸟用场景方案持续链接大众消费

互联网分析沙龙(techxue)原创 作者 | 锡海 编辑 | 七喜 从上海车展再到AWE2023展会,只要有大型活动的地方,都能看到人潮汹涌的景象,久违的烟火气又回来了。数据显示,社会消费已出现较为强劲反弹&#xff0…

长知识了,mongo的时间居然这个样子

1、前言 最近一直在使用mongo数据库,前面文章也介绍了一直在做数据过期的事情, mongo中的数据过期时间之前在程序中增加了一个字段 【Springboot系列】项目启动时怎么给mongo表加自动过期索引 之前看到时间字段没有时区的信息,没有关注&a…

微服务注册中心选型:Zookeeper、Eureka、Nacos、Consul和Etcd

注册中心基本概念 什么是注册中心? 注册中心主要有三种角色: 服务提供者(RPC Server):在启动时,向 Registry 注册自身服务,并向 Registry 定期发送心跳汇报存活状态。 服务消费者&#xff08…

怎么知道网站服务器有没有被攻击?

​  一个网站服务器遭到攻击可能会给企业带来巨大的金融损失,因此,企业需要及时发现服务器是否被攻击。但是,企业如何知道自己的服务器是否被攻击呢?下面,我们来看一些服务器被攻击的警告信号。 1.网络延迟增加 在网络攻击中&a…

记一次运气非常好的渗透到服务器的经历

平平无奇的客服平台 这个客服平台是有RCE的,如果上传到的不是oss服务器,存储在本地服务器的话 在返回端口的url是存在st2 root权限,由于是客服后台服务器,没有啥有用价值的信息 直接替换私钥连服务器 继续翻找有用的信息 配置文件…

Django性能监视工具django-silk的使用

django-silk 是一个轻量级的 Django 应用性能监视工具,可帮助您了解 Django 应用的性能瓶颈、数据库查询等问题。它可以使用在django前后端分离的项目中,直接通过请求后台API接口即可对性能进行监视。以下是 django-silk 的使用步骤: 1.安装…

资本认可 | 开源网安成为中国未来独角兽企业,引领软件安全不断发展

4月11日,第七届万物生长大会中国未来独角兽大会盛大召开,本次大会中国投资发展促进会创投专委会联合微链共同发布了《2023中国未来独角兽TOP100榜单》,开源网安成功入选榜单。 《2023中国未来独角兽TOP100榜单》瞄准近两年融资较为活跃或融资…

快速简单制作macOS Ventura系统ISO格式镜像

ISO格式的镜像其实没有什么制作难度,下面苹果系统之家教大家怎么快速简单制作ISO格式的镜像,教程使用到的都是Mac官方的命令。制作好的ISO格式镜像可以用于虚拟机安装或者制作到U盘或者直接在Mac里面打开安装升级。 准备系统镜像 首先下载好macOS 镜像…

上海亚商投顾:沪指延续反弹涨0.67% AI概念股掀跌停潮

指数今日低开高走,沪指午后一度涨超1%,以保险为首的大金融板块拉升,中国平安在一季报驱动下,迎来久违涨停,成交超120亿元。医药股全天强势,何氏眼科、金石亚药、普蕊斯、天宇股份20CM涨停,第一医…

约翰霍普金斯大学诺奖得主涉嫌造假,撤回5篇PNAS论文

2019年,约翰霍普金斯大学的著名基因医学科学家Gregg L. Semenza博士因为“发现细胞如何感知和适应氧气供应”,和另外两名科学家( William Kaelin Jr. and Peter J. Ratcliffe)分享当年的生理医学诺贝尔奖。 近期,Gregg…

SpringBoot整合WebSocket详细教程

预期效果 共开启两个页面&#xff0c;实现一对一聊天。 服务端代码&#xff1a;https://gitee.com/lianaozhe/springboot-websocket.git 代码实现逻辑 服务端 导入相关依赖&#xff1a; <dependency><groupId>org.springframework.boot</groupId><art…

Silane-PEG-FITC 硅烷聚乙二醇荧光素FITC-PEG-Silane在医疗设备领域有广泛应用,具有很好的生物相容性。

FITC-PEG-Silane&#xff0c;荧光素-聚乙二醇-硅烷 中文名称&#xff1a;荧光素-聚乙二醇-硅烷 英文名称&#xff1a;FITC-PEG-Silane 存储条件&#xff1a;-20C&#xff0c;避光&#xff0c;避湿 性状 :白色、微黄色粉末或固体&#xff0c;取决于分子量 溶剂&#xff1a;…

新版android studio gradle插件7.4.2.pom一直无法下载问题

android studio同步时候出现org.gradle.api.plugins.UnknownPluginException&#xff0c;Plugin [id: com.android.application, version: 7.4.2] was not found in any of the following sources: pom插件一直无法下载&#xff0c;搞了好几天&#xff0c;简直想砸电脑&#x…

USB转串口芯片CH340与CH341使用问题汇总

USB转串口&#xff1a; CH340C/N/K/E/B/G/T CH341F/B/C/T/A USB转打印口&#xff1a; CH340H/S CH341F/B/A USB转I2C&#xff1a;CH341F/B/C/T/A USB转SPI&#xff1a;CH341F/B/A/H USB转并口/GPIO&#xff1a;CH341F/B/A 芯片供电注意事项 CH340/CH341等 USB 芯片都支…

定时器按键消抖

用到按键就要处理因为机械结构带来的按键抖动问题&#xff0c;也就是按键消抖。 按键消抖原理&#xff1a;在按键按下以后延时一段时间再去读取按键值&#xff0c;如果此时按键值还有效就表示这是一次有效的按键&#xff0c;中间的延时就是消抖的。 但是&#xff0c;延时函数…

《梦幻西游H5》L inux搭建游戏服务器!

西游H5 linux 架设教程 准备工具&#xff1a; Termux&#xff08;安卓&#xff09; Notepad SecureCRT WinSCP 安装宝塔直接运行命令即可。 yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.s…

在 Ubuntu 18.04 中配置静态 IP 及灵活切换

Ubuntu 18.04 使用 Netplan 来配置 IP 地址&#xff0c;Netplan 是一个新的命令行网络配置工具。 新的 IP 配置方法不会再用到 /etc/network/interfaces 这个文件&#xff0c;取而代之的是一个 YAML 文件。 默认的 Netplan 配置文件一般在 /etc/netplan 目录下。 查看当前 i…

opencv_04条形码区域分割

基于OpenCV的条形码区域分割 要基于OpenCV实现条形码区域分割&#xff0c;可以按照以下步骤进行&#xff1a; 加载图像&#xff1a;使用OpenCV中的imread函数读取待处理图像。灰度化&#xff1a;使用OpenCV中的cvtColor函数将彩色图像转换为灰度图像。边缘检测&#xff1a;使用…

java-android面试-线程状态

开篇一张图&#xff08;本图来源于网络&#xff09;&#xff0c;余下全靠编&#xff0c;我们正式开始。 一、线程的状态 总共有6种状态&#xff0c;分别如下&#xff1a; 1. 新建状态(New): 线程对象被创建后&#xff0c;就进入了新建状态。例如&#xff0c;Thread thread …

ShareSDK Google平台注册指南

注册开发者账号 访问Firebase&#xff0c;使用Google账号登录 创建项目 登录完成后&#xff0c;点击转到控制台 点击添加项目 输入项目名称&#xff0c;点击继续 根据需要选择是否开启Google Analytics&#xff08;分析&#xff09;&#xff0c;点击继续 选择Google Analytic…