Linux下的IO模型

阻塞与非阻塞IO(Input/Output)

阻塞与非阻塞IO(Input/Output)是计算机操作系统中两种不同的文件或网络通信方式。它们的主要区别在于程序在等待IO操作完成时的行为。

阻塞IO(Blocking IO)

在阻塞IO模式下,当一个线程发起IO请求(如读取数据或写入数据)时,它会一直等待直到IO操作完成。这意味着在等待数据的过程中,该线程不能执行其他任务,因此被称为“阻塞”。这种模式下,每个IO请求都需要一个独立的线程来处理,因为每个线程都会被阻塞直到IO操作完成。

优点:

  • 编程模型简单,易于理解和实现。
  • 对于IO操作较少的应用,资源消耗相对较小。

缺点:

  • 线程利用率低,因为线程在等待IO操作完成时不能执行其他任务。
  • 可扩展性差,随着并发IO请求的增加,需要更多的线程来处理,这会导致资源消耗增加

常见的阻塞IO函数:

ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

非阻塞IO(Non-blocking IO)

在非阻塞IO模式下,当一个线程发起IO请求时,它会立即返回,不会等待IO操作完成。如果IO操作尚未完成,系统会返回一个特定的错误码(如EWOULDBLOCK)。线程可以继续执行其他任务,直到IO操作准备好,此时系统会通知线程,线程可以再次尝试进行IO操作

优点:

  • 线程可以处理多个IO请求,提高了线程的利用率。
  • 可扩展性好,适用于高并发的IO操作场景。

缺点:

  • 编程模型复杂,需要处理更多的错误码和状态检查。
  • 需要额外的机制(如事件循环、IO多路复用等)来管理多个IO请求

fcntl()函数:

函数头文件:
#include <unistd.h>
#include <fcntl.h>

函数原型:
int fcntl(int fd, int cmd, ... /* arg */ );

函数参数:
fd: 要操作的文件描述符
cmd: 一个命令代码,它指定了要执行的操作
arg: 一个可变参数,它的类型和值取决于 cmd 的值

cmd命令:
F_DUPFD(int):复制文件描述符。指定新文件描述符的最小值
F_GETFD(void):获取文件描述符的状态。
F_SETFD(int):设置文件描述符的状态。可以设置 FD_CLOEXEC 标志
F_GETFL(void):获取文件描述符的状态标志。
F_SETFL(int):设置文件描述符的状态标志。常用的标志有 O_NONBLOCK 设置文件描述符为非阻塞模式 
和 O_APPEND设置文件描述符为追加模式。
F_GETLK(struct flock*):获取文件锁的状态。用于存储当前的锁信息
F_SETLK(struct flock*):设置文件锁。
F_SETLKW(struct flock*):设置文件锁,并等待锁就绪。
...

函数返回值:
成功:
F_DUPFD:新的文件描述符
F_GETFD:文件描述符标志的值
F_GETFL:文件描述符状态的值 
...    
失败:返回-1,并设置错误原因

 在Linux中,可以通过将文件描述符设置为非阻塞模式来实现非阻塞IO。这可以通过fcntl()系统调用完成

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

IO多路复用 

基本思想:由内核来监控多个文件描述符是否可以进行I/O操作,如果有就绪的文件描述符,将结果 告知给用户进程,则用户进程在进行相应的I/O操作

IO多路复用的主要优点:

提高资源利用率:通过减少线程或进程的数量,降低了操作系统在线程上下文切换和维护线程状态上的开销。

提高系统性能:线程不需要在等待IO操作时被阻塞,而是可以继续执行其他任务,直到IO操作准备好。

可扩展性:可以处理大量的并发连接

IO多路复用的机制:

a.select多路复用I/O

在Linux中,select() 系统调用是一种实现IO多路复用的方法,它允许程序同时监视多个文件描述符,以确定是否有文件描述符已经准备好进行IO操作。这意味着程序可以等待多个输入或输出通道,当至少有一个通道可以进行操作时,select() 调用会返回。

函数描述:
函数头文件:
#include <sys/select.h>

函数原型:
int select(int nfds, fd_set *readfds, fd_set *writefds,
                fd_set *exceptfds,struct timeval *timeout);

