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

【网络】多路转接——poll | epoll

🐱作者:一只大喵咪1201
🐱专栏:《网络》
🔥格言:你只管努力,剩下的交给时间!
tu

书接上文五种IO模型 | select。

poll | epoll

  • 🍧poll
    • 🧁认识接口
    • 🧁简易poll服务器
    • 🧁poll的特点
  • 🍧epoll
    • 🧁认识接口
    • 🧁epoll原理
    • 🧁简易epoll服务器
    • 🧁epoll的特点
    • 🧁epoll的工作方式
  • 🍧总结

🍧poll

poll也是一种多路转接的方案,它专门用来解决select的两个问题:

  • 等待fd有上限的问题。
  • 每次调用都需要重新设置fd_set的问题。

🧁认识接口

图
如上图所示便是poll系统调用的声明,它有三个参数。

  • struct pollfd* fds:用来设置需要等待的fd以及事件

图
如上图所示,struct pollfd结构体中存在三个成员变量,第一个是fd,表示需要操作系统等待的文件描述符。第二个是short events,表示需要操作系统等待该fd的事件类型。第三个是short revents,操作系统告诉用户层该fd的哪个事件就绪了。

此时的文件描述符fd直接设置到struct pollfd结构中即可,需要设置哪个就设置哪个,不用再去寻找对应的位图。

告诉操作系统需要等待的事件时,只需要直接设置short events即可,不用将不同的事件类型放在不同的位图中。

当指定文件描述符fd的就绪时,操作系统会设置对应short revents,用户层直接读取fds中的这个字段便可知道是哪个事件就绪了。

  • struct pollfd结构体将用户和操作系统设置的字段分开了,所以就不存在相互干扰的问题。

events和revents的取值:
图
如上图所示便是用户层以及操作系统可以设置的事件类型,这些同样是一些宏定义,常用的就是POLLIN数据可读,以及POLLOUT数据可写。

假设fds结构体中,events的值是POLLIN,此时操作系统就关注指定文件描述符的读事件是否就绪,如果就绪,就将revents的值也设置成POLLIN,用户层读取到该值后就知道文件可读了。

  • nfds_t nfds:需要poll等待的文件描述符fd的个数。

图
如上图所示,在内核中,nfds_t类型本质上是一个unsigned long int类型,也是一个整形。

第二个参数nfds就是用来设定需要poll等待文件描述符的个数的。用户层和操作系统同时维护一个元素为struct pollfd类型的数组,这个数组中有多少个元素,用户层需要让操作系统等待的文件描述符就有多少个,变量nfds就表示数组的大小。

  • 这个数组就类似用户层和操作系统之间的“临界资源”,双方都能看到,而且都可以访问,由于访问的位置不同,所以不会出现干扰。

由于nfds的值是由用户层设定的,所以poll可同时等待的文件描述符数量并没有上限,unsigned long int的最大值非常大,远大于一个系统能打开的文件个数,所以可以理解为没有上限。

  • int timeout:阻塞等待的时间

select中的struct timeval变量的作用类似,但是这里的timeout是一个int类型的变量,它的单位是1ms。并且它不是一个输入输出型参数,只需要定义一次即可。

timeout>0表示在timout时间以内阻塞等待,超出这个时间就超时返回,如该值是1000就表示阻塞等待1s。

timeout ==0表示非阻塞等待。timeout < 0表示阻塞等待。

  • 返回值:就绪事件的个数。

select的返回值意义一样,本喵就不再解释了。


🧁简易poll服务器

下面本喵用poll实现一下上面select所实现的服务器,大部分代码都一样,本喵仅讲解不一样的部分:

图
如上图所示,poll服务器中,成员变量只是将原本的int* _fdarry类型数组变成了struct pollfd* _rfds类型的数组,其他成员保持不变。

  • initServer():初始化服务器

tu
如上图所示,初始化服务器中,在堆区开辟的数组变成了struct pollfd类型的数组,大小是由户自定义的。

在初始化这个数组的时候,需要初始化struct pollfd中的三个字段,其中fd仍然是-1,reventsevents都是0,本喵将初始化字段放在了一个函数中。

同样需要将数组中第一个需要等待的文件设置成监听套接字,事件设置成POLLIN,表示需要操作系统等待监听套接字中的读事件。

  • Start():启动服务器

tu
如上图所示,可以看到此时Start函数比之前简洁了许多,因为不用每次轮询的时候都重新设置一遍fd_set了,直接调用poll即可,然后根据返回值做具体的处理。

同时阻塞事件也不用再重新设置了,因为并不是一个输入输出型参数,操作系统并不会改变timeout只需要设置一次即可。

HandlerReadEvent():处理事件函数。

图
如上图所示,当事件就绪时,仍然需要调用HandlerReadEvent函数来处理事件,而且仍然需要遍历数组来判断具体是哪个文件描述符的哪个事件就绪了,然后再看是调用Accept还是Recver

  • 此时遍历判断的时候,判断的是struct pollfd中的fd字段,之前所有判断fd的语句中,都需要变成_rfds[i].fd
  • 在判断是否是POLLIN事件就绪时,需要将revents的值与POLLIN进行按位与,如果结果大于0则说明该事件就绪了。

