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

【Linux】IO多路复用——select,poll,epoll的概念和使用,三种模型的特点和优缺点,epoll的工作模式

文章目录

  • Linux多路复用
    • 1. select
      • 1.1 select的概念
      • 1.2 select的函数使用
      • 1.3 select的优缺点
    • 2. poll
      • 2.1 poll的概念
      • 2.2 poll的函数使用
      • 2.3 poll的优缺点
    • 3. epoll
      • 3.1 epoll的概念
      • 3.2 epoll的函数使用
      • 3.3 epoll的优点
      • 3.4 epoll工作模式

Linux多路复用

  IO多路复用是一种操作系统的技术,用于在单个线程或进程中管理多个输入输出操作。它的主要目的是通过将多个IO操作合并到一个系统调用中来提高系统的性能和资源利用率,避免了传统的多线程或多进程模型中因为阻塞IO而导致的资源浪费和低效率问题。

  在IO多路复用中,通常使用的系统调用有 select()、poll()、epoll() 等,它们允许程序等待多个文件描述符(sockets、文件句柄等)中的任何一个变为可读或可写,然后再进行实际的IO操作。这种模型相比于传统的多线程或多进程模型,具有更高的并发处理能力和更低的系统开销。

在这里插入图片描述
  

1. select

1.1 select的概念

  系统提供select函数来实现多路复用输入/输出模型。

  select系统调用是用来让我们的程序监视多个文件描述符的状态变化的;

  程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变。

在这里插入图片描述

  

1.2 select的函数使用

 int select(int nfds, fd_set *readfds, fd_set *writefds,fd_set *exceptfds, struct timeval *timeout);

函数参数:

  nfds:是需要监视的最大的文件描述符值+1。

  readfds:需要检测的可读文件描述符的集合。

  writefds:需要检测的可写文件描述符的集合。

  exceptfds:需要检测的异常文件描述符的集合。

  timeout:为结构体timeval,用来设置select()的等待时间;

  当timeout等于NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了事件;

  当timeout为0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。

  当timeout为特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。

  其中的可读,可写,异常文件描述符的集合是一个fd_set类型,fd_set是系统提供的位图类型,位图的位置是否是1,表示是否关系该事件。

  例如:

    输入时:假如我们要关心 0 1 2 3 文件描述符

    0000 0000->0000 1111 比特位的位置,表示文件描述符的编号
         比特位的内容 0or1 表示是否需要内核关心

    输出时:

    0000 0100->此时表示文件描述符的编号
         比特位的内容 0or1哪些用户关心的fd 上面的读事件已经就绪了,这里表示2描述符就绪了

  

  系统提供了关于fd_set的接口,便于我们使用位图:

 void FD_CLR(int fd, fd_set *set); // 用来清除描述词组set中相关fd 的位int FD_ISSET(int fd, fd_set *set); // 用来测试描述词组set中相关fd 的位是否为真void FD_SET(int fd, fd_set *set); // 用来设置描述词组set中相关fd的位void FD_ZERO(fd_set *set); // 用来清除描述词组set的全部位

  

函数返回值:

  执行成功则返回文件描述词状态已改变的个数。

  如果返回0代表在描述词状态改变前已超过timeout时间,没有返回。

  当有错误发生时则返回-1,错误原因存于errno,此时参数readfds,writefds, exceptfds和timeout的值变成不可预测。

  错误值可能为:

  EBADF 文件描述词为无效的或该文件已关闭
  EINTR 此调用被信号所中断
  EINVAL 参数n 为负值。
  ENOMEM 核心内存不足

  

select的执行过程:

  (1)执行fd_set set; FD_ZERO(&set);则set用位表示是0000,0000。

  (2)若fd=5,执行FD_SET(fd,&set);后set变为0001,0000(第5位置为1) 。

  (3)若再加入fd=2,fd=1,则set变为0001,0011 。

  (4)执行select(6,&set,0,0,0)阻塞等待,表示最大文件描述符+1是6,监控可读事件,立即返回。

  (5)若fd=1,fd=2上都发生可读事件,则select返回,此时set变为0000,0011。注意:没有事件发生的fd=5被清空。

  

1.3 select的优缺点

select的特点:

  (1)可监控的文件描述符个数取决与sizeof(fd_set)的值。一般大小是1024,但是fd_set的大小可以调整。

  (2)将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd。

    1. 是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。

    2. 是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。

  

select缺点

  (1)每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便。

  (2)每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大。

  (3)同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大。

  (4)select支持的文件描述符数量太小。

  

select使用代码:

