当前位置: 首页 > news >正文

让我们,从头到尾,通透I/O模型

什么是IO

一句话总结
IO就是内存和硬盘的输入输出

I/O 其实就是 input 和 output 的缩写,即输入/输出。

那输入输出啥呢?

比如我们用键盘来敲代码其实就是输入,那显示器显示图案就是输出,这其实就是 I/O。

而我们时常关心的磁盘 I/O 指的是硬盘和内存之间的输入输出。

读取本地文件的时候,要将磁盘的数据拷贝到内存中,修改本地文件的时候,需要把修改后的数据拷贝到磁盘中。

网络 I/O 指的是网卡与内存之间的输入输出。

当网络上的数据到来时,网卡需要将数据拷贝到内存中。当要发送数据给网络上的其他人时,需要将数据从内存拷贝到网卡里。

那为什么都要跟内存交互呢?

我们的指令最终是由 CPU 执行的,究其原因是 CPU 与内存交互的速度远高于 CPU 和这些外部设备直接交互的速度。

因此都是和内存交互,当然假设没有内存,让 CPU 直接和外部设备交互,那也算 I/O。

总结下:I/O 就是指内存与外部设备之间的交互(数据拷贝)。

好了,明确什么是 I/O 之后,让我们来揭一揭 socket 通信内幕~

如何通信

socket

socket 创建

首先服务端需要先创建一个 socket。在 Linux 中一切都是文件,那么创建的 socket 也是文件,每个文件都有一个整型的文件描述符(fd)来指代这个文件。

int socket(int domain, int type, int protocol);
  • domain:这个参数用于选择通信的协议族,比如选择 IPv4 通信,还是 IPv6 通信等等
  • type:选择套接字类型,可选字节流套接字、数据报套接字等等。
  • protocol:指定使用的协议。这个 protocol 通常可以设为 0 ,因为由前面两个参数可以推断出所要使用的协议。

比如socket(AF_INET, SOCK_STREAM, 0);,表明使用 IPv4 ,且使用字节流套接字,可以判断使用的协议为 TCP 协议。

这个方法的返回值为创建的 socket 的 fd。

bind 绑定

现在我们已经创建了一个 socket,但现在还没有地址指向这个 socket。

众所周知,服务器应用需要指明 IP 和端口,这样客户端才好找上门来要服务,所以此时我们需要指定一个地址和端口来与这个 socket 绑定一下。

int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

参数里的 sockfd 就是我们创建的 socket 的文件描述符,执行了 bind 参数之后我们的 socket 距离可以被访问又更近了一步。

listen 监听

执行了 socket、bind 之后,此时的 socket 还处于 closed 的状态,也就是不对外监听的,然后我们需要调用 listen 方法,让 socket 进入被动监听状态,这样的 socket 才能够监听到客户端的连接请求。

int listen(int sockfd, int backlog);

传入创建的 socket 的 fd,并且指明一下 backlog 的大小。

这个 backlog 我查阅资料的时候,看到了三种解释:

  1. socket 有一个队列,同时存放已完成的连接和半连接,backlog为这个队列的大小。
  2. socket 有两个队列,分别为已完成的连接队列和半连接队列,backlog为这个两个队列的大小之和。
  3. socket 有两个队列,分别为已完成的连接队列和半连接队列,backlog仅为已完成的连接队列大小。

解释下什么叫半连接

我们都知道 TCP 建立连接需要三次握手,当接收方收到请求方的建连请求后会返回 ack,此时这个连接在接收方就处于半连接状态,当接收方再收到请求方的 ack 时,这个连接就处于已完成状态:
在这里插入图片描述
所以上面讨论的就是这两种状态的连接的存放问题。

我查阅资料看到,基于 BSD 派生的系统的实现是使用的一个队列来同时存放这两种状态的连接, backlog 参数即为这个队列的大小。

而 Linux 则使用两个队列分别存储已完成连接和半连接,且 backlog 仅为已完成连接的队列大小

accept 服务端连接