struct timeval {
               time_t      tv_sec;         /* seconds */
               suseconds_t tv_usec;        /* microseconds */
           };

函数参数:
nfds:最大文件描述符加1
readfds:指向读文件描述符集合的指针。
如果某个文件描述符在返回时有数据可读,该描述符会在这个集合中被设置
writefds:指向写文件描述符集合的指针。
如果某个文件描述符在返回时可以无阻塞地写入数据,该描述符会在这个集合中被设置
exceptfds:指向异常文件描述符集合的指针。通常用于检测异常条件,如错误状态
timeout:指定等待时间,可以指定为NULL,表示无限期等待直到有文件描述符就绪

函数返回值:
成功:返回已经就绪的文件描述符的个数。如果设置timeout,超时就会返回0
失败:-1,并设置errno确定错误原因
 操作文件描述符集合的宏定义:
从文件描述符集合set中移除文件描述符fd
void FD_CLR(int fd, fd_set *set);

检查文件描述符fd是否在文件描述符集合set中
int  FD_ISSET(int fd, fd_set *set);
返回值:如果fd在集合set中,返回非零值;否则返回零。

将文件描述符fd添加到文件描述符集合set中
void FD_SET(int fd, fd_set *set);

初始化或清空文件描述符集合set
void FD_ZERO(fd_set *set);
工作原理:
  1. 内核监控: 当select()被调用时,内核会接管文件描述符集合,并开始监控这些文件描述符的状态。

  2. 数据准备: 当有网络数据到达或文件准备好被读取时,内核会检测到这些状态的变化。

  3. 通知进程: 一旦某个文件描述符就绪,内核会通知调用select()的进程。

  4. 返回就绪集合select()返回时,会返回就绪的文件描述符数量,并更新传递给它的文件描述符集合,以反映哪些文件描述符已经就绪。

  5. 用户空间处理: 应用程序在用户空间检查哪些文件描述符就绪,并进行相应的IO操作。

  6. 超时处理: 如果在指定的超时时间内没有任何文件描述符就绪,select()将返回0,表示超时。

 示例代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/select.h>

#define NFDS 0 
int main()
{
        // 可读的文件描述符集合
        fd_set readfds;
        // 清空可读的文件描述符集合
        FD_ZERO(&readfds);
        // 添加要监控的文件描述符到集合中
        FD_SET(0,&readfds);
        // 设置超时时间
        struct timeval timeout = {.tv_sec=3,.tv_usec=0};
        int result;
        struct timeval timeout_bak;
        fd_set readfds_bak;

        while(1)
        {
                timeout_bak = timeout;
                readfds_bak = readfds;
                int result=select(NFDS+1, &readfds_bak,NULL,NULL, &timeout_bak);
                if(result==-1)
                {
                        perror("select");
                        exit(EXIT_FAILURE);
                }
                else if(result==0)
                {
                        printf("Overtime\n");
                }
                else if(result>0)
                {
                        for(int i=0;i<result;i++)
                        {
                                if(FD_ISSET(0,&readfds)){
                                        char buf[128]={0};
                                        fgets(buf,sizeof(buf),stdin);
                                        printf("buf:%s\n",buf);
                                }
                        }
                }
        }
}
注意事项:
  • select()的所有文件描述符集合都需要在用户空间和内核空间之间复制,这可能会带来性能开销。
  • select()有一个限制,即可以监视的文件描述符数量通常有一个上限(通常是1024),这取决于具体的系统配置。
  • 超时时间在select()返回后可能会被修改,如果需要再次使用原来的超时时间,需要备份timeval结构体。

 b.poll多路复用I/O

select() 类似,但它没有文件描述符的数量限制。这意味着 poll() 可以处理任意数量的文件描述符,只要系统资源(如内存)允许。这使得 poll() 在处理大量并发连接时更为有效

poll():使用一个 pollfd 结构体数组来跟踪文件描述符和事件。每个 pollfd 结构体包含一个文件描述符、期望的事件(events)和实际发生的事件(revents)。这种方式使得 poll() 在处理大量文件描述符时更为高效,因为它直接操作文件描述符数组,而不需要复制整个集合

函数描述:

函数头文件:
#include <poll.h>

函数原型:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

