Linux下的IO模型

news2024/11/27 0:34:39

阻塞与非阻塞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.coloradmin.cn/o/2190076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

付费计量系统通用功能(13)

11.17 Class 17: Security function Capability of maintaining the integrity of data elements, functions and processes. 数据单元、功能和过程的可靠性 Maintains the integrity of the system.系统的可靠 Some examples of security function at…

Meta推出Movie Gen 旗下迄今最先进的视频生成AI模型

Meta 今天发布了 MovieGen 系列媒体基础AI模型&#xff0c;该模型可根据文本提示生成带声音的逼真视频。 MovieGen 系列包括两个主要模型&#xff1a; MovieGen Video 和 MovieGen Audio。 MovieGen Video 是一个具有 300 亿个参数的变换器模型&#xff0c;可根据单个文本提示生…

一“填”到底:深入理解Flood Fill算法

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一 floodfill算法是什么&#xff1f; 二 相关OJ题练习 2.1 图像渲染 2.2 岛屿数量 2.3 岛屿的最大面积 2.4 被围绕的区域 2.5 太平洋大西洋水流问题 2.6 扫雷游戏 2.7 衣橱整…

数据科学:Data+AI驾驭数据的智慧之旅

数据科学&#xff1a;DataAI驾驭数据的智慧之旅 前言一、数据存储计算二、数据治理三、结构化数据分析四、语音分析五、视觉分析六、文本分析七、知识图谱 前言 今天想和大家深入聊聊数据科学这个充满魅力又极具挑战的领域。在当今数字化时代&#xff0c;数据如同潮水般涌来&a…

掌握这一招,轻松用Vue和ECharts打造炫酷雷达图——详细教程指南

大家好&#xff0c;今天我要分享的是如何使用ECharts来绘制雷达图。雷达图是一种常用的数据可视化工具&#xff0c;特别适合展示多个量化指标的比较&#xff0c;也可以进行多维度用户行为分析。接下来&#xff0c;我将一步步教大家如何通过ECharts来实现这一效果。效果图如下&a…

mysql事务 -- 事务的隔离性(测试实验+介绍,脏读,不可重复读,可重复度读,幻读),如何实现(RR和RC的本质区别)

目录 事务的隔离性 引入 测试 读未提交 脏读 读提交 不可重复读 属于问题吗? 例子 可重复读 幻读 串行化 原理 总结 事务的隔离性 隔离性的理解 -- mysql事务 -- 如何理解事务,四个属性,查看是否支持事务,事务操作(提交方式,事务的开始和回滚,提交),事务的隔离…

(Django)初步使用

前言 Django 是一个功能强大、架构良好、安全可靠的 Python Web 框架&#xff0c;适用于各种规模的项目开发。它的高效开发、数据库支持、安全性、良好的架构设计以及活跃的社区和丰富的文档&#xff0c;使得它成为众多开发者的首选框架。 目录 安装 应用场景 良好的架构设计…

基于单片机的智能浇花系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于51单片机&#xff0c;采样DHT11温湿度传感器检测温湿度&#xff0c;通过LCD1602显示 4*4按键矩阵可以设置温度湿度阈值&#xff0c;温度大于阈值则开启水泵&#xff0c;湿度大于阈值则开启风扇…

从零开始讲PCIe(6)——PCI-X概述

一、概述 PCI-X 在硬件和软件上与 PCI 具有向后兼容性&#xff0c;同时提供了更高的性能和效率。它使用与 PCI 相同的连接器格式&#xff0c;因此 PCI-X 设备可以插入 PCI 插槽&#xff0c;反之亦然。而且&#xff0c;PCI-X 采用相同的配置模型&#xff0c;因此在 PCI 系统上运…

Apollo9.0 Planning2.0决策规划算法代码详细解析 (4): PlanningComponent::Proc()

&#x1f31f; 面向自动驾驶规划算法工程师的专属指南 &#x1f31f; 欢迎来到《Apollo9.0 Planning2.0决策规划算法代码详细解析》专栏&#xff01;本专栏专为自动驾驶规划算法工程师量身打造&#xff0c;旨在通过深入剖析Apollo9.0开源自动驾驶软件栈中的Planning2.0模块&am…