其他部分代码本喵就不讲解了,因为和select中的代码一样,只是遍历判断时由直接的_fdarry[i]变成了_rfds[i].fd,包括pollServer.cpp中的代码都不用作任何修改。

图
如上图,服务器运行起来后,由于没有新连接到来,所以监听套接字每隔5s就超时返回一次。

同样为了避免干扰,将poll设置成阻塞等待方式:

图
如上图所示,将timeout的值设置成-1,表示让poll阻塞等待,可以看到,运行起来后,由于没有新连接到来,程序阻塞不动了。

图
如上图所示,使用两个telnet客户端连接客户端后,现象和selsect一样,也是一个服务端进程可以同时和两个客户端进行通信。

🧁poll的特点

优点:

  • struct pollfd结构包含了要监视的event和发生的revent,不再使用select“参数-值”传递的方式,接口使用比select更方便。
  • poll并没有最大等待文件描述符数量限制 (但是数量过大后性能也是会下降)。

缺点:

  • select一样,poll返回后,需要轮询struct pollfd数组来获取就绪的描述符。
  • 每次调用poll都需要把大量的struct pollfd结构从用户层拷贝到内核中。
  • 同时连接的大量客户端在一时刻可能只有很少的处于就绪状态,因此随着监视的描述符数量的增长, 其效率也会线性下降。

🍧epoll

epoll是基于poll的基础上改进的,它不仅克服了select的缺点,而且解决了poll遍历成本,是效率最高的多路转接模式,但是它也是最复杂的一种模式。

🧁认识接口

epoll_create
图
如上图所示的epoll_create系统调用是用来创建epoll句柄的。

  • int size:自Linux2.6.8以后,该参数是被忽略的,不起实际作用,但是必须是大于0的一个值。
  • 返回值:返回的也是一个文件描述符fd。

epoll句柄在内核中也是一个结构体,类似于struct file,而Linux下一切皆文件,所以返回的也是一个文件描述符,拿着这个文件描述符可以访问到这个epoll句柄。


epoll_ctl

图
如上图所示的epoll_ctl系统调用是用来修改创建的epoll句柄属性的。

  • int epfd:该值就是epoll_create的返回值,用来指示哪个epoll句柄。

  • int op:是修改句柄属性的选项,有增,删,改三个选项:

  • EPOLL_CTL_ADD:向句柄中增加要等待的文件描述符。

  • EPOLL_CTL_MOD:修改句柄中指定的文件描述符。

  • EPOLL_CTL_DEL:从句柄中删除指定的文件描述符。

  • int fd:要进行操作的文件描述符。

  • struct epoll_event* event:用来指定要等待的事件。

图
如上图所示便是内核中struct epoll_event结构体的定义,它有两个成员变量。

第一个成员是uint32_t events,用来设置需要等待的事件,其值也是有几个宏组成的集合:

意义
EPOLLIN表示对应的文件描述符可以读 (包括对端SOCKET正常关闭)
EPOLLOUT表示对应的文件描述符可以写
EPOLLPRI表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来)
EPOLLERR表示对应的文件描述符发生错误
EPOLLHUP表示对应的文件描述符被挂断
EPOLLET将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的
EPOLLONESHOT只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要再次把这个socket加入到EPOLL队列里

第二个参数是一个联合体epoll_data_t data,可以看到有四个成员共用这个联合体,后面本喵会讲解它每个变量的妙用。

  • 返回值:调用成功返回0,调用失败返回-1,并且设置相应的错误码。

epoll_wait

tu
如上图所示的epoll_wait系统调用是用来从操作系统中获取被等待文件描述符的状态的。第一个参数不做解释。

  • struct epoll_event* events:是一个数组,操作系统将就绪的文件描述符放入这个数组中供用户层读取。

  • int maxevents:该值就是events数组的大小,是用户层用来告诉内核这个数组有多大的,这个值不能大于epoll_create时的size

  • int timeout:和poll中是一样的,不再解释。

  • 返回值: 也是和poll的返回值以及select代表的意义一样,大于0表示就绪的文件描述符个数,等于0表示超时返回,小于0表示调用失败。


以上三个系统调用是epoll模型的核心调用,epoll_ctl是用户层用来告诉内核自己的需求的,epoll_wait是内核用来告诉用户层哪些文件描述符的什么事件就绪的。

现在知道了接口的使用,但是仍然并不清除为什么epoll模型能够解决pollselect存在的问题,所以我们需要大概知道epoll模型的底层原理。

🧁epoll原理

网络通信过程中,接收端将数据从网卡(硬件层)开始逐层向上交付,最后给到应用层,那么接收端是如何知道网卡上有数据到来的?也就是操作系统是怎么感知到数据来了呢?

图
如上图所示,本喵将计算机体系结构,冯诺依曼体系结构,以及中断向量表放在了一起来讲解。

