【网络】多路转接——五种IO模型 | select
🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
五种IO模型 | select
- 🍧五种IO模型
- 🍧select
- 🧁认识接口
- 🧁简易select服务器
- 🧁select的特点
- 🍧总结
🍧五种IO模型
在学习系统部分的时候,本喵就讲解过IO,当时我们学习的IO就是从文件中读数据和写数据,到了后来学习网络的时候,我们知道,从网络中读取和写入数据也是IO,那么IO到底是什么呢?今天我们来更深刻的认识一下IO。
就拿读取数据来说,无论是调用read还是recv,在文件描述符所指向的struct file中的接收缓冲区如果没有数据的时候,都会阻塞等待。
当缓冲区中有数据后,才会进行读取,所谓读取,本质就是在拷贝,就是将内核缓冲区中的数据拷贝到用户缓冲区中供用户去使用。
无论是等待还是拷贝,都是读取的过程,二者缺一不可。
- IO = 等 + 数据拷贝
那么什么是高效的IO呢?我们知道,IO过程中我们在意的是拷贝,而不是等待,由于各种技术的发展,拷贝所花费的时间几乎是固定的,是由电路或者系统等等机制来保证的,所以拷贝的效率已经很难再有提升了。
- 高效的IO = 减少等待的比重
IO的种类有五种,下面本喵给大家介绍一下:
- 阻塞IO:在内核将数据准备好之前,系统调用会一直等待,所有的套接字以及文件,默认都是阻塞方式。

如上图所示便是阻塞IO的示意图,在进程调用recvfrom从内核缓冲区中读取数据时,如果数据没有准备好,进程就会阻塞在调用处等待,直到数据准备好,才会将内核缓冲区的数据拷贝到用户缓冲区,并且给进程返回值。
- 阻塞IO是最常见的IO模型,也是最简单的IO模型,我们之前写的所有IO都是阻塞式的。
- 非阻塞IO:如果内核还未将数据准备好,系统调用仍然会直接返回,并且返回
EAGAN或者EWOULDBLOCK错误码。

如上图所示,进程调用recvfrom从内核缓冲区中读取数据时,即使数据没有准备好,仍然会给进程一个返回一个EAGAN或者EWOULDBLOCK。
通常情况下使用这种IO方式时,如果返回EAGAN或者EWOULDBLOCK,说明数据没有准备好,就会再次调用recvfrom去读取数据,如此反复,直到数据准备好并且完成拷贝,最后返回表示成的返回值。
- 非阻塞IO需要程序员以循环的方式反复尝试读写文件描述符,这个过程称为轮询。
- 这对CPU来说是较大的浪费,一般只有特定场景下才使用。
默认情况下,文件描述符fd指向的struct file中的缓冲区的阻塞式IO,所以我们前面无论是在进行文件操纵还是使用套接字的时候,都是阻塞IO。

如上图所示系统调用fcntl,可以使用它将fd所指向的文件改成非阻塞IO模式,它的参数是一个可变参数。
- int fd:要修改文件的文件描述符fd。
- 返回值fl:大于0表示当前文件的状态,小于0表示调用失败。
- int cmd:对fd所指向的文件要进行的操作,可以传递两个参数:
F_GETFL:用来获取该文件当前的状态。F_SETFL:用来设置该文件当前的状态。
- 可变参数部分:可以传递参数有O_RDONLY,O_WRONLY等等,最重要的是
O_NONBLOCK非阻塞IO方式,但是在传参的时候,必须和fl进行按位或,如fl | O_NONBLOCK。

如上图所示代码是本喵写的一个工具函数,作用就是将指定fd所代表的文件设置成非阻塞IO方式。

如上图,从内核中的代码可以看到,O_NONBLOCK是一个宏,仅仅代表着一个比特位的状态,所以在传参时,需要使用fl | O_NONBLOCK的方式,在文件原有的状态上增加非阻塞。
再重新来看一下read系统调用:

如上图所示为man手册中的描述,调用失败以后,返回-1,并且错误码被设置。
- 设置不同的错误码代表着不同的意义。