现在我们已经初始化好监听套接字了,此时会有客户端连上来,然后我们需要处理这些已经完成建连的连接。

从上面的分析我们可以得知,三次握手完成后的连接会被加入到已完成连接队列中去。

在这里插入图片描述
这时候,我们就需要从已完成连接队列中拿到连接进行处理,这个拿取动作就由 accpet 来完成。

int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);

这个方法返回的 int 值就是拿到的已完成连接的 socket 的文件描述符,之后操作这个 socket 就可以进行通信了。

如果已完成连接队列没有连接可以取,那么调用 accept 的线程会阻塞等待。

至此服务端的通信流程暂告一段落,我们再看看客户端的操作。

connect 客户端连接

客户端也需要创建一个 socket,也就是调用 socket(),这里就不赘述了,我们直接开始建连操作。

客户端需要与服务端建立连接,在 TCP 协议下开始经典的三次握手操作,再看一下上面画的图:
在这里插入图片描述
客户端创建完 socket 并调用 connect 之后,连接就处于 SYN_SEND 状态,当收到服务端的 SYN+ACK 之后,连接就变为 ESTABLISHED 状态,此时就代表三次握手完毕。

int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);

调用connect需要指定远程的地址和端口进行建连,三次握手完毕之后就可以开始通信了。

客户端这边不需要调用 bind 操作,默认会选择源 IP 和随机端口。

建立连接操作总结

用一幅图来小结一下建连的操作:
在这里插入图片描述
可以看到这里的两个阻塞点:

  • connect:需要阻塞等待三次握手的完成。
  • accept:需要等待可用的已完成的连接,如果已完成连接队列为空,则被阻塞。

read、write

连接建立成功之后,就能开始发送和接收消息了,我们来看一下
在这里插入图片描述
read 为读数据,从服务端来看就是等待客户端的请求,如果客户端不发请求,那么调用 read 会处于阻塞等待状态,没有数据可以读,这个应该很好理解。

write 为写数据,一般而言服务端接受客户端的请求之后,会进行一些逻辑处理,然后再把结果返回给客户端,这个写入也可能会被阻塞。

这里可能有人就会问 read 读不到数据阻塞等待可以理解,write 为什么还要阻塞,有数据不就直接发了吗?

因为我们用的是 TCP 协议,TCP 协议需要保证数据可靠地、有序地传输,并且给予端与端之间的流量控制。

所以说发送不是直接发出去,它有个发送缓冲区,我们需要把数据先拷贝到 TCP 的发送缓冲区,由 TCP 自行控制发送的时间和逻辑,有可能还有重传什么的。

如果我们发的过快,导致接收方处理不过来,那么接收方就会通过 TCP 协议告知:别发了!忙不过来了。发送缓存区是有大小限制的,由于无法发送,还不断调用 write 那么缓存区就满了,满了就不然你 write 了,所以 write 也会发生阻塞。

综上,read 和 write 都会发生阻塞。

总结:为什么网络 I/O 会被阻塞?——io模型

因为建连和通信涉及到的 accept、connect、read、write 这几个方法都可能会发生阻塞。

阻塞会占用当前执行的线程,使之不能进行其他操作,并且频繁阻塞唤醒切换上下文也会导致性能的下降。

由于阻塞的缘故,起初的解决的方案就是建立多个线程,但是随着互联网的发展,用户激增,连接数也随着激增,需要建立的线程数也随着一起增加,到后来就产生了 C10K 问题。

服务端顶不住了呀,咋办?

优化呗!

所以后来就弄了个非阻塞套接字,然后 I/O多路复用、信号驱动I/O、异步I/O。

下篇我们就来好好盘盘,这几种 I/O 模型!

基础知识介绍——内核态与用户态

上篇我们已经搞懂了 socket 的通信内幕,也明白了网络 I/O 确实会有很多阻塞点,阻塞 I/O 随着用户数的增长只能利用增加线程的方式来处理更多的请求,而线程不仅会占用内存资源且太多的线程竞争会导致频繁地上下文切换产生巨大的开销。