webpack插件 --- webpack-bundle-analyzer【查看包体积】

const UglifyJsPlugin require(uglifyjs-webpack-plugin) // 清除注释 const CompressionWebpackPlugin require(compression-webpack-plugin); // 开启压缩// 是否为生产环境 const isProduction process.env.NODE_ENV production; const { BundleAnalyzerPlugin } requi…

大数据可视化分析建模论

大数据可视化分析建模论 前言大数据可视化分析建模 前言 在这个信息爆炸的时代&#xff0c;数据如同潮水般涌来&#xff0c;我们每天都在与海量的数据打交道。数据已经成为了企业决策、科研创新以及社会发展的核心要素。如何从这些纷繁复杂的数据中提取有价值的信息&#xff0…

C++多态、虚函数以及抽象类

目录 1.多态的概念 2.多态的定义及实现 2.1多态的构成条件 2.1.1实现多态还有两个必要条件 2.1.2虚函数 2.1.3虚函数的重写/覆盖 2.1.4多态场景的题目 2.1.5虚函数重写的一些其他问题 2.1.5.1协变(了解) 2.1.5.2析构函数的重写 2.1.6override和final关键字 2.…

【Java】JAVA知识总结浅析

Java是一门功能强大的编程语言&#xff0c;广泛应用于多个领域。Java的编程思想&#xff0c;包括面向过程和面向对象编程&#xff0c;Java的发展历史&#xff0c;各版本的特点&#xff0c;JVM原理&#xff0c;数据类型&#xff0c;Java SE与Java EE的区别&#xff0c;应用场景&…

《业务三板斧:定目标、抓过程、拿结果》读书笔记2

为什么要看懂“目标全景图”&#xff1f; 很多管理者在定目标时缺乏全局思维&#xff0c;“只见树木&#xff0c;不见森林”&#xff0c;导 致定出来的目标短浅&#xff0c;管理者如井底之蛙。“目标全景图”是企业的 整个目标体系&#xff0c;如图1-1所示。管理者看懂“目标全…

Pikachu- Over Permission-垂直越权

以admin 账号登陆&#xff0c;添加一个用户&#xff1b; 把添加用户的这个请求发送到 repeater&#xff1b; 退出admin&#xff0c;使用普通用户pikachu登陆&#xff1b; 只有查看权限&#xff1b; 使用pikachu 用户的认证信息&#xff0c;替换repeater处管理员创建用户请求的…

【电力系统】配电网前推后带法求电力系统潮流

摘要 在配电网潮流计算中&#xff0c;前推后带法是一种常用的算法&#xff0c;适用于径向结构配电网。本文通过详细介绍前推后带法的基本原理和计算流程&#xff0c;并结合实际实验结果展示了该方法在电力系统中的应用。实验结果表明&#xff0c;该方法在潮流计算中具有较高的…

Android Framework AMS(02)AMS启动及相关初始化5-8

该系列文章总纲链接&#xff1a;专题总纲目录 Android Framework 总纲 本章关键点总结 & 说明&#xff1a; 说明&#xff1a;本章节主要涉及systemserver启动AMS及初始化AMS相关操作。同时由于该部分内容过多&#xff0c;因此拆成2个章节&#xff0c;本章节是第二章节&…

CSS元素显示类型

display 属性是 CSS 中最重要的属性之一&#xff0c;主要用来控制元素的布局&#xff0c;通过 display 属性您可以设置元素是否显示以及如何显示。 根据元素类型的不同&#xff0c;每个元素都有一个默认的 display 属性值&#xff0c;例如<div>默认的 display 属性值为 …

Pandas基础学习

导入 导入pandas一般是这样导入的 import pandas as pdSeries 创建 s1 pd.Series([5, 17, 3, 26, 31])注意Series的第一个字母要大写&#xff0c;表明这其实是Series类的构建函数, 返回的是Series类的实例 获得元素或者索引 单独获得元素 s1.values单独获得索引值 s…