当网卡接收到数据后,输入外设(网卡)会自己产生一个控制信号直接给CPU中的控制器,表示此时网卡中有数据到来,可以读了。

  • 冯诺依曼体系中,外设的数据信号不能直接和CPU传递,如上图中红色线条,必须经过存储器。
  • 外设的控制信号可以直接传递给CPU的控制器,如上图黑色线条。
  • 外设给CPU发送一个信号表示数据到来,这叫做中断事件发生。

CPU根据中断信号的编号,去操作系统维护的中断向量表中找到对应的中断服务函数并且执行。

中断服务函数中会调用网卡接收数据的驱动程序,将数据读取并且向上层交付,如上图绿色线条所示。

  • 在这里要重点关注中断服务函数,从网卡中接收数据是从它开始的。

epoll是一个模型,这个模型包含多个数据结构,而前面所讲的句柄,可以理解为是这个模型标志,通过句柄可以找到这个模型,并且使用它。

图
如上图所示是整个epoll模型理论图,包含计算机体系结构中的驱动层,操作系统,系统调用三层。

在调用epoll_create创建模型后,会返回一个文件描述符fd,这个fd同样放在服务器进程PCB所维护的进程描述符表中,通过fd这个句柄就可以找到对应的epoll模型。

  • epoll模型同样是一个大的结构体,只是这个结构体更加复杂,在Linux眼中,都是struct file,所以创建模型后返回的也是一个文件描述符。

上图中操作系统中黑色框内的部分就是epoll模型,包含一个红黑树和一个就绪队列


以增加需要操作系统等待的文件描述符为例,调用epoll_ctl,将fd以及需要等待的事件构建成struct epoll_event变量插入到红黑树中,操作系统会遍历红黑树中所有节点。

  • 红黑树节点中包含很多成员变量,如上图左下角所示,这其中必然有文件描述符fd,需要等待的事件event,左右字节的指针。
  • 还包括nextprev指针。

如果是删除或者修改等操作,同样是在修改这颗红黑树,而红黑树查找效率非常高,所以对应的操作也会很高效。


当操作系统发现红黑树中有节点的事件就绪后,就会将该节点放入到就绪队列中,就绪队列是一个双向循环链表。

将节点从红黑树放入到就绪队列中并没有发生拷贝,秘密就在nextprev指针上。当网卡中有数据到来时,通过中断函数最终调用了网卡驱动程序,在驱动程序中有一个回调函数void* private_data,这是由操作系统提供的。

private_data回调函数会将红黑树节点中的nextprev指针的指向关系做对应的修改,让该节点链入到就绪队列中去。

  • 红黑树的一个节点,它不只属于红黑树,还可能属于就绪队列。
  • 如上图所示,红黑树中的节点和就绪队列中的节点地址都是0x11223344

本喵画的是逻辑图,所以将就绪队列和红黑树分开了。

就绪队列中必然也包括就绪文件的文件描述符,以及就绪的事件,如上图所示的struct epoll_event结构。

  • 所以,凡是处于就绪队列中的节点必然已经就绪。

用户层在调用epoll_wait后,获取的就是内核中就绪队列中的内容,所以获取到的全部都是就绪的事件,所以用户层的struct epoll_event类型数组中,全部都是就绪的事件。

  • epoll_wait将所有就绪的事件,按照顺序放入到用户层传入的数组中。

此时从内核到用户层虽然也需要遍历,但是此时是遍历拷贝,而不需要遍历检测,所以时间复杂度相当于从之前的O(N)变成了O(1),效率提升的不是一点半点。

🧁简易epoll服务器

poll服务器一样,本喵仅讲解不一样的地方,其他和select服务器中有详细讲解。

图

如上图所示是epoll服务器类的基本组成,相比poll服务器有三个变化的成员变量,_epfdepoll模型句柄文件描述符,_revs是用户层获取就绪文件描述符的数组,_num是该数组的大小。

还包含几个全局的默认值,size是调用epoll_create是的参数,要大于0并且大于用户层数组的大小。defaultnum是用户层数组的默认大小。

  • initServer: 初始化服务器

tu
如上图所示初始化服务器代码,创建epoll模型,并且将监听套接字的读事件让操作系统去等待,加入到红黑树中,最后就是在堆区开辟获取就绪事件所用的数组。

在设置struct epoll_event结构体变量的时候,暂时先给data联合体的fd赋值,虽然epoll_ctl的第三个参数才是真正指定要等待的文件描述符。

由于用户层在设置的时候设置的是联合体的fd字段,所以当该文件的事件就绪时,就绪队列中该文件的联合体中仍然是fd字段。

  • Start: 启动服务器

tu
如上图代码所示,Start成员函数的实现更加简单,仅仅是调用了epoll_wait函数,当有事件就绪时,调用HandlerReadEvent时需要传入就绪事件的个数,方便遍历拷贝。

HandlerReadEvent

图
如上图所示,可以看到,此时遍历拷贝的元素全部是已经就绪的事件,而不需要再挨个检测,所以效率非常高。

Accept

图
如上图代码所示,监听到的新连接同样需要交给操作系统去等待,所以需要将新连接的文件描述符添加到红黑树中。

Recver
图
如上图所示,接收数据的代码和之前是一模一样的,不同的是,当新连接出现异常关闭后,需要将新连接的文件描述符从红黑树中删除,让操作系统不用再等待该文件描述符了。