#pragma once#include <iostream>
#include <sys/select.h>
#include <sys/time.h>
#include "Socket.hpp"using namespace std;static const uint16_t defaultport = 888;
static const int fd_num_max = (sizeof(fd_set) * 8);
int defaultfd = -1;class SelectServer
{
public:SelectServer(uint16_t port = defaultport) : _port(port){for (int i = 0; i < fd_num_max; i++){fd_array[i] = defaultfd;// std::cout << "fd_array[" << i << "]" << " : " << fd_array[i] << std::endl;}}bool Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();return true;}void Accepter(){// 我们的连接事件就绪了std::string clientip;uint16_t clientport = 0;int sock = _listensock.Accept(&clientip, &clientport); // 会不会阻塞在这里?不会if (sock < 0) return;lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sock);// sock -> fd_array[]int pos = 1;for (; pos < fd_num_max; pos++) // 第二个循环{if (fd_array[pos] != defaultfd)continue;elsebreak;}if (pos == fd_num_max){lg(Warning, "server is full, close %d now!", sock);close(sock);}else{fd_array[pos] = sock;PrintFd();// TODO}}void Recver(int fd, int pos){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;cout << "get a messge: " << buffer << endl;}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);fd_array[pos] = defaultfd; // 这里本质是从select中移除}else{lg(Warning, "recv error: fd is : %d", fd);close(fd);fd_array[pos] = defaultfd; // 这里本质是从select中移除}}void Dispatcher(fd_set &rfds){for (int i = 0; i < fd_num_max; i++) // 这是第三个循环{int fd = fd_array[i];if (fd == defaultfd)continue;if (FD_ISSET(fd, &rfds)){if (fd == _listensock.Fd()){Accepter(); // 连接管理器}else // non listenfd{Recver(fd, i);}}}}void Start(){int listensock = _listensock.Fd();fd_array[0] = listensock;for (;;){fd_set rfds;FD_ZERO(&rfds);int maxfd = fd_array[0];for (int i = 0; i < fd_num_max; i++) // 第一次循环{if (fd_array[i] == defaultfd)continue;FD_SET(fd_array[i], &rfds);if (maxfd < fd_array[i]){maxfd = fd_array[i];lg(Info, "max fd update, max fd is: %d", maxfd);}}// accept?不能直接accept!检测并获取listensock上面的事件,新连接到来,等价于读事件就绪// struct timeval timeout = {1, 0}; // 输入输出,可能要进行周期的重复设置struct timeval timeout = {0, 0}; // 输入输出,可能要进行周期的重复设置// 如果事件就绪,上层不处理,select会一直通知你!// select告诉你就绪了,接下来的一次读取,我们读取fd的时候,不会被阻塞// rfds: 输入输出型参数。 1111 1111 -> 0000 0000int n = select(maxfd + 1, &rfds, nullptr, nullptr, /*&timeout*/ nullptr);switch (n){case 0:cout << "time out, timeout: " << timeout.tv_sec << "." << timeout.tv_usec << endl;break;case -1:cerr << "select error" << endl;break;default:// 有事件就绪了,TODOcout << "get a new link!!!!!" << endl;Dispatcher(rfds); // 就绪的事件和fd你怎么知道只有一个呢???break;}}}void PrintFd(){cout << "online fd list: ";for (int i = 0; i < fd_num_max; i++){if (fd_array[i] == defaultfd)continue;cout << fd_array[i] << " ";}cout << endl;}~SelectServer(){_listensock.Close();}private:Sock _listensock;uint16_t _port;int fd_array[fd_num_max];   // 数组, 用户维护的!// int wfd_array[fd_num_max];
};

  

2. poll

2.1 poll的概念

  poll和select实现原理基本类似,

  poll只为了解决select的两个硬伤:

  1.等待的fd是有上限的,(底层类似链表储存实现,而不是位图)

  2.每次要对关心的fd进行事件重置,(pollfd结构包含了要监视的event和发生的event,使用前后不用初始化fd_set)

  

2.2 poll的函数使用

int poll(struct pollfd *fds, nfds_t nfds, int timeout);// pollfd结构
struct pollfd {int fd; /* file descriptor */short events; /* requested events */short revents; /* returned events */
};

函数参数解释:

  fds:是一个poll函数监听的结构列表. 每一个元素中, 包含了三部分内容: 文件描述符, 监听的事件集合, 返回的事件集合。

  nfds:表示fds数组的长度。

  timeout:表示poll函数的超时时间, 单位是毫秒(ms)。

在这里插入图片描述

返回结果:

  返回值小于0, 表示出错。

  返回值等于0, 表示poll函数等待超时。

  返回值大于0, 表示poll由于监听的文件描述符就绪而返回。

  

2.3 poll的优缺点

poll的优点

  (1)pollfd结构包含了要监视的event和发生的event,不再使用select“参数-值”传递的方式. 接口使用比 select更方便。

  (2)poll并没有最大数量限制 (但是数量过大后性能也是会下降)。

poll的缺点

  (1)和select函数一样,poll返回后,需要轮询pollfd来获取就绪的描述符。

  (2)每次调用poll都需要把大量的pollfd结构从用户态拷贝到内核中。

  (3)同时连接的大量客户端在一时刻可能只有很少的处于就绪状态, 因此随着监视的描述符数量的增长, 其效率也会线性下降。

  

poll使用代码:

#pragma once#include <iostream>
#include <poll.h>
#include <sys/time.h>
#include "../select/Socket.hpp"using namespace std;static const uint16_t defaultport = 8888;
static const int fd_num_max = 64;
int defaultfd = -1;
int non_event = 0;class PollServer
{
public:PollServer(uint16_t port = defaultport) : _port(port){for (int i = 0; i < fd_num_max; i++){_event_fds[i].fd = defaultfd;_event_fds[i].events = non_event;_event_fds[i].revents = non_event;// std::cout << "fd_array[" << i << "]" << " : " << fd_array[i] << std::endl;}}bool Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();return true;}void Accepter(){// 我们的连接事件就绪了std::string clientip;uint16_t clientport = 0;int sock = _listensock.Accept(&clientip, &clientport); // 会不会阻塞在这里?不会if (sock < 0) return;lg(Info, "accept success, %s: %d, sock fd: %d", clientip.c_str(), clientport, sock);// sock -> fd_array[]int pos = 1;for (; pos < fd_num_max; pos++) // 第二个循环{if (_event_fds[pos].fd != defaultfd)continue;elsebreak;}if (pos == fd_num_max){lg(Warning, "server is full, close %d now!", sock);close(sock);// 扩容}else{// fd_array[pos] = sock;_event_fds[pos].fd = sock;_event_fds[pos].events = POLLIN;_event_fds[pos].revents = non_event;PrintFd();// TODO}}void Recver(int fd, int pos){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;cout << "get a messge: " << buffer << endl;}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);close(fd);_event_fds[pos].fd = defaultfd; // 这里本质是从select中移除}else{lg(Warning, "recv error: fd is : %d", fd);close(fd);_event_fds[pos].fd = defaultfd; // 这里本质是从select中移除}}void Dispatcher(){for (int i = 0; i < fd_num_max; i++) // 这是第三个循环{int fd = _event_fds[i].fd;if (fd == defaultfd)continue;if (_event_fds[i].revents & POLLIN){if (fd == _listensock.Fd()){Accepter(); // 连接管理器}else // non listenfd{Recver(fd, i);}}}}void Start(){_event_fds[0].fd = _listensock.Fd();_event_fds[0].events = POLLIN;int timeout = 3000; // 3sfor (;;){int n = poll(_event_fds, fd_num_max, timeout);switch (n){case 0:cout << "time out... " << endl;break;case -1:cerr << "poll error" << endl;break;default:// 有事件就绪了,TODOcout << "get a new link!!!!!" << endl;Dispatcher(); // 就绪的事件和fd你怎么知道只有一个呢???break;}}}void PrintFd(){cout << "online fd list: ";for (int i = 0; i < fd_num_max; i++){if (_event_fds[i].fd == defaultfd)continue;cout << _event_fds[i].fd << " ";}cout << endl;}~PollServer(){_listensock.Close();}private:Sock _listensock;uint16_t _port;struct pollfd _event_fds[fd_num_max]; // 数组, 用户维护的!// struct pollfd *_event_fds;// int fd_array[fd_num_max];// int wfd_array[fd_num_max];
};

  

3. epoll

3.1 epoll的概念

在这里插入图片描述

在这里插入图片描述

  epoll: 是为处理大批量句柄而作了改进的poll(真的是大改进)。

  epoll是IO多路复用技术,在实现上维护了一个用于返回触发事件的Socket的链表和一个记录监听事件的红黑树,epoll的高效体现在:

  (1)对监听事件的修改是 log N(红黑树)。

  (2)用户程序无需遍历所有的Socket(发生事件的Socket被放到链表中直接返回)。

  (3)内核无需遍历所有的套接字,内核使用回调函数在事件发生时直接转到对应的处理函数。

  

3.2 epoll的函数使用

  epoll 有3个相关的系统调用:

epoll_create

int epoll_create(int size);

  创建一个epoll的句柄,自从linux2.6.8之后,size参数是被忽略的,用完之后, 必须调用close()关闭。

  

epoll_ctl

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epoll的事件注册函数:

  它不同于select()是在监听事件时告诉内核要监听什么类型的事件, 而是在这里先注册要监听的事件类型。

  第一个参数是epoll_create()的返回值(epoll的句柄)。

  第二个参数表示动作,用三个宏来表示。

  第三个参数是需要监听的fd。

  第四个参数是告诉内核需要监听什么事。

第二个参数的取值:

  EPOLL_CTL_ADD :注册新的fd到epfd中。

  EPOLL_CTL_MOD :修改已经注册的fd的监听事件。

  EPOLL_CTL_DEL :从epfd中删除一个fd。

struct epoll_event结构如下:

在这里插入图片描述

  

events可以是以下几个宏的集合:

  EPOLLIN : 表示对应的文件描述符可以读 (包括对端SOCKET正常关闭)。

  EPOLLOUT : 表示对应的文件描述符可以写。

  EPOLLPRI : 表示对应的文件描述符有紧急的数据可读 (这里应该表示有带外数据到来)。

  EPOLLERR : 表示对应的文件描述符发生错误。

  EPOLLHUP : 表示对应的文件描述符被挂断。

  EPOLLET : 将EPOLL设为边缘触发(Edge Triggered)模式, 这是相对于水平触发(Level Triggered)来说的。

  EPOLLONESHOT:只监听一次事件, 当监听完这次事件之后, 如果还需要继续监听这个socket的话, 需要再次把这个socket加入到EPOLL队列里。

  

epoll_wait

int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);

收集在epoll监控的事件中已经发送的事件:

  参数events是分配好的epoll_event结构体数组。

  epoll将会把发生的事件赋值到events数组中 (events不可以是空指针,内核只负责把数据复制到这个events数组中,不会去帮助我们在用户态中分配内存)。

  maxevents告之内核这个events有多大,这个 maxevents的值不能大于创建epoll_create()时的size。

  参数timeout是超时时间 (毫秒,0会立即返回,-1是永久阻塞)。

  如果函数调用成功,返回对应I/O上已准备好的文件描述符数目,如返回0表示已超时, 返回小于0表示函数失败。

  

epoll原理:

在这里插入图片描述

  (1)当某一进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。

  (2)每一个epoll对象都有一个独立的eventpoll结构体,用于存放通过epoll_ctl方法向epoll对象中添加进来的事件。

  (3)这些事件都会挂载在红黑树中,如此,重复添加的事件就可以通过红黑树而高效的识别出来(红黑树的插入时间效率是lgn,其中n为树的高度)。

  (4)而所有添加到epoll中的事件都会与设备(网卡)驱动程序建立回调关系,也就是说,当响应的事件发生时会调用这个回调方法。

  (5)这个回调方法在内核中叫ep_poll_callback,它会将发生的事件添加到rdlist双链表中。

  (6)在epoll中,对于每一个事件,都会建立一个epitem结构体。

  (7)当调用epoll_wait检查是否有事件发生时,只需要检查eventpoll对象中的rdlist双链表中是否有epitem元素即可。

  (8)如果rdlist不为空,则把发生的事件复制到用户态,同时将事件数量返回给用户. 这个操作的时间复杂度是O(1)。
  

struct eventpoll{ .... /*红黑树的根节点,这颗树中存储着所有添加到epoll中的需要监控的事件*/ struct rb_root rbr; /*双链表中则存放着将要通过epoll_wait返回给用户的满足条件的事件*/ struct list_head rdlist; .... 
};
struct epitem{ struct rb_node rbn;//红黑树节点 struct list_head rdllink;//双向链表节点 struct epoll_filefd ffd; //事件句柄信息 struct eventpoll *ep; //指向其所属的eventpoll对象 struct epoll_event event; //期待发生的事件类型 
}

总结一下, epoll的使用过程简单看就三步:

  (1)调用epoll_create创建一个epoll句柄。

  (2)调用epoll_ctl, 将要监控的文件描述符进行注册。

  (3)调用epoll_wait, 等待文件描述符就绪。

  

3.3 epoll的优点

  (1)接口使用方便: 虽然拆分成了三个函数,但是反而使用起来更方便高效,不需要每次循环都设置关注的文件描述符,也做到了输入输出参数分离开。

  (2)数据拷贝轻量: 只在合适的时候调用 EPOLL_CTL_ADD 将文件描述符结构拷贝到内核中,这个操作并不频繁(而select/poll都是每次循环都要进行拷贝)。

  (3)事件回调机制: 避免使用遍历,而是使用回调函数的方式,将就绪的文件描述符结构加入到就绪队列中,epoll_wait 返回直接访问就绪队列就知道哪些文件描述符就绪,这个操作时间复杂度O(1),即使文件描述符数目很多,效率也不会受到影响。

  (4)没有数量限制: 文件描述符数目无上限。

  

3.4 epoll工作模式

  epoll默认:LT模式,事件到来但是上层不处理,高电平,一直有效。
          ET模式,数据或者连接,从无到有,从有到多,变化的时候才通知我们一次。

  ET的通知效率更高:倒逼程序员,每次通知都必须把本轮的数据取走 -> 循环读取,读取错误 -> fd默认是阻塞的 -> ET,所有的fd必须是非阻塞的。

  ET的IO效率也更高 -> tcp会向对方通告一个更大的窗口,从而概率上让对方一次给我发生更多数据,如果LT每次也可以就绪,那效率差不多。

  本质就是向就绪队列,添加一次或者是多次就绪节点。

  

Epoller.hpp Epoller对epoll进行封装

#pragma once#include "nocopy.hpp"
#include <sys/epoll.h>
#include "Log.hpp"
#include <cstring>
#include <cerrno>//封装我们的epoll,epoll公有继承于我们的nocopy类,不能被拷贝
class Epoller: public nocopy 
{static const int size=128;public:Epoller(){_epfd=epoll_create(size);if(_epfd<0){lg(Error,"epoll_create error: %s",strerror(errno));}else{lg(Info,"epoll_create success: %d",_epfd);}}//进行epoll事件等待//返回的是就绪事件的数量int EpollerWait(struct epoll_event revents[], int num){int n=epoll_wait(_epfd,revents,num,-1/*_timeout*/);return n;}//我们所要更新的时间操作和套接字监控的事件int EpollerUpdate(int oper, int sock, uint32_t event){int n=0;if(oper==EPOLL_CTL_DEL) //删除操作{n=epoll_ctl(_epfd,oper,sock,nullptr);if(n!=0){lg(Error,"epoll_ctl delete error!");}}else //新增和修改{struct epoll_event ev;ev.events=event;ev.data.fd=sock; //传入sock,方便我们知道是哪一个fd就绪//完成了我们对于哪一个文件和那一个文件的描述符进行事件关心//接下来进行注册n=epoll_ctl(_epfd,oper,sock,&ev);if(n!=0){lg(Error,"epoll_ctl error!");}}return n;}~Epoller(){if(_epfd>0){close(_epfd);}}private:int _epfd;int _timeout{3000};
};

  

EpollServer.hpp Epoll服务器

#pragma once#include <iostream>
#include <memory>
#include <sys/epoll.h>
#include "Socket.hpp"
#include "Epoller.hpp"
#include "Log.hpp"
#include "nocopy.hpp"uint32_t EVENT_IN = (EPOLLIN); //表示更新读事件
uint32_t EVENT_OUT = (EPOLLOUT); //表示更新写事件class EpollServer : public nocopy
{static const int num = 64;public:EpollServer(uint16_t port): _port(port),_listsocket_ptr(new Sock()),_epoll_ptr(new Epoller()){}void Init(){_listsocket_ptr->Socket();_listsocket_ptr->Bind(_port);_listsocket_ptr->Listen();lg(Info,"create listen socket success: %d\n",_listsocket_ptr->Fd());}void Accepter(){//获取了一个连接   std::string clientip;uint16_t clientport;int sock=_listsocket_ptr->Accept(&clientip,&clientport);if(sock>0){//我们不能直接读取数据//ssize_t n=read(sock,...);_epoll_ptr->EpollerUpdate(EPOLL_CTL_ADD,sock,EVENT_IN);lg(Info,"get a new link, client info @ %s:%d",clientip.c_str(),clientport);}}void Recver(int fd){// demochar buffer[1024];ssize_t n = read(fd, buffer, sizeof(buffer) - 1); // bug?if (n > 0){buffer[n] = 0;std::cout << "get a messge: " << buffer << std::endl;// wrirtestd::string echo_str = "server echo $ ";echo_str += buffer;write(fd, echo_str.c_str(), echo_str.size());}else if (n == 0){lg(Info, "client quit, me too, close fd is : %d", fd);//细节3_epoll_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);close(fd);}else{lg(Warning, "recv error: fd is : %d", fd);_epoll_ptr->EpollerUpdate(EPOLL_CTL_DEL, fd, 0);close(fd);}}void Dispatcher(struct epoll_event revs[], int num){//遍历获取文件描述符中已经就绪的事件for(int i=0;i<num;i++){uint32_t events=revs[i].events;int fd=revs[i].data.fd;if(events & EVENT_IN) //判断事件类型,这是读事件就绪{if(fd==_listsocket_ptr->Fd()){//获取了一个连接   Accepter();}else {//其他fd上面的普通读取事件就绪Recver(fd);}}else if(events & EVENT_OUT) //写事件就绪{}else {}}}//开始我们的epoll事件监听void Start(){//将listensock添加到epoll中 -> listensock和他关心的事件,添加到内核epoll模型的rb_tree_epoll_ptr->EpollerUpdate(EPOLL_CTL_ADD,_listsocket_ptr->Fd(),EVENT_IN);//我们将我们的监听套接字listsocket给epoll进行读事件管理,接下来由红黑树自动关心我们的事件struct epoll_event revs[num];for(;;){int n=_epoll_ptr->EpollerWait(revs,num);if(n>0){//有事件就绪lg(Debug,"event happend, fd is %d",revs[0].data.fd);//处理就绪事件Dispatcher(revs,n);}else if(n==0){lg(Info,"time out...");}else{lg(Error,"epoll wait error");}}}~EpollServer(){_listsocket_ptr->Close();}private:std::shared_ptr<Sock> _listsocket_ptr;std::shared_ptr<Epoller> _epoll_ptr;uint16_t _port;
};

相关文章:

【Linux】IO多路复用——select,poll,epoll的概念和使用,三种模型的特点和优缺点,epoll的工作模式

文章目录 Linux多路复用1. select1.1 select的概念1.2 select的函数使用1.3 select的优缺点 2. poll2.1 poll的概念2.2 poll的函数使用2.3 poll的优缺点 3. epoll3.1 epoll的概念3.2 epoll的函数使用3.3 epoll的优点3.4 epoll工作模式 Linux多路复用 IO多路复用是一种操作系统的…...

IBCS 虚拟专线——让企业用于独立IP

在当今竞争激烈的商业世界中&#xff0c;企业的数字化运营对网络和服务器的性能有着极高的要求。作为一家企业的 IT 主管&#xff0c;我深刻体会到了在网络和服务器配置方面所面临的种种挑战&#xff0c;以及 IBCS 虚拟专线带来的革命性改变。 我们企业在业务扩张的过程中&…...

驾驭巨龙:Perl中大型文本文件的处理艺术

驾驭巨龙&#xff1a;Perl中大型文本文件的处理艺术 Perl&#xff0c;这门被亲切称为“实用提取和报告语言”的编程语言&#xff0c;自从诞生之日起&#xff0c;就以其卓越的文本处理能力闻名于世。在面对庞大的文本文件时&#xff0c;Perl的强大功能更是得到了充分的体现。本…...

Kafka~特殊技术细节设计:分区机制、重平衡机制、Leader选举机制、高水位HW机制

分区机制 Kafka 的分区机制是其实现高吞吐和可扩展性的重要特性之一。 Kafka 中的数据具有三层结构&#xff0c;即主题&#xff08;topic&#xff09;-> 分区&#xff08;partition&#xff09;-> 消息&#xff08;message&#xff09;。一个 Kafka 主题可以包含多个分…...

springcloud-config 客户端启用服务发现client的情况下使用metadata中的username和password

为了让spring admin 能正确获取到 spring config的actuator的信息&#xff0c;在eureka的metadata中添加了metadata.user.user metadata.user.password eureka.instance.metadata-map.user.name${spring.security.user.name} eureka.instance.metadata-map.user.password${spr…...

云计算 | 期末梳理(中)

1. 经典虚拟机的特点 多态(Polymorphism):支持多种类型的OS。重用(Manifolding):虚拟机的镜像可以被反复复制和使用。复用(Multiplexing):虚拟机能够对物理资源时分复用。2. 系统接口 最基本的接口是微处理器指令集架构(ISA)。应用程序二进制接口(ABI)给程序提供使用硬件资源…...

pytest测试框架pytest-order插件自定义用例执行顺序

pytest提供了丰富的插件来扩展其功能&#xff0c;本章介绍插件pytest-order&#xff0c;用于自定义pytest测试用例的执行顺序。pytest-order是插件pytest-ordering的一个分支&#xff0c;但是pytest-ordering已经不再维护了&#xff0c;建议大家直接使用pytest-order。 官方文…...

吴恩达机器学习 第三课 week2 推荐算法(上)

目录 01 学习目标 02 推荐算法 2.1 定义 2.2 应用 2.3 算法 03 协同过滤推荐算法 04 电影推荐系统 4.1 问题描述 4.2 算法实现 05 总结 01 学习目标 &#xff08;1&#xff09;了解推荐算法 &#xff08;2&#xff09;掌握协同过滤推荐算法&#xff08;Collabo…...

MySQL CASE 表达式

MySQL CASE表达式 一、CASE表达式的语法二、 常用场景1&#xff0c;按属性分组统计2&#xff0c;多条件统计3&#xff0c;按条件UPDATE4, 在CASE表达式中使用聚合函数 三、CASE表达式出现的位置 一、CASE表达式的语法 -- 简单CASE表达式 CASE sexWHEN 1 THEN 男WHEN 2 THEN 女…...

Unity3D 游戏数据本地化存储与管理详解

在Unity3D游戏开发中&#xff0c;数据的本地化存储与管理是一个重要的环节。这不仅涉及到游戏状态、玩家信息、游戏设置等关键数据的保存&#xff0c;还关系到游戏的稳定性和用户体验。本文将详细介绍Unity3D中游戏数据的本地化存储与管理的技术方法&#xff0c;并给出相应的代…...

昇思25天学习打卡营第1天|初学教程

文章目录 背景创建环境熟悉环境打卡记录学习总结展望未来 背景 参加了昇思的25天学习记录&#xff0c;这里给自己记录一下所学内容笔记。 创建环境 首先在平台注册账号&#xff0c;然后登录&#xff0c;按下图操作&#xff0c;创建环境即可 创建好环境后进入即可&#xff0…...

ctfshow-web入门-命令执行(web59-web65)

目录 1、web59 2、web60 3、web61 4、web62 5、web63 6、web64 7、web65 都是使用 highlight_file 或者 show_source 1、web59 直接用上一题的 payload&#xff1a; cshow_source(flag.php); 拿到 flag&#xff1a;ctfshow{9e058a62-f37d-425e-9696-43387b0b3629} 2、w…...

Websocket在Java中的实践——最小可行案例

大纲 最小可行案例依赖开启Websocket&#xff0c;绑定路由逻辑类 测试参考资料 WebSocket是一种先进的网络通信协议&#xff0c;它允许在单个TCP连接上进行全双工通信&#xff0c;即数据可以在同一时间双向流动。WebSocket由IETF标准化为RFC 6455&#xff0c;并且已被W3C定义为…...

python请求报错::requests.exceptions.ProxyError: HTTPSConnectionPool

在发送网页请求时&#xff0c;发现很久未响应&#xff0c;最后报错&#xff1a; requests.exceptions.ProxyError: HTTPSConnectionPool(hostsvr-6-9009.share.51env.net, port443): Max retries exceeded with url: /prod-api/getInfo (Caused by ProxyError(Unable to conne…...

【Unity】Excel配置工具

1、功能介绍 通过Excel表配置表数据&#xff0c;一键生成对应Excel配置表的数据结构类、数据容器类、已经二进制数据文件&#xff0c;加载二进制数据文件获取所有表数据 需要使用Excel读取的dll包 2、关键代码 2.1 ExcelTool类 实现一键生成Excel配置表的数据结构类、数据…...

001 线性查找(lua)

文章目录 迭代器主程序 迭代器 -- 定义一个名为 linearSearch 的函数&#xff0c;它接受两个参数&#xff1a;data&#xff08;一个数组&#xff09;和 target&#xff08;一个目标值&#xff09; function linearSearch(data, target) -- 使用 for 循环遍历数组 data&…...

数据结构之链表

储备知识&#xff1a; 线性表 &#xff1a;一对一的数据所组成的关系称为线性表。 线性表是一种数据内部的逻辑关系&#xff0c;与存储形式无关线性表既可以采用连续的顺序存储(数组)&#xff0c;也可以采用离散的链式存储(链表)顺序表和链表都称为线性表 顺序存储就是将数据存…...

【小工具】 Unity相机宽度适配

相机默认是根据高度适配的&#xff0c;但是在部分游戏中需要根据宽度进行适配 实现步骤 定义标准屏幕宽、高判断标准屏幕宽高比与当前的是否相等通过**&#xff08;标准宽度/当前宽度&#xff09; &#xff08;标准高度 / 当前高度&#xff09;**计算缩放调整相机fieldOfView即…...

centos误删yum和python

在下载pkdg时&#xff0c;因为yum报错坏的解释器&#xff0c;然后误删了yum和python。 在下载各种版本&#xff0c;创建各种软连接&#xff0c;修改yum文件都不好使后&#xff0c;发现了这样一个方法&#xff1a;Centos&#xff1a; 完美解决python升级导致的yum报错问题(相信…...

WP黑格导航主题BlackCandy

BlackCandy-V2.0全新升级&#xff01;首推专题区(推荐分类)更多自定义颜色&#xff01;选择自己喜欢的色系&#xff0c;焕然一新的UI设计&#xff0c;更加扁平和现代化&#xff01; WP黑格导航主题BlackCandy...

elasticsearch底层核心组件

Elasticsearch是一个高度可扩展的开源全文搜索和分析引擎&#xff0c;它基于Apache Lucene构建&#xff0c;并添加了分布式特性。以下是Elasticsearch的一些底层核心组件&#xff1a; 1. **Lucene**&#xff1a; - Elasticsearch基于Apache Lucene&#xff0c;一个高性能的…...

EasyExcel数据导入

前言&#xff1a; 我先讲一种网上信息的获取方式把&#xff0c;虽然我感觉和后面的EasyExcel没有什么关系&#xff0c;可能是因为这个项目这个操作很难实现&#xff0c;不过也可以在此记录一下&#xff0c;如果需要再拆出来也行。 看上了网页信息&#xff0c;怎么抓到&#x…...

20240630 每日AI必读资讯

&#x1f4da;全美TOP 5机器学习博士发帖吐槽&#xff1a;实验室H100数量为0&#xff01; - 普林斯顿、哈佛「GPU豪门」&#xff0c;手上的H100至少三四百块&#xff0c;然而绝大多数ML博士一块H100都用不上 - 年轻的研究者们纷纷自曝自己所在学校或公司的GPU情况&#xff1a…...

第十一章 Qt的模型视图

目录 一、模型/视图的原理 1、原理分析 2、模型(数据模型) 3、视图 4、代理 二、文件系统模型 1、项目练习 2、UI 设计 3、代码实现 三、字符串链表模型 QStringListModel 1、项目效果 2、项目实现 四、标准项模型(QStandardItemModel) 1、模型分析 2、项目效…...

力扣 单词规律

所用数据结构 哈希表 核心方法 判断字符串pattern 和字符串s 是否存在一对一的映射关系&#xff0c;按照题意&#xff0c;双向连接的对应规律。 思路以及实现步骤 1.字符串s带有空格&#xff0c;因此需要转换成字符数组进行更方便的操作&#xff0c;将字符串s拆分成单词列表…...

10款好用不火的PC软件,真的超好用!

AI视频生成&#xff1a;小说文案智能分镜智能识别角色和场景批量Ai绘图自动配音添加音乐一键合成视频https://aitools.jurilu.com/市场上有很多软件&#xff0c;除了那些常见的大众化软件&#xff0c;还有很多不为人知的小众软件&#xff0c;它们的作用非常强大&#xff0c;简洁…...

Windows怎么实现虚拟IP

在做高可用架构时&#xff0c;往往需要用到虚拟IP&#xff0c;在linux上面有keepalived来实现虚拟ip的设置。在windows上面该怎么弄&#xff0c;keepalived好像也没有windows版本&#xff0c;我推荐一款浮动IP软件PanguVip&#xff0c;它可以实现windows上面虚拟ip的漂移。设置…...

【计算机网络】HTTP——基于HTTP的功能追加协议(个人笔记)

学习日期&#xff1a;2024.6.29 内容摘要&#xff1a;基于HTTP的功能追加协议和HTTP/2.0 HTTP的瓶颈与各功能追加协议 需求的产生 在Facebook、推特、微博等平台&#xff0c;每分每秒都会有人更新内容&#xff0c;我们作为用户当然希望时刻都能收到最新的消息&#xff0c;为…...

【多媒体】Java实现MP4视频播放器【JavaFX】【音视频播放】

在Java中播放视频可以使用多种方案&#xff0c;最常见的是通过Swing组件JFrame和JLabel来嵌入JMF(Java Media Framework)或Xuggler。不过&#xff0c;JMF已经不再被推荐使用&#xff0c;而Xuggler是基于DirectX的&#xff0c;不适用于跨平台。而且上述方案都需要使用第三方库。…...

2024 Parallels Desktop for Mac 功能介绍

Parallels Desktop的简介 Parallels Desktop是一款由Parallels公司开发的桌面虚拟化软件&#xff0c;它允许用户在Mac上运行Windows和其他操作系统。通过强大的技术支持&#xff0c;用户无需重新启动电脑即可在Mac上运行Windows应用程序&#xff0c;实现了真正的无缝切换。 二…...

颍川韩氏,来自战国七雄韩国的豪族

颍川是战国七雄韩国故土&#xff0c;韩国被秦国灭国后&#xff0c;王公贵族们除了坚决反秦的被杀了外&#xff0c;大部分都留存了下来。这些人在楚、汉反秦战争中&#xff0c;成为反秦统一战线的重要力量&#xff0c;其中两人先后被封为重新恢复的韩国的国王。 一个是横阳君韩…...

Spring boot中如何使用Thymeleaf模板

大家好&#xff0c;我是 网创有方。今天给大家分享下Spring boot中如何使用Thymeleaf模板。 在 IntelliJ IDEA 中使用 Thymeleaf 模板引擎来开发 Spring Boot 应用程序是相对简单的。以下是一些基本步骤&#xff0c;帮助你在 IDEA 中设置和使用 Thymeleaf&#xff1a; 创建一个…...

单片机学习(14)--DS18B20温度传感器

DS18B20温度传感器 13.1DS18B20温度传感器基础知识1.DS18B20介绍2.引脚及应用电路3.内部结构框图4.存储器框图5.单总线介绍6.单总线电路规范7.单总线时序结构8.DS18B20操作流程9.DS18B20数据帧 13.2DS18B20温度读取和温度报警器代码1.DS18B20温度读取&#xff08;1&#xff09;…...

ue 材质贴图Tiling repeat

材质问题&#xff0c;如下 贴图显然不符合逻辑&#xff0c;太大&#xff0c;并且是一次性贴图 换一个红砖纹理&#xff0c;就看清了&#xff0c;砖太大了 修改&#xff1a; 拖出一个TexCoord&#xff0c;代表坐标&#xff0c;拖出一个参数&#xff0c;代表次数&#xff0c;如…...

【图像超分辨率】一个简单的总结

文章目录 图像超分辨率(Image Super-Resolution, ISR)1 什么是图像超分辨率&#xff1f;2 图像超分辨率通常有哪些方法&#xff1f;&#xff08;1&#xff09;基于插值的方法&#xff08;2&#xff09;基于重建的方法&#xff08;3&#xff09;基于学习的方法&#xff08;LR im…...

WEB与低代码:B/S架构在开发中的应用与优势

在互联网迅猛发展的今天&#xff0c;WEB应用已经成为人们日常生活和工作中不可或缺的一部分。随着技术的进步和需求的多样化&#xff0c;开发高效、灵活且易于维护的WEB应用变得尤为重要。B/S架构&#xff08;Browser/Server Architecture&#xff09;作为一种常见的WEB应用架构…...

内容营销专家刘鑫炜揭秘:姜萍一夜暴红背后的品牌传播密码

在互联网的浪潮下&#xff0c;品牌传播的方式愈发多样和复杂。近日&#xff0c;江苏省涟水中等专业学校的十七岁中专生姜萍因在世界级数学竞赛中取得优异成绩而一夜暴红&#xff0c;成为网络上的热议焦点。 在这个充满变数的时代&#xff0c;谁也无法预测下一个网红会是谁。然…...

安装VEX外部编辑器

Houdini20配置VEX外部编辑器方法_哔哩哔哩_bilibili 下载并安装Visual Studio Code软件&#xff1a;Download Visual Studio Code - Mac, Linux, Windows 在Visual Studio Code软件内&#xff0c;安装相关插件&#xff0c;如&#xff1a; 中文汉化插件vex插件 安装Houdini Expr…...

ISO 19110全局要求类/req/global/bound-association-role要求的详细解释

/req/global/bound-association-role 要求: 如果模型允许在一个关联角色&#xff08;association role&#xff09;中存在“rolePlayer”关联&#xff0c;并且该角色属于一个“globalProperty”角色&#xff0c;那么这种绑定必须通过一个绑定的关联角色实体&#xff08;bound …...

武汉凯迪正大等简述电缆电容检测:原理、应用与重要性

为了确保电缆的安全稳定运行评估电缆绝缘质量以及检测潜在故障&#xff0c;需要对电缆做一些必要的检测。本文将依照凯迪正大的一些经验对电缆电容检测的原理、应用及其重要性进行简单的序述。 一、电缆电容检测的原理 电缆电容检测基于电容的基本特性&#xff0c;电容是指两个…...

python调用阿里云OSS对象存储

1)安装SDK import oss2 print(oss2.__version__) 如果能返回SDK版号,则安装成功 2)配置访问凭证 import oss2 from project1 import settings #以下参数在阿里云账号中可以查到 auth=oss2.Auth(settings.ACCESS_KEY_ID, settings.ACCESS_KEY_SECRET) endpoint=settings.E…...

530、二叉搜索树的最小绝对差

给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 代码如下&#xff1a; class Solution { private: int result INT_MAX; TreeNode* pre NULL; void traversal(TreeNode…...

docker配置redis主从复制

下载redis,复制redis.conf 主节点(6379) 修改redis.conf # bind 127.0.0.1 # 注释掉这里 protected-mode no # 改为no port 6379从节点(6380) 修改redis.conf bind 127.0.0.1 protected-mode no # 改为no port 6380 replicaof 172.17.0.2 6379 # 这里的ip为主节点容器的i…...

IPython调试秘籍:pdb调试器深度解析与实战

&#x1f41e; IPython调试秘籍&#xff1a;pdb调试器深度解析与实战 在Python编程中&#xff0c;调试是开发过程中不可或缺的一环。IPython&#xff0c;作为一个强大的交互式Python解释器&#xff0c;内置了pdb调试器&#xff0c;使得代码调试变得异常便捷。本文将深入探讨如…...

MySQL 死锁处理

查询是否锁表 SHOW OPEN TABLES WHERE In_use > 0; 查看正在锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCKS; 查看等待锁的事务 SELECT * FROM INFORMATION_SCHEMA.INNODB_LOCK_WAITS; 查看进程信息 SHOW PROCESSLIST; 或者 SELECT * FROM INFORMATION_SCHE…...

scatterlist的相关概念与实例分析

概念 scatterlist scatterlist用来描述一块内存&#xff0c;sg_table一般用于将物理不同大小的物理内存链接起来&#xff0c;一次性送给DMA控制器搬运 struct scatterlist {unsigned long page_link; //指示该内存块所在的页面unsigned int offset; //指示该内存块在页面中的…...

leetCode.97. 交错字符串

leetCode.97. 交错字符串 题目思路 代码 class Solution { public:bool isInterleave(string s1, string s2, string s3) {int n s1.size(), m s2.size();if ( s3.size() ! n m ) return false;vector<vector<bool>> f( n 1, vector<bool> (m 1));s1 …...

算力时代,算能(SOPHGO)的算力芯片/智算板卡/服务器选型

数字经济时代&#xff0c;算力成为支撑经济社会发展新的关键生产力&#xff0c;全球主要经济体都在加快推进算力战略布局。随着大模型持续选代&#xff0c;模型能力不断增强&#xff0c;带来算力需求持续增长。算力对数字经济和GDP的提高有显著的带动作用&#xff0c;根据IDC、…...

ManageEngine连续荣登Gartner 2024年安全信息和事件管理魔力象限

我们很高兴地宣布&#xff0c;ManageEngine再次在Gartner的安全信息和事件管理&#xff08;SIEM&#xff09;魔力象限中榜上有名&#xff0c;这是我们连续第七年获得这一认可。 Gartner ManageEngine Log360是一款全面的SIEM解决方案&#xff0c;旨在帮助组织有效处理日志数据…...

51单片机第11步_在C语言中插入汇编语言

本章重点介绍如何在C语言中插入汇编语言。要不是有记录&#xff0c;真不知道怎么搞。 /* 你在 Project Workspace窗口中,将光标移到DELAY.c处,点下鼠标右键,选择"Options for file DELAY.c", 点击右边的"Generate Assembler SRC File"和“Assemble SRC …...

openssl error:0A000126:SSL routines:ssl3_read_n:unexpected eof

os: ubuntu22.04 openssl s_client -showcerts -connect github.com:443 返回出错&#xff0c;导致wget curl https网站都不成功。 key error info: 40079A1F82130000:error:0A000126:SSL routines:ssl3_read_n:unexpected eof while reading:ssl/record/rec 我电脑上遇到这…...

基于Qt实现的PDF阅读、编辑工具

记录一下实现pdf工具功能 语言&#xff1a;c、qt IDE&#xff1a;vs2017 环境&#xff1a;win10 一、功能演示&#xff1a; 二、功能介绍&#xff1a; 1.基于saribbon主体界面框架&#xff0c;该框架主要是为了实现类似word导航项 2.加载PDF放大缩小以及预览功能 3.pdf页面跳转…...

Clojure学习:运行在 Java 虚拟机(JVM)上的动态Lisp编程语言

因为HY的学习资料较少&#xff0c;所以通过学习Clojure来辅助学习HY。前期也看过一些Common Lisp和On Lisp的文档&#xff0c;现在还不太清楚HY跟谁最接近。 文档&#xff1a;Clojure - Getting Started 在FreeBSD下安装Clojure 在FreeBSD系统&#xff0c;可以直接使用pkg进…...

golang 通过reflect反射方式调用对象方法 (动态方法调用) 详解

在go语言中有一个reflect反射包是非常强大的一个包&#xff0c; 通过反射我们几乎可以对对象干任何事情&#xff0c; 今天就给大家讲一下通过反射动态调用对象方法 的方法。 “ 对象方法 的方法” 这里怎么会有2个“方法” 是不是有点绕&#xff1f; 非也&#xff0c;这里的第…...

Oh My Zsh Git 插件

以下是一些常见的别名和它们对应的 Git 命令&#xff1a; g: gitga: git addgaa: git add --allgapa: git add --patchgau: git add --updategb: git branchgba: git branch -agbd: git branch -dgbda: git branch --no-color --merged | command grep -vE “^(||*|\s*(main|m…...

茗鹤 | 如何借助APS高级计划排程系统提高汽车整车制造的效率

在我们做了详尽的市场调研及头部汽车制造企业排程需求沟通后&#xff0c;我们发现尽管企业有很多的业务系统做支撑&#xff0c;在计划排程领域&#xff0c;所有的汽车制造总装厂仍旧使用人工“Excel”做排产规划&#xff0c;其中少部分也会借助MRP、第三方辅助排产工具。鉴于我…...

2024年激光雷达上车提速,车型标配率增长至6.3%

据盖世汽车研究院2024年1-5月激光雷达供应商排行榜显示,国内激光雷达供应商装机量累计超44.5万颗,速腾聚创以43%市占率位居首位,激光雷达装机量持续加速渗透,车型标配率增长至6.3%。2024年1~5月车载激光雷达累计装机量超44.5万颗,2023年同期为11万颗,同比增长304.5%。“激…...

云原生架构内涵_3.主要架构模式

云原生架构有非常多的架构模式&#xff0c;这里列举一些对应用收益更大的主要架构模式&#xff0c;如服务化架构模式、Mesh化架构模式、Serverless模式、存储计算分离模式、分布式事务模式、可观测架构、事件驱动架构等。 1.服务化架构模式 服务化架构是云时代构建云原生应用的…...

huggingface的self.state与self.control来源(TrainerState与TrainerControl)

文章目录 前言一、huggingface的trainer的self.state与self.control初始化调用二、TrainerState源码解读(self.state)1、huggingface中self.state初始化参数2、TrainerState类的Demo 三、TrainerControl源码解读(self.control)总结 前言 在 Hugging Face 中&#xff0c;self.s…...

习近平将出席中国—阿拉伯国家合作论坛第十届部长级会议开幕式并发表主旨讲话

新华社北京5月26日电 外交部发言人华春莹26日宣布:经中阿双方共同商定,中国—阿拉伯国家合作论坛第十届部长级会议将于5月30日在北京举行。国家主席习近平将出席会议开幕式并发表主旨讲话。中共中央政治局委员、外交部长王毅将同阿方主席、毛里塔尼亚外长马尔祖克共同主持会议…...

软件构造复习的一些经验笔记

软件构造复习的一些经验笔记 术语解释 LSP原则&#xff08;里氏替换原则&#xff09; 什么是LSP原则&#xff0c;就是A类继承B类&#xff0c;A类应该比B类的spec&#xff08;规约&#xff09;更强 换句话说&#xff1a;你爹会做鱼香肉丝&#xff0c;你爹的手艺遗传给了你&a…...

Linux文本文件管理003

★排序、去重、统计★ 1&#xff09;排序 sort -n按照数值排序 -r降序排列 2&#xff09;去重 uniq 过滤相邻、重复的行 -c 对重复行计数 3&#xff09;统计 wc 统计文件中的字节数、单词数、行数 -l 显示行数 今天通过使用grep、awk、cut指令和上面几个选项提取文本文件…...