struct pollfd {
               int   fd;         /* file descriptor */
               short events;     /* requested events */
               short revents;    /* returned events */
           };

函数参数:
fds:指向pollfd结构体数组的指针,该数组包含了需要监控的文件描述符。
nfds:fds 数组的长度。
timeout:超时时间,以毫秒为单位。如果设置为-1,则poll()会无限期等待;如果设置为0,则不会等待,立即返回

fd:需要监控的文件描述符。
events:需要监控的事件类型,可以是POLLIN(可读)、POLLOUT(可写)、POLLERR(错误)、POLLHUP(挂起)等。
revents:实际发生的事件,由poll()函数填充

函数返回值:
成功:返回revents就绪的文件描述符的数量,如果超时,返回0
失败:返回-1,并设置error错误原因

 工作原理:

  1. 监控多个文件描述符poll() 通过监控多个文件描述符的状态,允许程序在单个线程内处理多个输入输出源。这通过使用一个文件描述符集合来实现,其中每个文件描述符都可以设置为监控读、写或异常事件。

  2. 事件驱动poll() 采用事件驱动的方式工作。程序指定哪些事件(如可读、可写)感兴趣,poll() 则等待这些事件发生。当任何一个文件描述符上发生了感兴趣的事件时,poll() 调用返回。

  3. 水平触发(Level Triggered)poll() 默认的工作方式是水平触发(LT),这意味着只要文件描述符的状态没有改变,poll() 会持续报告该文件描述符就绪,即使数据已经被读取或写入。

  4. 超时处理: 如果在指定的超时时间内没有任何文件描述符就绪,poll()将返回0,表示超时。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <poll.h>
#define MAX_FDS 10
int main()
{
        struct pollfd fds[MAX_FDS]={0};
        // 将标准输入文件描述符封装成struct pollfd结构体对象
        struct pollfd fd={
                .fd = 0,
                .events=POLLIN
        };
        fds[0]=fd;
        nfds_t nfds = 1;

        int result;

        while(1)
        {
                result = poll(fds, nfds, 2000);
                if(result==-1)
                {
                        perror("poll");
                        exit(EXIT_FAILURE);
                }
                else if(result == 0)
                {
                        printf("overtime\n");
                }
                else if(result>0)
                {
                        for(int i=0;i<nfds;i++)
                        {
                                if(fds[i].revents == POLLIN){
                                        char buf[128]={0};
                                        fgets(buf,sizeof(buf),stdin);
                                        printf("buf:%s\n",buf);
                                }
                        }
                }
        }
        return 0;
}

c. epoll多路复用I/O

epoll相对于selectpoll有较大的不同,主要是针对前面两种多路复用 IO 接口的不足

epoll能够更有效地处理大量并发文件描述符,因为它不需要在每次调用时复制整个文件描述符集合,也不会受到文件描述符数量的限制

epoll 使用了两种主要的数据结构:红黑树和双向链表。

  • 红黑树epoll 在内核中使用红黑树来高效地管理文件描述符。每个注册到 epoll 的文件描述符都会存储在这颗树中,以便快速地进行查找、插入和删除操作。

  • 双向链表epoll 维护了一个就绪列表,这是一个双向链表,用于存储所有准备好进行 IO 操作的文件描述符。当一个文件描述符变得可读或可写时,它会被添加到这个链表中

事件通知机制:

  1. 回调机制(callback):当一个文件描述符的状态发生变化(例如,从不可读变为可读),epoll 会通过回调机制将这个事件添加到就绪列表中。这种机制避免了对所有注册的文件描述符进行轮询,从而提高了效率。

  2. 就绪列表epoll 维护了一个就绪列表,这是一个双向链表,用于存储所有准备好进行 IO 操作的文件描述符。当 epoll_wait() 被调用时,它只需检查这个就绪列表,而不是遍历整个文件描述符集合。

  3. 边缘触发(ET)模式:在边缘触发模式下,epoll 只在文件描述符的状态发生变化时通知应用程序。这意味着应用程序必须读取或写入所有可用的数据,直到遇到 EAGAIN 错误,否则该文件描述符不会被再次报告为就绪。

函数描述:

1.epoll创建函数
函数头文件:
#include <sys/epoll.h>

函数原型:
int epoll_create(int size);