epollServer.cpp源文件同样不需要做修改,直接用之前的就可以。

图
如上图运行结果所示,epoll模型创建好以后,句柄的值是4,监听套接字的文件描述符是3,两个telnet客户端的连接文件描述符是5和6,足以说明该句柄就是一个文件描述符,其他效果和之前的一样。


🧁epoll的特点

  • 接口使用方便:虽然拆分成了三个函数,但是反而使用起来更方便高效,不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离开。
  • 数据拷贝轻量:只在合适的时候调用epoll_ctl将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll是每次循环都要进行拷贝)。
  • 事件回调机制:避免使用遍历检测,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中。epoll_wait返回直接访问就绪队列就知道哪些文件描述符就绪,这个操作时间复杂度是O(1),即使文件描述符数目很多, 效率也不会受到影响。
  • 没有数量限制:文件描述符数目无上限。

虽然epoll的机制更复杂,但是它用起来更方便也更高效。

🧁epoll的工作方式

epoll主要解决的是多路转接中,进行IO的时候等的这一环节,当操作系统所监管的事件就绪了,就会通知用户层来处理事件,这个通知有两种方式:

  • 水平触发(Level Triggered)工作模式:简称LT。
  • 边缘触发(Edge Triggered)工作模式:简称ET。

来举一个生活中的例子,假设你正在打王者荣耀,正要推对方水晶的时候,你妈喊你吃饭,此时就存在两种方式:

  • 如果喊你一次你没动,那么就会继续喊第二次,第三次…,直到你去吃饭,这种方式就是水平触发。
  • 如果喊你一次你没动,之后就不再喊你了,这种方式就是边沿触发。

放在多路转接中就是,事件就绪时,操作系统通知用户层后,用户层没有读取数据或者没有读取完毕,如果操作系统继续通知就是LT模式,如果没有继续通知就是ET模式

  • epoll默认状态下就是LT工作模式。
  • LT模式下,事件未被用户层处理完毕,每调用一次epoll_wait就会返回一个大于0的值。
  • ET模式下,事件未被用户处理完毕,只有第一次调用epoll_wait才会返回大于0的值,之后不再返回,并且将事件设置为未就绪状态,除非该套接字中数据增加,才会再返回一次大于0的值。

在调用epoll_ctl的时候,将struct epoll_event中的uint32_t events字段设置成EPOLLET,此时该文件描述符就变成了ET模式,并没有设置LT模式的方法,因为默认就是LT模式。

使用ET模式能够减少epoll触发的次数,但是代价就是强逼着程序猿一次响应就绪过程中就把所有的数据都处理完,如果不处理完,剩下的数据就有可能被覆盖,后果由程序猿自己承担。

相当于一个文件描述符就绪之后,不会反复被提示就绪,所以就比 LT 更高效一些。


ET模式的高效是建立在程序员的痛苦之上的,由于它只通知用户层一次,如果不一次处理完数据就没机会再处理了,但是,用户层是怎么知道数据有没有读取完毕呢?

  • 答案是:循环读取,直到读不到数据了,就证明读完了。

图
如上图所示,此时就存在一个问题,客户端发送了10K的数据给服务端,服务端收到了epoll的通知后,用户层调用recv进行读取,但是一次没有读取完毕,只读取了1K的数据。

由于此时epoll是ET模式,所以操作系统认为事件已经被处理了,就又将读事件设置成了未就绪的状态,再次读取时recv就会阻塞不动,整个进程就阻塞了,如下面伪代码:

while(1)
{int ret = recv(sock,buffer,sizeof(buffer)-1,0);//第二次读取就会阻塞
}

由于epoll_wait不会再次返回,剩下的9K数据会一直在缓冲区中,直到下一次客户端再给服务器写数据,操作系统再次将读事件设置成就绪状态,才能再次recv

  • 服务端只有将10k数据完全读取完,才会给客户端一个确认应答。
  • 客户端收到服务端的确认应答后才会发送下一个请求。
  • 客户端发送下一个请求,epoll_wait才会返回,才会将读事件设置未继续,服务端才能再次去缓冲区中读取。

服务端无法读取剩余的数据,也就不会发出响应,客户端无法收到响应,也就不会再次发送请求,服务端无法收到再次的请求,就无法再次读取缓冲区中剩余的数据。

时间一长,就会触发TCP的超时重传机制,导致数据被覆盖甚至丢失等问题。

  • 为了解决这个问题,文件描述符对应的缓冲区必须设置成非阻塞 IO方式,本喵在上篇文章中讲解过,使用fcntl设置。
  • 只有非阻塞方式,才能用轮询的方式不断读取缓冲区中的数据,直到读取完毕。

如果是LT模式就不用设置成非阻塞模式,因为数据没有读取完毕,epoll_wait会持续返回,而事件也被保持就绪状态,recv就可以持续读取数据,直到将数据读取完毕。