如上图所示,可以设置的错误码有这么多,虽然返回值是-1,但是不同的错误码代表着不同的情况,其中EAGAIN或者EWOULDBLOCK表示的就是数据没有准备好,需要稍后再读。
- 读取数据时,内核中缓冲区的数据没有准备好并没有错,这是很正常的情况。
- 所以错误码
EAGAIN并不表示错误了,只是表示数据暂时没有准备好,需要稍后再来读取。

如上图所示,EAGAIN和EWOULDBLOCK都是宏,而且它们的值都是11,本质上是一个东西。

如上图代码所示,使用本喵写的工具serNonBlock将标准输入,也就是文件描述符为0的文件设置成非阻塞IO模式,然后运行。

如上图所示,可以看到,输入提示符>>>在不停打印,在本喵从键盘上输入的时候,仍然在打印,虽然输入的内容被>>>分隔开了,但是输入完成以后,回显echo的内容却是完整的,仍然是helloworld。
- 输入和输出互不影响,因为有输入缓冲区和输出缓冲区,二者是相互独立的。

如上图所示便是默认情况下的阻塞IO,可以看到,输入和输出轮替进行着,在本喵没有输入的情况下,该进程是阻塞等待的。

如上图所示,可以在else情况,也就是返回值s小于0的时候再进行具体的判断,如果错误码errno的值是EAGAIN说明只是数据没有准备好,并不是出错了,在等待数据就绪的过程中可以执行其他任务。

如上图所示,此时进程并没有阻塞,而是在轮询,数据没有准备好就执行其他任务来打发时间。
- 信号驱动IO:内核将数据准备好的时候,使用
SIGIO信号通知应用程序进行IO操作。

如上图所示信号驱动IO模型,该模式调用recvfrom并不是在主进程中调用,而且使用signal注册信号处理函数,在信号处理函数中调用recvfrom。
进程一直在正常运行,执行自己的逻辑,当内核接收缓冲区有数据到来时,进程会收到系统给发的信号SIGIO,然后就会调用该信号注册的处理方式,在信号处理方式中调用recvfrom读取缓冲区中的数据。
- 一旦进入信号处理函数中,说明内核缓冲区中的数据已经准备好了,在这里只需要读取,而不再需要等待。
- IO多路转接:虽然从流程图上看起来和阻塞IO类似,实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态。

如上图IO多路转接模式,该模式中,将IO的等待和拷贝两个步骤分开了。
进程调用select系统调用来等待内核缓冲区中数据就绪,当数据就绪以后通知进程调用recvfrom来将数据拷贝到用户缓冲区中。
这样看起来和信号驱动的IO模式没有什么区别,但是多路转接的优势在于可以同时等待多个文件描述符所指向的文件。
- IO多路转接模式下,可以同时等待多个文件,当一个或者多个文件的缓冲区中数据就绪时,就会通知上层用户来读取。
此时虽然也有等,但是等的比重就降低了,因为能够一次等待多个文件,进而让上层用户一次来读取多个缓冲区中的数据,拷贝的比重就增加了,从而提高了IO的效率。
- 异步IO:由内核在数据拷贝完成时,通知应用程序直接去用户缓冲区中使用数据。
- 同步和异步关注的是消息通信机制:
- 所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回,但是一旦调用返回,就得到返回值了。
- 也就是由调用者主动等待这个调用的结果。
异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。
- 当一个异步过程调用发出后,调用者不会立刻得到结果,而是在调用发出后,被调用者通过状态、信号等来通知调用者,或通过回调函数处理这个调用。