因此,阻塞 I/O 已经不能满足需求,所以后面大佬们不断地优化和演进,提出了多种 I/O 模型。

在 UNIX 系统下,一共有五种 I/O 模型,今天我们就来盘一盘它!

不过在介绍 I/O 模型之前,我们需要先了解一下前置知识。

内核态与用户态

我们的电脑可能同时运行着非常多的程序,这些程序分别来自不同公司。

谁也不知道在电脑上跑着的某个程序会不会发疯似得做一些奇怪的操作,比如定时把内存清空了。

因此 CPU 划分了非特权指令和特权指令,做了权限控制,一些危险的指令不会开放给普通程序,只会开放给操作系统等特权程序。

你可以理解为我们的代码调用不了那些可能会产生“危险”操作,而操作系统的内核代码可以调用。

这些“危险”的操作指:内存的分配回收,磁盘文件读写,网络数据读写等等。

如果我们想要执行这些操作,只能调用操作系统开放出来的 API ,也称为系统调用。

这就好比我们去行政大厅办事,那些敏感的操作都由官方人员帮我们处理(系统调用),所以道理都是一样的,目的都是为了防止我们(普通程序)乱来。

这里又有两个名词:

  • 用户空间
  • 内核空间。

我们普通程序的代码是跑在用户空间上的,而操作系统的代码跑在内核空间上,用户空间无法直接访问内核空间的。当一个进程运行在用户空间时就处于用户态,运行在内核空间时就处于内核态

当处于用户空间的程序进行系统调用,也就是调用操作系统内核提供的 API 时,就会进行上下文的切换,切换到内核态中,也时常称之为陷入内核态。

那为什么开头要先介绍这个知识点呢?

因为当程序请求获取网络数据的时候,需要经历两次拷贝:

  • 程序需要等待数据从网卡拷贝到内核空间。
  • 因为用户程序无法访问内核空间,所以内核又得把数据拷贝到用户空间,这样处于用户空间的程序才能访问这个数据。

介绍这么多就是让你理解为什么会有两次拷贝,且系统调用是有开销的,因此最好不要频繁调用。

然后我们今天说的 I/O 模型之间的差距就是这拷贝的实现有所不同!

今天我们就以 read 调用,即读取网络数据为例子来展开 I/O 模型。

发车!

IO模型

同步阻塞模型

假如A在河边钓鱼的时候,非常的专心,生怕鱼儿溜掉,故此,A就一直盯着鱼竿,一直等着鱼儿上钩,专心的做这一件事情,直到鱼儿上钩,才结束这个动作,这就是阻塞IO。在内核把数据准备好之前,系统调用会一直处于阻塞状态。

在这里插入图片描述
当用户程序的线程调用 read 获取网络数据的时候,首先这个数据得有,也就是网卡得先收到客户端的数据,然后这个数据有了之后需要拷贝到内核中,然后再被拷贝到用户空间内,这整一个过程用户线程都是被阻塞的。

假设没有客户端发数据过来,那么这个用户线程就会一直阻塞等着,直到有数据。即使有数据,那么两次拷贝的过程也得阻塞等着。

所以这称为同步阻塞 I/O 模型。

它的优点很明显,简单。调用 read 之后就不管了,直到数据来了且准备好了进行处理即可。

缺点也很明显,一个线程对应一个连接,一直被霸占着,即使网卡没有数据到来,也同步阻塞等着。

我们都知道线程是属于比较重资源,这就有点浪费了。

所以我们不想让它这样傻等着。

于是就有了同步非阻塞 I/O。

同步非阻塞 I/O

假如B也在河边钓鱼,B不想像A一样把所有的时间都花在等鱼儿上钩这件事情上,所以他的做法就是在等待鱼儿上钩的同时,自己也可以看看书,刷刷小编的博客,聊天等等。但是B也不是就不管鱼儿了,他会每隔一段固定时间都来看一下,有没有鱼儿上钩,如果有鱼儿上钩,他就结束这个动作,这就是非阻塞IO。
非阻塞IO往往需要程序员循环的方式反复尝试读取文件描述符,这个过程称为轮询,这对于cpu来说的话是较大的浪费,一般只有特定的场景下才能使用。
在这里插入图片描述
从图中我们可以很清晰的看到,同步非阻塞I/O 基于同步阻塞I/O 进行了优化:

在没数据的时候可以不再傻傻地阻塞等着,而是直接返回错误,告知暂无准备就绪的数据!

这里要注意,从内核拷贝到用户空间这一步,用户线程还是会被阻塞的。

这个模型相比于同步阻塞 I/O 而言比较灵活,比如调用 read 如果暂无数据,则线程可以先去干干别的事情,然后再来继续调用 read 看看有没有数据。

但是如果你的线程就是取数据然后处理数据,不干别的逻辑,那这个模型又有点问题了。

等于你不断地进行系统调用,如果你的服务器需要处理海量的连接,那么就需要有海量的线程不断调用,上下文切换频繁,CPU 也会忙死,做无用功而忙死。

那怎么办?

于是就有了I/O 多路复用。

多路复用

假如D也在河边钓鱼,但是D是一个土豪,他一个人就拿了好多鱼竿摆在哪里,这样很明显就增加了鱼儿上钩的机会。他只需要不断地查看每个鱼竿是否有鱼儿上钩就行了,提高了效率。 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。
在这里插入图片描述
从图上来看,好像和上面的同步非阻塞 I/O 差不多啊,其实不太一样,线程模型不一样。

既然同步非阻塞 I/O 在太多的连接下频繁调用太浪费了, 那就招个专员吧。

这个专员工作就是管理多个连接,帮忙查看连接上是否有数据已准备就绪。

也就是说,可以只用一个线程查看多个连接是否有数据已准备就绪。

具体到代码上,这个专员就是 select ,我们可以往 select 注册需要被监听的连接,由 select 来监控它所管理的连接是否有数据已就绪,如果有则可以通知别的线程来 read 读取数据,这个 read 和之前的一样,还是会阻塞用户线程。

这样一来就可以用少量的线程去监控多条连接,减少了线程的数量,降低了内存的消耗且减少了上下文切换的次数,很舒服。

想必到此你已经理解了什么叫 I/O 多路复用。

所谓的多路指的是多条连接,复用指的是用一个线程就可以监控这么多条连接。

看到这,你再想想,还有什么地方可以优化的?

信号驱动式IO

假如C也在河边钓鱼,他认为A、B不够聪明,故此,他想了一种办法,就是在鱼竿上挂上了一个铃铛,当有鱼儿上钩的时候,铃铛就会被触发,发出响声,他就可以过去将鱼儿钓上来了。信号驱动IO模型,应用进程告诉内核:当数据报准备好的时候,给我发送一个信号,对SIGIO信号进行捕捉,并且调用我的信号处理函数来获取数据报。
在这里插入图片描述
上面的 select 虽然不阻塞了,但是他得时刻去查询看看是否有数据已经准备就绪,那是不是可以让内核告诉我们数据到了而不是我们去轮询呢?

信号驱动 I/O 就能实现这个功能,由内核告知数据已准备就绪,然后用户线程再去 read(还是会阻塞)。

听起来是不是比 I/O 多路复用好呀?那为什么好像很少听到信号驱动 I/O?
为什么市面上用的都是 I/O 多路复用而不是信号驱动?

因为我们的应用通常用的都是 TCP 协议,而 TCP 协议的 socket 可以产生信号事件有七种。

也就是说不仅仅只有数据准备就绪才会发信号,其他事件也会发信号,而这个信号又是同一个信号,所以我们的应用程序无从区分到底是什么事件产生的这个信号。

那就麻了呀!

所以我们的应用基本上用不了信号驱动 I/O,但如果你的应用程序用的是 UDP 协议,那是可以的,因为 UDP 没这么多事件。

因此,这么一看对我们而言信号驱动 I/O 也不太行。

异步 I/O