selectpoll是采用LT模式的,和epoll的默认方式一样,那么如果将文件描述符设置成非阻塞方式,仍然使用LT模式不是更方便吗?既能循环读取,又能让epoll持续返回,也能提高效率啊,为什么仍然要多此一举设计一个ET模式呢?

  • ET模式的高效不仅仅体现在通知机制上,减少通知次数,降低系统调用的开销。
  • ET模式的高效还体现在增加底层网络的吞吐量上。

ET模式表面上看是在倒逼程序员将本轮就绪的数据全部读走,深入网络底层TCP协议去看,服务端由于一次将数据全部读走了,从而能给客户端应答一个更大的窗口值。

客户端就能更新出一个更大的滑动窗口,增加一次发送的数据量,从而提高底层数据发送的效率,更好的利用诸如TCP延迟应答等策略,提高整个网络通信的吞吐量。

  • 所以说,ET模式在压榨程序员的基础上,提高了整个网络通信的效率。

🍧总结

虽然介绍了pollepoll两种方式,但是epoll不仅解决了poll方式的问题,而且还带来了其他优势,比如使用简单,遍历成本低等优势,以及ET模式对于通信效率的提升,虽然epoll的机制更复杂,但是它带来了更好的效果,利远大于弊。

epoll的高性能是有一定的特定场景的,如果场景选择的不适宜,epoll的性能可能适得其反。

  • 对于多连接,且多连接中只有一部分连接比较活跃时,比较适合使用epoll。

例如,一个需要处理上万个客户端的服务器,例如各种互联网APP的入口服务器,这样的服务器就很适合epoll

如果只是系统内部,服务器和服务器之间进行通信,只有少数的几个连接,这种情况下用epoll就并不合适,具体要根据需求和场景特点来决定使用哪种模型。

相关文章:

【网络】多路转接——poll | epoll

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 书接上文五种IO模型 | select。 poll | epoll &#x1f367;poll&#x1f9c1;认识接口&#x1f9c1;简…...

音视频 ffmpeg命令视频录制(Windows)

先安装dshow软件 Screen Capturer Recorder&#xff0c; 项目地址&#xff1a;https://sourceforge.net/projects/screencapturer/files/ 然后查看可用设备名字&#xff1a;ffmpeg -list_devices true -f dshow -i dummy [dshow 0509d6c0] DirectShow video devices (some ma…...

【拾枝杂谈】从游戏开发的角度来谈谈原神4.0更新

君兮_的个人主页 勤时当勉励 岁月不待人 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;结合最近的学习内容和以后自己的目标&#xff0c;今天又开了杂谈这个新坑&#xff0c;分享一下我在学习游戏开发的成长和自己的游戏理解&#xff0c;当然现在还是一枚…...

QT设置mainwindow的窗口title

QT设置mainwindow的窗口title 在QT程序中&#xff0c;通常会有**aaaa-[bbbbbbb]**这种形式的title&#xff0c;对于刚上手qt的程序员同学&#xff0c;可能会简单的以为修改这种title&#xff0c;就是使用setWindowTitle这个接口&#xff0c;其实只对了一半&#xff0c;这种形式…...

SaaS多租户系统架构设计

前言&#xff1a;多租户是SaaS&#xff08;Software-as-a-Service&#xff09;下的一个概念&#xff0c;意思为软件即服务&#xff0c;即通过网络提供软件服务。SaaS平台供应商将应用软件统一部署在自己的服务器上&#xff0c;客户可以根据工作的实际需求&#xff0c;通过互联网…...

Java自定义捕获异常

需求分析 ElectricalCustomerVO electricalCustomerVO new ElectricalCustomerVO(); electricalCustomerVO.setElcNumber(chatRecordsLog.getDeviceNumber()); List<ElectricalCustomerVO> electricalCustomerlist electricalCustomerMapper.selectElectricalCustomer…...

力扣--数组类题目27. 移除元素

给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 示例 1&#xff1a; 输入&#xff1a;nums [3,2,2,3], val 3 输出&#xff1a;2, nums [2,2] 解释&#xff1a;函数应该返回新的长度 2, 并且 n…...

实际并行workers数量不等于postgresql.conf中设置的max_parallel_workers_per_gather数量

1 前言 本文件的源码来自PostgreSQL 14.5&#xff0c;其它版本略有不同 PostgreSQL的并行workers是由compute_parallel_worker函数决定的&#xff0c;compute_parallel_worker是估算扫描所需的并行工作线程数&#xff0c;并不是您在postgresql.conf中设置的max_parallel_work…...

java定位问题工具

一、使用 JDK 自带工具查看 JVM 情况 在我的机器上运行 ls 命令&#xff0c;可以看到 JDK 8 提供了非常多的工具或程序&#xff1a; 接下来&#xff0c;我会与你介绍些常用的监控工具。你也可以先通过下面这张图了解下各种工具的基本作用&#xff1a; 为了测试这些工具&#x…...

【Java】基础入门 (十六)--- 异常

1.异常 1.1 异常概述 异常是指程序在运行过程中出现的非正常的情况&#xff0c;如用户输入错误、除数为零、文件不存在、数组下标越界等。由于异常情况再程序运行过程中是难以避免的&#xff0c;一个良好的应用程序除了满足基本功能要求外&#xff0c;还应具备预见并处理可能发…...

[javaWeb]Socket网络编程