如上图所示异步IO模式示意图,进程调用aio_read,将等待数据就绪和将数据拷贝到用户缓冲区两个步骤的工作全部交给操作系统来完成。
当操作系统完成两个步骤以后,通知上层用户直接去用户缓冲区中使用数据即可。
信号驱动是告诉进程可以从内核缓冲区中拷贝数据到用户缓冲区了,而异步IO连拷贝这一步也不用做了,直接使用数据。
- 另外,我们回忆在学习多进程多线程的时候,也提到同步和互斥,这里的同步通信和进程之间的同步是完全不同的概念。
- 进程/线程同步也是进程/线程之间直接的制约关系,是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系。尤其是在访问临界资源的时候。
比较这五种IO模式,阻塞IO模式肯定是效率最低的,非阻塞以及信号驱动的IO模式,同样需要参加IO的等待和拷贝两个过程,对于IO的效率是相同的。
异步IO模式也是同样的道理,虽然等待和拷贝不是由线程去做的,而是由操作系统在做,但是线程也得在等待和拷贝完成后才能使用数据,所以IO的效率还是没有提高的。
而多路转接不一样,虽然等待和拷贝两个过程都参与,但是等待时可以一次等待多个文件描述符,拷贝时也是可以拷贝多个文件描述符中的数据。
- 由于等待的文件描述符数量多,所以有数据就绪的概率就高,进行拷贝也更加频繁。
- 站在上帝视角来看,多路转接模式下,进程在单位时间内进行拷贝的次数要比其他几种模式多。
所以多路转接更加高效,而我们研究的重点也在多路转接模式上,主要有select,poll,epoll三种方式。
🍧select
🧁认识接口

如上图所示,该系统调用有5个参数。
- int nfds:要等待的所有文件描述符中的最大
fd+1。
假设现在要等待的文件有3个,文件描述符分别是3,4,7,则传参时就需要传7+1=8给nfds。
- fd_set* reads:等待读取就绪文件描述符的位图。
select等待的文件有不同的事件会就绪,比如读取就绪,写就绪,错误就绪等等。

如上图所示是fd_set类型在内核中的定义,可以看到它就是一个位图。它有多个比特位,每一个比特位代表一个文件描述符,比特位的状态表示该比特位是否被监听。

如上图所示便是fd_set位图示意图,其中比特位的顺序由低到高从左向右,比特位的下标就是代表文件描述符fd,比特位的内容表示状态,这张图中,文件描述符为1,3,1023的三个文件描述符需要被select监视。

如上图所示,使用sizeof得到fd_set类型的大小是8字节,所以有1024个比特位,这意味着使用select最多监视1024个文件描述符。
将接收缓冲区所在文件的文件描述符设置到readfds中,当该缓冲区有数据到来时(读就绪),select就会通知上层进程去读取该缓冲区中的数据。
- fd_set* writefds:等待写入就绪文件描述符所在位图。
该位图和readfds一样,只是操作系统等待的事件由读就绪变成了写就绪,当发送缓冲区空了以后(写就绪),操作系统就会通知上层进程向该缓冲区中写入数据。
- fd_set* exceptfds:等待异常就绪文件描述符所在位图。
打开的文件,如TCP中的套接字,当对端关闭套接字以后,自己这边的套接字就会出现异常,此时操作系统就会通知上层进程处理该异常。
需要操作系统监视哪里事件就将对应的位图传给select,待事件就绪后就会通知上层进程去处理,如果不需要监视直接设置成nullptr就行。
- struct timeval* timeout:设置等待方式。

如上图所示struct timeval类型的定义,有两个成员,第一个是秒,第二个是微秒。
该参数传入nullptr的时候,select是阻塞等待,只有当一个或者多个文件描述符就绪时才会通知上层进程去读取数据。
该参数如果是struct timeval timeout = {0, 0},时间设置成0,select是非阻塞等待,就需要使用轮询的方式。
该参数如果设置了具体值,如struct timeval timeout = {5, 0},时间设置成5,select在5秒内阻塞等待,如果在5秒内有文件描述符就绪,则通知上层,如果没有文件描述符就绪则超时返回。
- 返回值:
ret > 0表示有ret个fd就绪了,ret == 0表示超时返回,ret < 0表示select调用失败了。
为了使用方便,内核还提供了一组宏供用户去设置fd_set位图,将对应的文件描述符设置进去,避免了我们自己使用按位或等运算给位图赋值的麻烦:

如上图所示,FD_CLR是将位图中指定文件描述符fd所对应位的状态清零,表示不用操作系统再等待该文件。
FD_ISSET是用来判断特定文件描述符fd是否被设置进了位图,如果设置返回1,没有则返回0。
FD_SET是用来将指定文件描述符fd对应的位图设置为1,表示需要操作系统等待该文件。
FD_ZERO是用来将整个位图清空的,此时操作系统不等待任何一个文件。
select的五个参数中,后面四个都是输入输出型参数,都是传的指针,意味着操作系统和用户共用一个参数。
调用select传参时,表示用户告诉内核,需要操作系统等待哪个文件,以及哪种事件。操作系统第一时间会将传入的位图参数清空,每就绪一个文件,传入位图相应比特位置一,然后传返回值给用户。
此时就是内核在告诉用户,你关心的多个fd,有哪些已经就绪了。
用户根据返回值来处理调用结果,如果是ret>0,则判断自己当初设置进位图中的文件描述符是否被置一了,如果置一了,说明该文件所对应的事件就绪了,用户就可以进行进一步处理。
- 由于传入的参数是输入输出型的,操作系统等待后的结果也是在这个参数中,所以用户必须自己再维护一个数据结构来记录自己最初设置要等待的文件描述符。
- 根据
select返回状态以及操作系统修改后的位图,与自己维护的记录结构作对比,得出自己当初要等待的文件是否就绪的结论。
无论是读取数据,还是写入数据,再或者是异常事件,都是采用这样的方式和机制。
struct timeval* timeout表示的阻塞等待时间,操作系统内部也会进行修改,假设初值是{5, 0}表示阻塞等待5s中,如果5s内没有事件就绪则select返回0,表示超时返回,此时原本传入的time的值也变成了{0, 0}。
如果5s内有事件就绪,假设操作系统等待了3秒,selsect返回值大于0,表示不是超时返回,有事件就绪,此时原本传入的time的值也变成了{2, 0},表示设置的阻塞事件还剩两秒。
- 所以在轮询过程中,每循环一次就需要重新设置一下时间,否则第一次超时返回后,
time的值就成了0,之后select就成了非阻塞IO,与初衷不符。
- 输入输出型参数的作用就是让用户和内核之间相互沟通,互相知晓对方要关心的。
🧁简易select服务器
本喵将创建套接字的代码封装成了一个Sock类,使用的时候直接调用即可,同样可以将其作为一个小组件。

如上图上,Sock类中包含套接字的创建,绑定,监听,以及获取新连接等四个方法,这些函数中还包含日志信息,具体的日志代码本喵就不贴了,有兴趣的小伙伴自行去查阅日志功能。

如上图所示便是select简易服务器的基本配置,成员变量包含监听套接字,端口号,用户维护的用于设置等待位图的数组_fdarry,以及回调函数。
定义了几个全局默认变量,包括默认端口号,select所能等待文件的上限1024,以及用户维护数组的初始默认值。
- initServer():初始化服务器

如上图所示,第一步便是初始化服务器,在系统中创建套接字,进行绑定和监听,构建完整的会话层。之后将用户维护的用来设置等待位图的数组进行初始化,大小是1024个元素,初始值都是-1。
- 0,1,2文件描述符不用
select等待,从listen套接字开始进行等待。
- Start():启动服务器

如上图所示,服务器在启动后,是一个while(1)的死循环,在这个循环中进行一遍又一遍的轮询。
由于select的后面四个参数都是输入输出型参数,所以每一次轮询之前都需要重新设置一遍,否则就会因为操作系统的修改而出现问题。需要重新设置位图状态,重新设置等待时间等等。
- 每次轮询的时候,都需要将用户维护的数组中需要等待的文件描述符重新设置到
fd_set位图中。
在将参数设置好以后便调用select系统调用,用switch将返回的情况分别处理,当n>0时,说明有文件就绪,可以去读取,所以调用HandlerReadEvent函数去处理该事件。
事件处理函数:

如上图所示,当执行到该函数的时候,必有套接字就绪,此时用户要做的就是判断到底是哪个套接字上事件就绪。
第一次执行该函数的时候,必然是监听套接字就绪,因为当前select等待的只有这一个套接字,所以判断符合条件以后,去执行Accepter函数来获取新连接。
- 监听套接字就绪也是属于读事件就绪,因为此时有新连接到来,需要用户去将新连接读取走。

如上图所示,当监听套接字就绪后,用户第一时间肯定就是读取监听到的新连接,并且获取客户端的IP地址以及端口号。
新连接获取成功后,由于此时没有客户端的数据到来,所以新连接套接字没有就绪。
- 此时必须将新连接套接字交给
select去等待就绪,所以需要向用户层维护的数组中添加该套接字的文件描述符。- 如果此时直接读取新连接套接字的话,由于没有就绪,就会阻塞在这里,服务器无法继续执行下去,与我们的初衷就不符合了。

为了方便,本喵还增加了一个Print函数,打印新增加的需要select等待的文件描述符。
第一次之后调用Accepter函数时,已经就绪的文件就不一定是监听套接字了,有可能是前面获取的新连接套接字,所以就需要进行具体判断到底是哪个套接字就绪了。当新连接套接字就绪后,就调用Recver从套接字中读取客户端的请求。

如上图所示,当新连接套接字就绪以后,首先要进行的就是读取套接字中客户端发来的数据,使用recv获取数据。
- 当前这种读取数据的方式是存在一定问题的,因为不能保证套接字中的数据是否被读完了。
但是此时并不会产生影响,所以暂且这样,后面本喵会制定具体的协议去处理它。
如果读取数据出现了问题,或者套接字出现异常,那么就需要关闭套接字,并且将文件描述符从用户维护的数组中清除。
读取成功后将请求处理,并且构建响应发送给客户端,当前的select服务端的重点在于读取,所以写入本喵这里就不详细写了。
- selectServer.cpp

如上图所示就是selectServer.cpp中的代码,服务器的回调函数中仅是将客户端发送来的数据原封不动的响应给客户端,没有做其他处理,其他部分代码本喵不再解释。

如上图所示,当服务器开始运行后,由于此时没有新连接到来,所以监听套接字一直处于未就绪的状态,前面在设置等待事件的时候设置了5s,所以每隔5s就超时返回一次。
- 可以看到当前最大文件描述符是3,也就是监听套接字的文件描述符。

如上图所示,为了避免超时返回的干扰,本喵将select的最后一个参数也设置成nullptr此时select就是阻塞等待,根据运行结果也可以看成是阻塞不动的。

如上图所示,此时使用两个telnet连接客户端,可以看到服务端提示有新事件就绪,此时是监听套接字监听到了两个客户端连接,所以显示两次,服务端每处理一次,用户层维护的数组中文件名描述符就会增加一个,所以最终是3,4,5三个文件需要select进行等待。
两个客户端向服务端发送消息的时候,服务端显示事件就绪,这是进行数据通信的套接字上有数据到来,提醒用户层去读取数据。
- 服务器上只有一个进程(线程),客户端有多个,此时服务端可以同时接收多个客户端的连接请求和数据。
- 多路转接实现了我们之前只能通过多进程或者多线程才能实现的功能,而且效率非常高。
🧁select的特点
select能同时等待的文件fd是有上限的,除非重新修改内核,否则无法解决。
fd_set位图大小只有1024个比特位,意味着select同时最多只能等待1024个文件描述符。