假如E也想钓鱼,但是他又有点忙,所以他雇佣了一个人专门帮他看着鱼竿,一旦有鱼儿上钩,就让这个人通知他,他过来将鱼儿钓上来。由内核在数据拷贝完成时, 通知应用程序(信号驱动是告诉应用程序何时可以开始拷贝数据).
这一次我们雇了一个钓鱼高手。他不仅会钓鱼,还会在鱼上钩之后给我们发短信,通知我们鱼已经准备好了。我们只要委托他去抛竿,然后就能跑去干别的事情了,直到他的短信。我们再回来处理已经上岸的鱼。

在这里插入图片描述
信号驱动 I/O 虽然对 TCP 不太友好,但是这个思路对的:往异步发展,但是它并没有完全异步,因为其后面那段 read 还是会阻塞用户线程,所以它算是半异步。

因此,我们得想下如何弄成全异步的,也就是把 read 那步阻塞也省了。

其实思路很清晰:让内核直接把数据拷贝到用户空间之后再告知用户线程,来实现真正的非阻塞I/O!

所以异步 I/O 其实就是用户线程调用 aio_read ,然后包括将数据从内核拷贝到用户空间那步,所有操作都由内核完成,当内核操作完毕之后,再调用之前设置的回调,此时用户线程就拿着已经拷贝到用户控件的数据可以继续执行后续操作。

在整个过程中,用户线程没有任何阻塞点,这才是真正的非阻塞I/O。

那么问题又来了:

为什么常用的还是I/O多路复用,而不是异步I/O?
因为 Linux 对异步 I/O 的支持不足,你可以认为还未完全实现,所以用不了异步 I/O。

这里可能有人会说不对呀,像 Tomcat 都实现了 AIO的实现类,其实像这些组件或者你使用的一些类库看起来支持了 AIO(异步I/O),实际上底层实现是用 epoll 模拟实现的。

而 Windows 是实现了真正的 AIO,不过我们的服务器一般都是部署在 Linux 上的,所以主流还是 I/O 多路复用。

至此,想必你已经清晰五种 I/O 模型是如何演进的了。

下篇,我将讲讲谈到网络 I/O 经常会伴随的几个容易令人混淆的概念:同步、异步、阻塞、非阻塞。

相关文章:

让我们,从头到尾,通透I/O模型

什么是IO 一句话总结 IO就是内存和硬盘的输入输出 I/O 其实就是 input 和 output 的缩写,即输入/输出。 那输入输出啥呢? 比如我们用键盘来敲代码其实就是输入,那显示器显示图案就是输出,这其实就是 I/O。 而我们时常关心的磁盘…...

Word控件Spire.Doc 【Table】教程(16):C#/VB.NET:在 Word 表格中插入或提取图像

Spire.Doc for .NET是一款专门对 Word 文档进行操作的 .NET 类库。在于帮助开发人员无需安装 Microsoft Word情况下,轻松快捷高效地创建、编辑、转换和打印 Microsoft Word 文档。拥有近10年专业开发经验Spire系列办公文档开发工具,专注于创建、编辑、转…...

C++如何实现系统语言切换功能,MessageBox的确认/取消按钮语言显示如何跟程序一致

文章目录前言 一、新建工程二、添加多国语言的资源三、程序语言设置四、语言切换五、字符串处理六、MessageBox的问题七、相关函数和类型参考文章前言 目前很多软件都是要出口到多个国家,多个地区,因此,为软件提供多国语言支持就成为了一个基…...

计算机组成原理学习笔记:循环冗余校验码

循环冗余校验码 CRC 码 循环冗余校验码 (cyclic redundancy Check, CRC) 十进制除法 从熟悉的十进制出发,假设现在你要给另一个人传送882这样的一个10进制数据,为了防止传送数据的过程中某一个数据发生错误你可以和你的另一个小伙伴约定一个除数&…...

Educational Codeforces Round 143 (Rated for Div. 2) A — C