函数参数:
size:这个参数是一个提示,告诉内核预计要监控的文件描述符数量
从Linux 2.6.8开始,这个参数被忽略,epoll_create可以处理的文件描述符数量只受限于系统资源,如内存和内核配置。

函数返回值:
成功:返回一个非负的文件描述符
失败:返回-1,并设置errno以指示错误原因
ENOMEM(内存不足)
ENFILE(打开的文件数量超过限制)
2.epoll控制函数
函数头文件:
#include <sys/epoll.h>

函数原型:
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

typedef union epoll_data {
               void        *ptr;
               int          fd;
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;
struct epoll_event {
               uint32_t     events;   /* Epoll events *//* 监控的事件类型 */
               epoll_data_t data;     /* User data variable *//* 与文件描述符相关联的数据 */
           };

函数参数:
epfd:由 epoll_create() 调用返回的 epoll 实例的文件描述符。
op:要执行的操作类型,可以是以下值之一:
EPOLL_CTL_ADD:添加新的文件描述符到epoll实例。
EPOLL_CTL_MOD:修改已经添加到epoll实例中的文件描述符的监控事件。
EPOLL_CTL_DEL:从epoll实例中删除文件描述符。
fd:要监控的文件描述符。
event:指向epoll_event结构体的指针,该结构体定义了文件描述符上感兴趣的事件

events:指定文件描述符上感兴趣的事件类型,可以是以下值的组合:
EPOLLIN:文件描述符可读。
EPOLLOUT:文件描述符可写。
EPOLLERR:文件描述符发生错误。
EPOLLHUP:文件描述符被挂起。
EPOLLET:设置边缘触发模式(默认是水平触发模式)。
data:用于传递与文件描述符相关联的特定数据,可以是文件描述符本身或其他自定义数据

函数返回值:
成功时:返回0
失败时:返回-1,并设置errno以指示错误原因
3.epoll等待函数
函数头文件:
#include <sys/epoll.h>

函数原型:
int epoll_wait(int epfd, struct epoll_event *events,
                      int maxevents, int timeout);

函数参数:
epfd:由epoll_create()调用返回的epoll实例的文件描述符。
events:指向epoll_event结构体数组的指针,该数组用于从内核接收发生的事件。
maxevents:events数组的最大长度,即可以返回的最大事件数量。
timeout:等待时间,单位为毫秒。如果设置为-1,则无限期等待直到至少有一个文件描述符就绪;如果设置为 0,则不等待,立即返回。

函数返回值:
成功:返回就绪的文件描述符的数量,即 events 数组中填充的事件数量
失败:返回-1,并设置errno以指示错误原因
EBADF(无效的文件描述符)
EFAULT(无效的内存访问)
EINTR(被信号打断)...
​

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/epoll.h>

#define MAX_FDS 10
#define MAX_EVENTS 10
int main()
{
        //创建epoll使用 epoll_create 函数
        int epfd = epoll_create(1);
        if(epfd==-1)
        {
                perror("epoll_create");
                exit(EXIT_FAILURE);
        }
        //添加文件描述符使用 epoll_ctl 函数将文件描述符注册到epoll
        struct epoll_event event;
        event.events = EPOLLIN; // 监听可读事件
        event.data.fd = 0; // 关联文件描述符
        int ret = epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);
        if(ret == -1)
        {
                perror("epoll_ctl");
                exit(EXIT_FAILURE);
        }
        //等待 使用 epoll_wait 函数等待事件
        int nfds;
        struct epoll_event events[MAX_FDS];
        while(1)
        {
                nfds = epoll_wait(epfd, events,MAX_EVENTS,2000);
                if(nfds==-1)
                {
                        perror("epoll_wait");
                        exit(EXIT_FAILURE);
                }
                else if(nfds == 0)
                {
                        printf("overtime\n");
                }
                else if(nfds>0)
                {
                        // epoll_wait返回就绪文件描述符的个数
                        for(int i=0;i<nfds;i++)
                        {
                                if(events[i].data.fd==0){
                                        char buf[128]={0};
                                        fgets(buf,sizeof(buf),stdin);
                                        printf("buf:%s\n",buf);
                                }
                        }
                }
        }
        return 0;
}

结语:

无论你是初学者还是有经验的开发者,我希望我的博客能对你的学习之路有所帮助。如果你觉得这篇文章有用,不妨点击收藏,或者留下你的评论分享你的见解和经验,也欢迎你对我博客的内容提出建议和问题。每一次的点赞、评论、分享和关注都是对我的最大支持,也是对我持续分享和创作的动力

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

无IDEA不Java:快速掌握Java集成开发环境

IntelliJ IDEA是一种强大的Java集成开发环境&#xff0c;是Java开发人员的首选工具之一。本文将介绍IDEA的基本使用方法和常用功能&#xff0c;以帮助初学者快速上手。 安装和配置 首先&#xff0c;需要下载并安装IntelliJ IDEA。在安装完成后&#xff0c;需要配置JDK&#xff…

pygame--超级马里奥(万字详细版)

超级马里奥点我下载https://github.com/marblexu/PythonSuperMario 1.游戏介绍 小时候的经典游戏&#xff0c;代码参考了github上的项目Mario-Level-1&#xff0c;使用pygame来实现&#xff0c;从中学习到了横版过关游戏实现中的一些处理方法。原项目实现了超级玛丽的第一个小…

稀缺森林火险等级预测算法,基于xgboost方法的火险等级预测,共划分5级,依据当前地区月份,降水量,风力等参数进行预测,并提供15000字的报告

森林火险等级预测算法&#xff0c;基于xgboost方法的火险等级预测&#xff0c;共划分5级&#xff0c;依据当前地区月份&#xff0c;降水量&#xff0c;风力等参数进行预测&#xff0c;并提供15000字的报告 森林火险等级预测算法介绍 项目名称 基于XGBoost的森林火险等级预测算…

无环SLAM系统集成后端回环检测模块(loop):SC-A-LOAM以及FAST_LIO_SLAM

最近在研究SLAM目标检测相关知识&#xff0c;看到一篇论文&#xff0c;集成了SC-A-LOAM作为后端回环检测模块&#xff0c;在学习了论文相关内容后决定看一下代码知识&#xff0c;随后将其移植&#xff0c;学习过程中发现我找的论文已经集成了回环检测模块&#xff0c;但是我的另…

mybatis-plus使用总结

基本使用 mybatis-plus依赖 <!-- mybatis-plus开始 --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.7</version></dependency><dependency>&l…

【Linux探索学习】第二弹——Linux的基础指令(中)——夯实基础第二篇

Linux基础指令&#xff08;上&#xff09;&#xff1a;【Linux探索学习】第一弹——Linux的基本指令&#xff08;上&#xff09;——开启Linux学习第一篇-CSDN博客 前言&#xff1a; 在前面我们已经讲解了一些常用的Linux的基础指令&#xff0c;那些当然是远远不够的&#xff…

自定义 shell文件系统

&#x1f3f7;️ 材料准备 创建一个文件:myshell.c: #include <stdio.h>int main() {return 0; }创建一个 Makefile 文件&#xff0c;文件内容如下&#xff1a; 1 mybash:myshell.c2 g -o $ $^ -stdc11 3 .PHONY:…

仿RabbitMQ实现消息队列服务端(二)

文章目录 ⽹络通信协议设计信道管理模块连接管理模块服务器模块实现 ⽹络通信协议设计 其中⽣产者和消费者都是客⼾端&#xff0c;它们都需要通过⽹络和BrokerServer进⾏通信。具体通信的过程我们使⽤Muduo库来实现&#xff0c;使⽤TCP作为通信的底层协议&#xff0c;同时在这个…

【智能大数据分析 | 实验二】Spark实验:部署Spark集群

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈智能大数据分析 ⌋ ⌋ ⌋ 智能大数据分析是指利用先进的技术和算法对大规模数据进行深入分析和挖掘&#xff0c;以提取有价值的信息和洞察。它结合了大数据技术、人工智能&#xff08;AI&#xff09;、机器学习&#xff08;ML&a…

如何编写一个优雅的commit message

在Git中&#xff0c;git commit 命令扮演着至关重要的角色。它的主要作用是将暂存区&#xff08;staging area&#xff09;里的改动内容提交到本地仓库&#xff08;repository&#xff09;中&#xff0c;形成一个新的版本或提交&#xff08;commit&#xff09;。这个过程是 Git…