可以看到,本喵的Linux机器上,最多可以打开100001个文件,远大于1024,所以说select能够同时等待的文件数量在高访问量的服务器中是远远不够的。
- 必须借助第三方数组等结构来维护需要
select等待的文件描述符fd。
由于select的后四个参数都是输入输出型参数,操作系统也会修改这几个参数,这就导致用户层必须自己维护一个数组来记录自己曾经想要让select等待的文件描述符。
并且每轮询一次就需要重新设置一次fd_set位图,不仅繁琐,而且对于效率相对较低。
select存在遍历成本。
在上面本喵实现的服务器代码中,对于用户层来说,存在多处遍历所维护的数组。
在将文件描述符设置到fd_set位图中的时候,需要遍历数组中的所有元素来找到合法的fd设置到位图中。
在select完成等待后,同样需要遍历一次数组,来确定是哪个fd的事件就绪了,然后再去处理。
- 内核也要进行遍历
select的第一个参数传参时传入的是最大文件描述符fd + 1,这个参数就是为了让内核确定遍历的范围,这个值之前的所有文件描述符,操作系统都会进行查看,看看是否有事件就绪。
- 内核和用户层来回拷贝的成本问题
select采用的是位图来标记哪个文件的事件就绪,无论是用户层告诉内核,还是内核告诉用户层,都需要拷贝,这样来回进行数据拷贝也是有很大的成本。
🍧总结
多路转接是一个非常重要的IO模式,而select方式只最基础的一种,它主要的作用是带领我们理解多路转接,后面本喵还会讲解poll和epoll方式。
相关文章:
【网络】多路转接——五种IO模型 | select
🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言:你只管努力,剩下的交给时间! 五种IO模型 | select 🍧五种IO模型🍧select🧁认识接口🧁…...
sql顺序倒序查询
要根据 orderNum 字段的顺序查询,你可以使用 SQL 的 ORDER BY 子句。默认情况下,ORDER BY 是按升序排序的,但你可以使用 DESC 关键字来指定降序排序。 以下是一个示例查询,按照 orderNum 字段的顺序将结果返回: SELEC…...
java和sql生成时间维度数据
JAVA: POM依赖: <dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.4.2</version></dependency> 代码: package com.kone.kcdp.common;import cn.hutool.co…...
HUT23级训练赛
目录 A - tmn学长的字符串1 B - 帮帮神君先生 C - z学长的猫 D - 这题用来防ak E - 这题考察FFT卷积 F - 这题考察二进制 G - 这题考察高精度 H - 这题考察签到 I - 爱派克斯,启动! J - tmn学长的字符串2 K - 秋奕来买瓜 A - tmn学长的字符串1 思路&#x…...
sm4 加解密算法工具类( Java 版 )
sm4 加解密算法工具类(java) 说明:密钥是 hexString import java.security.Key; import java.security.Security; import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec;import cn.hutool.core.codec.Base64Decoder; import…...
Redis项目实战——商户查询缓存
目录 为什么要用Redis实现商户查询缓存?用Redis实现商户查询缓存的基本思路?使用Redis缓存的问题及解决方法?一、如何保持数据库数据和Redis缓存数据的一致性?1 内存淘汰机制2 超时剔除机制3 主动更新机制(胜ÿ…...
重磅OpenAI发布ChatGPT企业版本
8月29日凌晨,Open AI官网发布ChatGPT企业版本! 企业版简介: ChatGPT企业版提供企业级安全和隐私、无限的高速 GPT-4 访问、用于处理更长输入的更长上下文窗口、高级数据分析功能、自定义选项等等。人工智能可以协助和提升我们工作生活的各个…...
# Go学习-Day7
文章目录 断言文件打开/关闭文件读取文件写入文件 命令行参数解析Argsflag包 JSON 个人博客:CSDN博客 断言 type Node struct {x inty int }func main() {var a interface{}var n Node Node{1, 2}a nvar b Nodeb a.(Node)fmt.Println(b) }此处我们有一个结构体…...
uniapp-form表单
<template><view class"ptb-20 plr-30 bg min100"><view class"bg-white radius-20 pd-30"><view class"bold mt-30 mb-50 size-32">选择方式:</view><u--form labelPosition"left" :mod…...
漏洞挖掘-利用
一、文章简介 整合一些web漏洞,以及对漏洞的理解。 二、Web漏洞 1.SQL注入 (1)定义 开发者程序编写过程中,对传入用户数据过滤不严格,将可能存在的攻击载荷拼接到SQL查询语句当中,再将这些查询语句传递到…...
React钩子函数之useDeferredValue的基本使用
在React中,使用钩子函数可以方便地管理组件的状态和副作用。useDeferredValue是React 18中新引入的钩子函数之一,它可以帮助我们优化渲染性能,让组件更加流畅。 useDeferredValue的作用是将一个值延迟更新。这个值可以是状态、属性或其他变量…...
lodash常用方法
cloneDeep 克隆 import { cloneDeep,reduce } from lodash; const b {c:1} const a cloneDeep(b)debounce 防抖 import { debounce } from lodash; debounce(() > {}, 300, { trailing: true })()omit方法删除指定属性,返回一个新的对象 import …...
QByteArray与结构体之间相互转换
Qt项目会碰到自定义结构体和字符数组之间的转换问题,不妨假设结构体名字为custom_struct, 字符数组名字为array_data QByteArray转换为自定义结构体 custom_struct *struct_data reinterpret_cast<custom_struct *>(array_data.data());自定义结构体转换为…...
npm如何安装淘宝镜像
通过命令配置 这种方法是通过修改npm的全局配置文件,将默认的镜像源改为淘宝镜像。具体步骤如下: 打开终端,输入以下命令,设置淘宝镜像源:(windowr) npm config set registry https://registr…...
从项目中突显技能:在面试中讲述你的编程故事
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
python的观察者模式案例
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言二、具体代码写在结尾 前言 最近写安卓的代码比较多,了解了java代码的注册回调机制,也就是观察者模式,搜索了一下python也有…...
C语言——类型转换
数据有不同的类型,不同类型数据之间进行混合运算时涉及到类型的转换问题。 转换的方法有两种: 自动转换(隐式转换):遵循一定的规则,由编译系统自动完成强制类型转换:把表达式的运算结果强制转换成所需的数据类型 语法格…...
jmeter性能测试入门完整版
1. Jmeter简介 Apache JMeter是一款纯java编写负载功能测试和性能测试开源工具软件。相比Loadrunner而言,JMeter小巧轻便且免费,逐渐成为了主流的性能测试工具,是每个测试人员都必须要掌握的工具之一。 本文为JMeter性能测试完整入门篇&…...
报错sql_mode=only_full_group_by
首发博客地址 https://blog.zysicyj.top/ 报错内容 ### The error may exist in file[D:\code\cppCode20221025\leader-system\target\classes\mapper\system\TJsonDataMapper.xml] ### The error may involve defaultParameterMap ### The error occurred while…...
伪造 IP 地址的原理和防范措施
在数字化时代,网络安全是至关重要的话题。其中,伪造 IP 地址是一种可能导致网络攻击和欺诈的技术手段。这里将深入探讨伪造 IP 地址的原理以及如何采取措施来防范这种风险。 一.伪造 IP 地址的原理 伪造 IP 地址是一种操纵网络通信的方式,它…...
iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版分享
平时用 iPhone 的时候,难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵,或者买了二手 iPhone 却被原来的 iCloud 账号锁住,这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...
使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装
以下是基于 vant-ui(适配 Vue2 版本 )实现截图中照片上传预览、删除功能,并封装成可复用组件的完整代码,包含样式和逻辑实现,可直接在 Vue2 项目中使用: 1. 封装的图片上传组件 ImageUploader.vue <te…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...
DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态
前言 在人工智能技术飞速发展的今天,深度学习与大模型技术已成为推动行业变革的核心驱动力,而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心,系统性地呈现了两部深度技术著作的精华:…...
Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
Python实现简单音频数据压缩与解压算法
Python实现简单音频数据压缩与解压算法 引言 在音频数据处理中,压缩算法是降低存储成本和传输效率的关键技术。Python作为一门灵活且功能强大的编程语言,提供了丰富的库和工具来实现音频数据的压缩与解压。本文将通过一个简单的音频数据压缩与解压算法…...
STM32标准库-ADC数模转换器
文章目录 一、ADC1.1简介1. 2逐次逼近型ADC1.3ADC框图1.4ADC基本结构1.4.1 信号 “上车点”:输入模块(GPIO、温度、V_REFINT)1.4.2 信号 “调度站”:多路开关1.4.3 信号 “加工厂”:ADC 转换器(规则组 注入…...