Educational Codeforces Round 143 (Rated for Div. 2) 文章目录A. Two Towers题目大意题目分析codeB. Ideal Point题目大意题目分析codeC. Tea Tasting题目大意题目分析codeA. Two Towers 题目大意 有两个有红蓝两种颜色组成的塔,每次操作可以将其中一个塔顶的色…...

【Unity VR开发】结合VRTK4.0:将浮点数从交互器传递到可交互对象

语录: 愿你熬得过万丈孤独,藏得下星辰大海。 前言: 默认情况下,交互器只能将单个布尔操作传递给可交互对象,后者控制可交互对象上的抓取操作。在其他时候,交互器中的其他操作可能希望传递给可交互对象&…...

【图像分类】基于PyTorch搭建卷积神经网络实现MNIST手写数字识别(附项目完整代码)

写在前面: 首先感谢兄弟们的关注和订阅,让我有创作的动力,在创作过程我会尽最大能力,保证作品的质量,如果有问题,可以私信我,让我们携手共进,共创辉煌。 在【图像分类】基于PyTorch搭建LSTM实现MNIST手写数字体识别(单向LSTM,附完整代码和数据集)文章中,我们使用了…...

4.4 MQC

1. 实验目的 熟悉MQC的应用场景掌握MQC的配置方法2. 实验拓扑 实验拓扑如图4-10所示: 图4-10:MQC 3. 实验步骤 (1) IP地址的配置 AR1的配置 <Huawei>system-view...

ClickHouse列存储(十一)—— ClickHouse

文章目录一、重点内容&#xff1a;1.数据库基本概念2.列式存储3.clickHouse存储设计4.clickHouse典型应用场景二、准备工作&#xff1a;1、了解数据库基本概念2、了解列式存储相关概念3、了解ClickHouse存储设计4、了解 ClickHouse典型应用场景三、详细知识点介绍&#xff1a;1…...

公司来了个卷王,真让人奔溃

2022年已经结束结束了&#xff0c;最近内卷严重&#xff0c;各种跳槽裁员&#xff0c;相信很多小伙伴也在准备今年的金三银四的面试计划。 在此展示一套学习笔记 / 面试手册&#xff0c;年后跳槽的朋友可以好好刷一刷&#xff0c;还是挺有必要的&#xff0c;它几乎涵盖了所有的…...

什么是refresh?Spring refresh 流程

refresh 是 AbstractApplicationContext 中的一个方法&#xff0c;负责初始化 ApplicationContext 容器&#xff0c;容器必须调用 refresh 才能正常工作。它的内部主要会调用 12 个方法&#xff0c;我们把它们称为 refresh 的 12 个步骤&#xff1a;1. prepareRefresh2. obtain…...

Python登陆系统

前言 #源码见文末公众号哈# 登录系统 一个简单的登录系统包含了登录账户、注册账户、修改密码以及注销账户的操作。 1. 登录账户 登录系统主要需要判断账户是否存在&#xff0c;不存在就注册一个账户&#xff0c;如果第一次登录系统&#xff0c;我们需要先新建一个文件&…...

【新2023】华为OD机试 - 重组字符串(Python)

华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 重组字符串 题目 给定一个非空字符串 S,其被 N 个‘-’分隔成 N+1 的子串,给定正整数 K, 要求除第一个子串外,其余的子串每 K 个字符组成新的子串,并用‘-’分隔。 对于新组成的每一个子串,如果它…...

视频监控流程图

<html> <head> <meta http-equiv"Content-Type" content"text/html; charsetUTF-8"/> <link rel"stylesheet" type"text/css" href"visio.css"/> <title> 视频监控流程图 </title> <…...

普通单双面板的生产工艺流程之图形转移,华秋一文告诉你

衔接上文&#xff0c;继续为朋友们分享普通单双面板的生产工艺流程。 如图&#xff0c;第五道主流程为图形转移。 图形转移的目的为&#xff1a; 利用光化学原理&#xff0c;将图形线路的形状转移到印制板上&#xff0c;再利用化学原理&#xff0c;将图形线路在印制板上制作出…...

1.8 providers