【HarmonyOS】时间处理Dayjs

背景 在项目中经常会使用要时间的格式转换&#xff0c;比如数据库返回一个Date数据&#xff0c;你需要转成2024-10-2的格式&#xff0c;鸿蒙的原生SDK中是没有办法实现的&#xff0c;因此&#xff0c;在这里介绍第三方封装好并且成熟使用的库Dayjs。 安装 切换到Entry文件夹下…

【学习资源】人在环路的机器学习

说明&#xff1a;本文图片和内容来源 Human-in-the-Loop Machine Learning Human-in-the-Loop Machine Learning Active learning and annotation for human-centered AI by Robert (Munro) Monarch, June 2021 介绍Human-in-the-Loop的目标&#xff0c;学习过程&#xff0c…

gdb 调试 linux 应用程序的技巧介绍

使用 gdb 来调试 Linux 应用程序时&#xff0c;可以显著提高开发和调试的效率。gdb&#xff08;GNU 调试器&#xff09;是一款功能强大的调试工具&#xff0c;适用于调试各类 C、C 程序。它允许我们在运行程序时检查其状态&#xff0c;设置断点&#xff0c;跟踪变量值的变化&am…

基于Arduino的宠物食物分配器

创作本文的初衷是本人的一个养宠物的梦想&#xff08;因为家里人对宠物过敏&#xff0c;因此养宠物的action一直没有落实&#xff09;&#xff0c;但是梦想总是要有的哈哈哈哈哈。上周正好是和一个很好的朋友见面&#xff0c;聊到了养宠物的事情&#xff0c;她大概是讲到了喂宠…

震撼!工业史上第一家万级别规模的工业数字化设备效果图平台

耗时八年打造&#xff0c;国内第一家万级别规模的工业数字化设备效果图平台 平台&#xff1a;www.kingview3d.cn 创作者&#xff1a;kingview3d郭工 行业&#xff1a;煤矿综合自动化、污水处理、净水处理、楼宇暖通、环保工程、医药废水处理、二供、无负压加压站、提升泵站、一…

《NoSQL》非关系型数据库MongoDB 学习笔记!

Mongo基础&#xff1a; 使用数据库&#xff1a; 使用use 命令 后面跟着要使用的数据库名字即可&#xff0c; 例如&#xff1a;use cities, 值得注意的是&#xff0c; mongo中不像mysql&#xff0c; 还需要先创建数据库&#xff0c;后访问&#xff0c; mongo中&#xff0c;你无…

【WebGis开发 - Cesium】如何确保Cesium场景加载完毕

目录 引言一、监听场景加载进度1. 基础代码2. 加工代码 二、进一步封装代码1. 已知存在的弊端2. 封装hooks函数 三、使用hooks方法1. 先看下效果2. 如何使用该hooks方法 三、总结 引言 本篇为Cesium开发的一些小技巧。 判断Cesium场景是否加载完毕这件事是非常有意义的。 加载…

在 Elasticsearch Serverless 上使用 Eland

作者&#xff1a;来自 Elastic Quentin Pradet 本博客将向你展示如何使用 Eland 将机器学习模型导入 Elasticsearch Serverless&#xff0c;然后如何使用类似 Pandas 的 API 探索 Elasticsearch。 Elasticsearch Serverless 中的 NLP 自 Elasticsearch 8.0 起&#xff0c;可以…

SQL专项练习第二天

在数据处理和分析中&#xff0c;Hive 是一个强大的工具。本文将通过五个 Hive 相关的问题展示其在不同场景下的应用技巧。 先在home文件夹下建一个hivedata文件夹&#xff0c;把我们所需的数据写成txt文件导入到/home/hivedata/文件夹下面。 一、找出连续活跃 3 天及以上的用户…

【AI论文精读1】针对知识密集型NLP任务的检索增强生成(RAG原始论文)

目录 一、简介一句话简介作者、引用数、时间论文地址开源代码地址 二、摘要三、引言四、整体架构&#xff08;用一个例子来阐明&#xff09;场景例子&#xff1a;核心点&#xff1a; 五、方法 &#xff08;架构各部分详解&#xff09;5.1 模型1. RAG-Sequence Model2. RAG-Toke…