网络编程&#xff1a;写一个应用程序,让这个程序可以使用网络通信。这里就需要调用传输层提供的 api。 Socket套接字 传输层提供协议&#xff0c;主要是两个: UDP和TCP 提供了两套不同的 api&#xff0c;这api也叫做socket api。 UDP和 TCP 特点对比&#xff1a; UDP: 无连…...

<MySon car=“宝马“ :money=“money“></MySon>有没有冒号

为什么car"宝马"没有&#xff1a; 但是 :money"money"就有&#xff1a; <script setup> import {ref} from vue import MySon from /components/MySon.vueconst money ref(100) </script><template><h3>father</h3><My…...

netty(三):NIO——多线程优化

NIO多线程优化 使用Boss线程来处理accepct事件使用Worker线程来处理读写事件&#xff0c;可以创建多个worker线程 package com.review;import lombok.extern.slf4j.Slf4j;import java.io.IOException; import java.net.InetSocketAddress; import java.nio.channels.*; impor…...

Linux操作系统--linux概述

1.Linux概述 Linux&#xff0c;全称GNU/Linux&#xff0c;是一种免费使用和自由传播的类UNIX操作系统&#xff08;OS&#xff09;。简单的说就是一种操作系统。在日常中常见的操作系统有一下三种: 2.linux起源和背景 (1).linux的诞生 linux操作系统是由李纳斯托瓦兹&#xf…...

数组中出现次数超过一半的数字

⭐️ 题目描述 &#x1f31f; OJ链接&#xff1a;数组中出现次数超过一半的数字 思路&#xff1a; 采用投票计数的方式&#xff0c;我们可以把每个数字都看成一次投票并且计数&#xff0c;那么最后剩下来的就是数组中数字出现次数最多的那一个。比如 { 1,2,3,2,2,2,5,4,2 } &a…...

网络优化工程师,你真的了解吗?

一、5G网络优化工程师到底是什么&#xff1f; 5G&#xff0c;就是我们通常所说的第五代移动通信标准&#xff0c;属于目前最热门的新技术趋势。随着2019年5G技术进入正式的商用阶段&#xff0c;拥有广阔的发展前景&#xff0c;备受瞩目。“5G工程师”这个词是一个概念词&#x…...

git 的常用命令

git是一个版本管理器&#xff0c;是程序员必备工具之一&#xff0c;其主分为三个区&#xff1a; 工作区&#xff1a; 暂存区&#xff1a; 仓库&#xff1a; 通过保持软件版本&#xff0c;分支&#xff0c;合并&#xff0c;等多种版本操作&#xff0c;使软件能在自己想要的版本…...

linux如何拷贝文件,删除多余的一级目录,用*号代替所有文件

加上*&#xff0c;代表目录下的所有文件 mv /home/user/dir1/dir1/* /home/user/dir1/可以使用mv命令的通配符来去掉一层目录。 例如&#xff0c;假设有一个名为/home/user/dir1/dir2/file.txt的文件&#xff0c;要将它移动到/home/user/dir2/目录下并去掉dir1目录&#xff0…...

springboot使用properties

一、方式1&#xff1a; 1.1.配置类&#xff1a; package cn.zyq.stater.config;import cn.zyq.stater.bean.User4; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework…...

Android中获取手机SIM卡的各种信息

通过以下工具类方法可以获取到手机SIM的各种信息数据&#xff01;&#xff01;&#xff01; package com.utils; import android.telephony.TelephonyManager; import com.baidu.platform.comapi.map.E; import org.json.JSONArray; import org.json.JSONObject; import java.…...

matlab 根据索引提取点云