生成providersnest g service <name>providers的注入方式构造函数注入Injectable() export class KeywordService {constructor(private readonly httpService: HttpService,private readonly pro: ProService,) {} }Inject()注入export class KeywordController {Inject…...

如何编写一个基本的 Verilog Module(模块)

1、概述这篇文章主要介绍了 Verilog 在 FPGA 设计中的概念和使用方法。首先讨论使用模块&#xff08;module&#xff09;关键字构造 Verilog 设计的方式&#xff0c;以及这与所描述的硬件的关系。这包括对参数、端口&#xff08;port&#xff09;和例化&#xff08;instantiato…...

让乔布斯想要「发动核战争」的 Android,为何成了占有率最高的系统?

2008 年 9 月 23 日&#xff0c;Apple 的创始人和 CEO 史蒂夫乔布斯像往常一样走进了公司&#xff0c;此时距离初代 iPhone 的发布会才过了一年半&#xff0c;这款充满了争议的产品就像一块从山崖滚落的巨岩&#xff0c;一路电光石火的给手机市场的《小石潭记》来了场焚书坑儒。…...

FPGA开发软件(vivado + modelsim)环境搭建(附详细安装步骤+软件下载)

本文详细介绍了vivado软件和modelsim软件的安装&#xff0c;以及vivado中配置modelsim仿真设置&#xff0c;每一步都加文字说明和图片。一、软件安装包下载1、vivado vivado版本很多&#xff0c;目前最新的已更新到vivado2022.2&#xff0c;版本越高&#xff0c;安装包越大&…...

TypeScript 学习之类型

布尔类型 类型: boolean最简单的类型&#xff0c;值只有 true/false let isDone: boolean true;数字类型 类型&#xff1a;number数字都是浮点数&#xff0c;支持二进制、八进制、十进制、十六进制。 let decLiteral: number 16; // 十进制 let hexLiteral: number 0xf0…...

基于MATLAB计算MIMO信道容量(附完整代码与分析)

目录 一.介绍 二. 代码 三. 运行结果及分析 3.1 MIMO信道容量&#xff1a;固定发射天线数为4 3.2 MIMO信道容量&#xff1a;固定接收天线数为4 3.3 AWGN信道与瑞利信道容量 四. 总结 一.介绍 本文章将在MATLAB环境中分析MIMO信道容量&#xff0c;AWGN信道容量&#xf…...

CSDN城市开发者联盟、C友会期待你的加入

文章目录&#x1f31f; 课前小差&#x1f31f; chatGPT&#x1f31f; CSDN中的持续学习&#x1f31f; 23年原力计划&#x1f31f; C友会、CDC&#x1f31f; 如何关联本地的开发者&#xff1f;&#x1f31f; 写在最后&#x1f31f; 课前小差 哈喽&#xff0c;大家好&#xff0c…...

【新2023】华为OD机试 - 吃火锅(Python)

华为 OD 清单查看地址:blog.csdn.net/hihell/category_12199275.html 吃火锅 题目 入职后,导师会请你吃饭,你选择了火锅, 火锅里会在不同时间下很多菜, 不同食材要煮不同时间,才能变得刚好合适, 你希望吃到最多的刚好合适的菜, 但是你的手速不够快用m代替手速, 每次…...

类似LeetCode的登录页面(小程序版)

前言每一个项目都会有用户端的注册和登录页面&#xff0c;对于刚入门的小白来说&#xff0c;在UI设计方面不太擅长&#xff0c;就算大致的UI界面设计出来了&#xff0c;但是落实到代码上来实现的时候就很容易卡住。这篇博客主要介绍的就是仿作一个类似LeetCode登录的简约大方页…...

CUDA的统一内存

CUDA的统一内存 文章目录CUDA的统一内存N.1. Unified Memory IntroductionN.1.1. System RequirementsN.1.2. Simplifying GPU ProgrammingN.1.3. Data Migration and CoherencyN.1.4. GPU Memory OversubscriptionN.1.5. Multi-GPUN.1.6. System AllocatorN.1.7. Hardware Coh…...

MySQL-其他函数(补充)

格式化函数FORMAT(x, n) 例&#xff1a;将数字x进行格式化&#xff0c;以四舍五入的方式保留n位小数&#xff0c;结果以字符串的形式返回mysql> select format(12.3456,3),format(2.2,4),format(9.333,0); --------------------------------------------------- | format(12…...

MySQL Study Notes Design in 2023

文章目录1 概述1.1 MySQL相关概述1.2 数据模型1.3 SQL分类2 数据库设计-DDL2.1 约束2.2 字段3 数据库操作-DML3.1 增加(insert)1 概述 1.1 MySQL相关概述 数据库&#xff1a;英文为 DataBase&#xff0c;简称DB&#xff0c;它是存储和管理数据的仓库。 数据库管理系统&#xf…...

C++ 修改防火墙firewall设置(Windows)

文章目录1、简介1.1 防火墙概述1.2 入站&#xff0c;还是出站&#xff1f;1.3 防火墙规则优先级2、系统界面方式3、命令行方式3.1 防火墙基本状态设置3.2 入站出站规则设置3.3 其他设置3.4 telnet检测端口4、C方式4.1 注册表4.2 COM&#xff08;Windows XP&#xff09;4.3 COM&…...

Spring 入门教程详解

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

day43【代码随想录】动态规划之一和零、完全背包理论基础

文章目录前言一、一和零&#xff08;力扣474&#xff09;二、完全背包前言 1、一和零 2、完全背包理论基础 一、一和零&#xff08;力扣474&#xff09; 求装满这个背包最多有多少个物品 给你一个二进制字符串数组 strs 和两个整数 m 和 n 。 请你找出并返回 strs 的最大子集…...

自己建网站用gbk/东莞专业网站推广工具

本来是一件很简单的事情。cvsup -gL 2 -h cvsup.freebsd.org /usr/share/examples/cvsup/ports-supfilecd /usr/ports/lang/php5(或者用&#xff1a;/usr/ports/www/mod_php5)make install clean但是结果却发现pivot无法运行&#xff0c;报一个错误&#xff0c;说是什么函数找不…...

wordpress添加动态图标/搜索引擎营销的原理是什么

STL之deque容器1. deque容器基本概念2. deque容器实现原理3. deque常用API1. deque容器基本概念 vector容器是单向开口的连续内存空间&#xff0c;deque则是一种双向开口的连续线性空间。所谓的双向开口&#xff0c;意思是可以在头尾两端分别做元素的插入和删除操作&#xff0c…...

什么样的网站适合优化/武汉网络seo公司

2019独角兽企业重金招聘Python工程师标准>>> netstat -apn|grep 7021 查看端口使用情况 lsof -i :7020 查看端口进程 转载于:https://my.oschina.net/shootercn/blog/16899...

网站设计需要什么/西安网络推广seo0515

C#一个到多个Cookie的字符串添加到CookieCollection集合中多个站点&#xff08;Domain&#xff09;与多个路径&#xff08;Path&#xff09;与多个Cookie名&#xff08;c.name&#xff09;的字符要添加到CookieCollection集合中在网上找不到可行的方法&#xff0c;isGood用一天…...

品牌网站设计案例/怎样把自己的产品放到网上销售

扩展就是向一个已有的类、结构体或枚举类型添加新功能&#xff08;functionality&#xff09;。这包括在没有权限获取原始源代码的情况下扩展类型的能力&#xff08;即逆向建模&#xff09;。扩展和 Objective-C 中的分类&#xff08;categories&#xff09;类似。&#xff08;…...

北滘 网站建设/网络营销软文范文

HDFS的优点和缺点HDFS的优点1、可构建在廉价机器上通过多副本提高可靠性&#xff0c;提供了容错和恢复机制&#xff0c;服务器节点的宕机是常态 必须理性对待。2、高容错性数据自动保存多个副本&#xff0c;副本丢失后&#xff0c;自动恢复。HDFS的核心设计思想&#xff1a; 分…...