目录 一、语法二、说明三、名称-值对应参数1、输入参数2、输出参数四、代码示例五、结果展示六、参考链接一、语法 ptCloudOut = select(ptCloud,indices) ptCloudOut = select(ptCloud,row,column...

蓝芯、四川邦辰面试(部分)

蓝芯 HTTP请求经过MQ异步处理后&#xff0c;怎样返回结果呢&#xff1f;grpc比起spring cloud的优缺点&#xff1f; 四川邦辰 SkyWalking的埋点具体是怎么操作的&#xff1f;newBing: SkyWalking支持两种埋点方式&#xff1a;自动埋点和手动埋点。自动埋点是指通过SkyWalking…...

openCV实战-系列教程13:文档扫描OCR识别下(图像轮廓/模版匹配)项目实战、源码解读

&#x1f9e1;&#x1f49b;&#x1f49a;&#x1f499;&#x1f49c;OpenCV实战系列总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 上篇内容&#xff1a; openCV实战-系列教程11&#xff1a;文档扫描OCR识别上&am…...

SpringBootWeb案例 Part 4

3. 修改员工 需求&#xff1a;修改员工信息 在进行修改员工信息的时候&#xff0c;我们首先先要根据员工的ID查询员工的信息用于页面回显展示&#xff0c;然后用户修改员工数据之后&#xff0c;点击保存按钮&#xff0c;就可以将修改的数据提交到服务端&#xff0c;保存到数据…...

什么是ChatGPT水印,ChatGPT生成的内容如何不被检测出来,原理什么?

太长不看版 1. 什么是ChatGPT水印&#xff1f; ChatGPT水印是AI以伪随机方式生成的独特tokens序列。该序列用来作为水印&#xff0c;以区分AI生成内容和人类原创内容。 2. 如何规避ChatGPT水印&#xff1f; 一种规避方法是使用其他AI模型改写ChatGPT生成的文本。这会破坏水…...

Android 6.0 Settings中添加虚拟键开关

添加系统默认键值 b/frameworks/base/packages/SettingsProvider/res/values/defaults.xml-212,4 212,7 <!-- Default for Settings.Secure.NFC_PAYMENT_COMPONENT --><string name"def_nfc_payment_component"></string><!--mh.modify 2019060…...

Yolov8小目标检测(12):动态稀疏注意力BiFormer | CVPR 2023

💡💡💡本文改进:动态稀疏注意力,cvpr2023。 BiFormer | 亲测在红外弱小目标检测涨点,map@0.5 从0.755提升至0.758 💡💡💡Yolo小目标检测,独家首发创新(原创),适用于Yolov5、Yolov7、Yolov8等各个Yolo系列,专栏文章提供每一步步骤和源码,带你轻松实现小…...

C# VS调试技巧

一.按照条件调试步骤 ①在需要代码执行的行打断点 ②触发此断点&#xff0c;让代码执行到此处 ③鼠标滑至在断点处&#xff0c;点击设置 ④设置断点条件&#xff0c;如下图所示 二、多线程调试技巧 ①在需要代码执行的行打断点 ②触发此断点&#xff0c;让代码执行到此处…...

VS的调试技巧

Visual Studiohttps://visualstudio.microsoft.com/zh-hans/vs/ 目录 1、什么是调试&#xff1f; 2、debug和release 3、调试 3.1、环境 3.2、 快捷键 3.2.1、F10和F11 3.2.2、ctrlF5 3.2.3、F5与F9 3.2.3.1、条件断点 3.3、监视和内存观察 3.3.1、监视 3.3.2、内存 …...

lucene国内镜像 极速下载

文章目录 国内镜像汇总-极速下载【JavaPub版】 lucene国内镜像 https://mirrors.cloud.tencent.com/apache/lucene/ 国内镜像汇总-极速下载【JavaPub版】...

Qt 信号槽连接方式

使用示例&#xff1a; QObject::connect(sender, SIGNAL(signal()), receiver, SLOT(slot()), Qt::AutoConnection); 目录 连接方式 一、AutoConnection 二、DirectConnection 三、QueuedConnection 四、BlockingQueuedConnection 五、UniqueConnection 总结 连接方式 "q…...

(线程池) 100行以内的简单线程池

文章目录 前言CodeThreadPool.hppmain.cpp 简单讲解所需头文件using成员变量构造析构添加任务PS测试效果 END 前言 线程池_百度百科 (baidu.com) 线程池是一种多线程处理形式&#xff0c;处理过程中将任务添加到队列&#xff0c;然后在创建线程后自动启动这些任务。线程池线程都…...

Mysql按姓氏从小到大排序的正确sql

一、前言 最近有个需求&#xff0c;要按姓氏从小到大查询数据。(姓名都是中文的) 写了一个sql&#xff1a; select a.* from mytable a order by substr(a.NAME,1,1) asc结果发现这样不行&#xff0c;排序是乱的。 二、解决办法 查询发现&#xff0c;如果mysql字符集是gbk的…...

【C++】详细介绍模版初阶—函数模版、类模板

文章目录 一、泛型编程二、函数模版2.1 函数模版概念2.2 函数模版格式2.3 函数模版的原理2.4 函数模版的实例化2.5 函数模版的匹配原则 三、类模版3.1 类模版定义3.2 类模版实例化 总结 ヾ(๑╹◡╹)&#xff89;" 人总要为过去的懒惰而付出代价ヾ(๑╹◡╹)&#xff89;&…...

BananaPi BPI-6202工业控制板全志科技A40i、24V DC输入、RS485接口

Banana Pi BPI-6202“嵌入式单板计算机”采用工业级全志A40i四核Cortex-A7处理器&#xff0c;工业温度范围和长生命周期&#xff0c;2GB DDR3&#xff0c;8GB eMMC闪存&#xff0c;M.2 SATA插槽等。 这是自 Banana Pi去年推出Banana Pi BPI-M2 Ultra SBC 和BPI-M2 Berry以来&am…...

Python - functools.partial设置回调函数处理异步任务基本使用

一. 前言 在Python中&#xff0c;回调函数是指在一个函数执行完成后&#xff0c;调用另一个函数的过程。通常情况下&#xff0c;回调函数作为参数传递给原始函数&#xff0c;原始函数在执行完自己的逻辑后&#xff0c;会自动调用回调函数并将结果作为参数传递给它。 二. 回调…...

phpspreadsheet导出excel自动获得列,数字下标

安装composer require phpoffice/phpspreadsheetuse PhpOffice\PhpSpreadsheet\Spreadsheet; use PhpOffice\PhpSpreadsheet\Writer\Xlsx; use PhpOffice\PhpSpreadsheet\Style\Border;$spreadsheet new Spreadsheet(); $sheet $spreadsheet->getActiveSheet();//从65开&a…...

结算日-洛谷

结算日 - 洛谷 解释&#xff1a; 1.用sum记录贝西走到某位置的累计的总钱&#xff0c;flag标记是否有欠债还不了的情况&#xff08;1为有&#xff09;&#xff0c;ans记录步数。 2.若sum<0&#xff0c;则欠债无法还&#xff0c;flag标记为1&#xff0c;并记录下此刻的位置…...

Android Native Code开发学习(一)环境配置

Android Native Code开发学习&#xff08;一&#xff09; 本教程为native code学习笔记&#xff0c;希望能够帮到有需要的人 我的电脑系统为ubuntu 22.04&#xff0c;当然windows也是可以的&#xff0c;区别不大 环境配置 首先我们新建一个native C项目 然后我们下载NDK和C…...

Python GUI应用程序开发之wxPython使用详解

概要 wxPython是一个强大的跨平台GUI工具包&#xff0c;它使用Python编程语言开发&#xff0c;提供了丰富的控件功能。如果你是一名Python开发者&#xff0c;而且希望创建一个功能齐全的桌面应用程序&#xff0c;那么wxPython是一个值得考虑的选择。 什么是wxPython wxPython…...

【电子学会真题】青少年软件编程(C语言)等级考试试卷(一级) 2021年9月

试卷下载 pdf 格式下载&#xff1a;https://download.csdn.net/download/SHUTIAN2010/88255543 word 格式下载&#xff1a;https://download.csdn.net/download/SHUTIAN2010/88255558 1&#xff0e;计算乘积 一行两个整数a、b&#xff0c;以空格分隔。&#xff08;0&#xff1…...

学习完毕JavaSE的感想

今天&#xff0c;把Java复习完毕了&#xff0c;之前学习的时候&#xff0c;学校里学的总是有限的 &#xff0c;自己上手操作之后才发觉差的很多&#xff0c;部署服务器发现要学操作系统&#xff0c;学完了web基础 &#xff0c;又发现还得学前后端分离vue react这些&#xff0c;…...

FastJson的学习

fastjson是阿里巴巴的开源JSON解析库&#xff0c;它可以解析JSON格式的字符串&#xff0c;支持将Java Bean序列化为JSON字符串&#xff0c;也可以从JSON字符串反序列化到JavaBean。 fastjson是json的序列化和反序列化 一、添加依赖 <dependency><groupId>com.ali…...

python scrapy框架

scrapy概述 Scrapy&#xff0c;Python开发的一个快速、高层次的屏幕抓取和web抓取框架&#xff0c;用于抓取web站点并从页面中提取结构化的数据。Scrapy用途广泛&#xff0c;可以用于数据挖掘、监测和自动化测试 scrapy安装 pip install scrapy -i https://pypi.tuna.tsinghua…...

滑动窗口系列3-Leetcode134题加油站

在一条环路上有 n 个加油站&#xff0c;其中第 i 个加油站有汽油 gas[i] 升。 你有一辆油箱容量无限的的汽车&#xff0c;从第 i 个加油站开往第 i1 个加油站需要消耗汽油 cost[i] 升。你从其中的一个加油站出发&#xff0c;开始时油箱为空。 给定两个整数数组 gas 和 cost &…...

LOIC(low orbit ion cannon)

前言 重要的话说三遍&#xff1a; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上&#xff01;&#xff01;&#xff01; 该程序仅用于学习用途&#xff0c;请勿用于非法行为上…...

从格灵深瞳中报稳定盈利,看AI公司的核心竞争力

2023年过半&#xff0c;人工智能产业话题不断。大模型和AIGC掀起热潮&#xff0c;让众多AI公司开始进入新一轮竞赛。但与此同时&#xff0c;不少AI公司依然处于亏损中&#xff0c;研发投入和商业产出难以实现正循环。如何形成健康的商业模式&#xff0c;仍是一大挑战。 AI公司…...

理解 Databend Cluster key 原理及使用

Databend Cluster Key 是指 Databend 可以按声明的 key 排序存储&#xff0c;主要用于用户对时间响应比较高&#xff0c;同时愿意为这个 cluster key 进行额排序操作的用户。 Databend 只支持一个 Cluster key&#xff0c;Cluster key中可以包含多列及表达式。 基本语法 -- 语…...

C++day3(类、this指针、类中的特殊成员函数)

一、Xmind整理&#xff1a; 二、上课笔记整理&#xff1a; 1.类的应用实例 #include <iostream> using namespace std;class Person { private:string name; public:int age;int high;void set_name(string n); //在类内声明函数void show(){cout << "na…...

Qt中的配置文件:实现个性化应用程序配置与保存加载

一、前言 在现代软件开发中,用户对于应用程序的个性化配置和设置变得越来越重要。为了满足用户需求并提供更好的用户体验,开发人员常常需要实现一种机制,以便在每次启动应用程序时能够记住用户上次的配置。这样用户就可以方便地恢复到他们熟悉的环境,无需重新进行所有设置…...