day36-多路IO复用

一、基本概念

(服务器多客户端模型)


定义:单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力


作用:应用程序通常需要处理来自多条事件流中的事件,比如我现在用的电脑,需要同时处理键盘鼠标的输入、中断信号等等事件,再比如web服务器如nginx,需要同时处理来来自N个客户端的事件。


逻辑控制流在时间上的重叠叫做 并发(时间段是并行,但时间点上是串行的)

并行:在一个时间点上同时运行。

而CPU单核在同一时刻只能做一件事情,一种解决办法是对CPU进行时分复用 (多个事件流将CPU切割成多个时间片,不同事件流的时间片交替进行)。在计算机系统中,我们用线程或者进程来表示一条执行流,通过不同的线程或进程在操作系统内部的调度,来做到对CPU处理的时分复用。这样多个事件流就可以并发进行,不需要一个等待另一个太久,在用户看起来他们似乎就是并行在做一样。


使用并发处理的成本:

线程/进程创建成本

CPU切换不同线程/进程成本 Context Switch (上下文切换:页表、寄存器、缓存)

多线程的资源竞争


有没有一种可以在单线程/进程中处理多个事件流的方法呢?一种答案就是IO多路复用。

因此IO多路复用解决的本质问题 是在用更少的资源完成更多的事

二、IO模型

1、阻塞IO

2、非阻塞IO EAGAIN 忙等待 errno

3、信号驱动IO SIGIO 用的相对少(了解)

4、并行模型 进程,线程

5、IO多路复用 select、poll、epoll

2.1阻塞IO

最常用 默认设置

2.2非阻塞IO

在阻塞IO的基础上调整其为不再阻塞等待。

在程序执行阶段调整文件的执行方式为非阻塞:

===》fcntl() ===>动态调整文件的阻塞属性

复制代码
int fcntl(int fd,         int cmd,         ... /* arg */ );
            操作对象    进行何种操作        看第二个参数是否需要

功能:修改指定文件的属性信息。

参数:fd 要调整的文件描述符

cmd 要调整的文件属性宏名称

... 可变长的属性值参数。

返回值:成功 不一定,看cmd; 失败 -1


1.获得原设备状态标志位

2.在原标志位基础上加上 非阻塞

3.当是FILE *文件时,使用fileno,转换为标志位

4.当写0,是只读

eg:修改文件的非阻塞属性:

int flag ;

flag = fcntl(fd,F_GETFL,0); ///获取fd文件的默认属性到flag变量中。

flag = flag | O_NONBLOCK; ///将变量的值调整并添加非阻塞属性

fcntl(fd,F_SETFL,flag); ///将新属性flag设置到fd对应的文件生效。

以上代码执行后的阻塞IO将变成非阻塞方式。

缺点:CPU占用率高

2.3信号驱动IO(了解)

文件描述符需要追加O_ASYNC//信号通知 标志。

设备有io事件可以执行时,内核发送SIGIO信号。

1.追加标志

int flag ;

flag = fcntl(fd,F_GETFL,0);

fcntl(fd,F_SETFL,flag | O_ASYNC); //设置为异步

2.设置信号接收者

fcntl(fd,F_SETOWN,getpid());//常用设置

3.对信号进行捕获

signal(SIGIO,myhandle);

2.4并行模型

1.进程

2.线程

IO 多路复用 ===》并发服务器 ===》TCP协议

3、select循环服务器 ===> 用select函数来动态检测有数据流动的文件描述符

2.5 IO多路复用

2.5.1 select函数

select:

  1. 创建fd集合

  2. 文件描述符加入集合

  3. select等待事件到来

  4. 找到对应的fd,进行读写操作

  5. 清除标志位

    int select(int nfds, fd_set *readfds, fd_set *writefds,
    检测读 一般不检测
    fd_set *exceptfds,
    错误
    struct timeval *timeout);
    超时控制,NULL为阻塞,填秒数/毫秒,都写0,非阻塞工作,只扫一圈,都没有准备好,返-1;


功能:完成指定描述符集合中有效描述符的动态检测。

该函数具有阻塞等待功能,在函数执行完毕后

目标测试集合中将只保留最后有数据的描述符。

参数:nfds 描述符的上限值,一般是链接后描述符的最大值+1;

readfds 只读描述符集

writefds 只写描述符集

exceptfds 异常描述符集

以上三个参数都是 fd_set * 的描述符集合类型

timeout 检测超时 如果是NULL表示一直检测不超时 。

返回值:超时 0

失败 -1

成功 >0
为了配合select函数执行,有如下宏函数:

void FD_CLR(int fd, fd_set *set);

功能:将指定的set集合中编号为fd的描述符号删除。

int FD_ISSET(int fd, fd_set *set);//是否就绪

功能:判断值为fd的描述符是否在set集合中,

如果在则返回真,否则返回假。

void FD_SET(int fd, fd_set *set);

功能:将指定的fd描述符,添加到set集合中。

void FD_ZERO(fd_set *set);

功能:将指定的set集合中所有描述符删除。

2.5.2 epoll函数

epoll:

  1. 创建fd集合(二叉树)
  2. 加入关心的文件描述符
  3. epoll_wait 当 epoll_wait 成功时,它返回准备就绪的文件描述符的数量,如果超时则返回 0 。如果发生错误,则返回 -1 并设置相应的 errno
  4. epoll把准备就绪的fd放入rev集合(数组)
    1.int epoll_create(int size);


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

哪个集合 何种操作 放入谁 监视事件,用户自定义变量

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

-1

0 非阻塞

epoll 解决了select和poll的几个性能上的缺陷:

①不限制监听的描述符个数(poll也是),只受进程打开描述符总数的限制;

②监听性能不随着监听描述 符数的增加而增加,是O(1)的,不再是轮询描述符来探测事件,而是由描述符主动上报事件;

③使用共享内存的方式,不在用户和内核之间反复传递监听的描述 符信息;

④返回参数中就是触发事件的列表,不用再遍历输入事件表查询各个事件是否被触发。

epoll显著提高性能的前提是:监听大量描述符,并且每次触发事件的描述符文件非常少。

epoll的另外区别是:①epoll创建了描述符,记得close;

②支持水平触发和边沿触发。
仅TCP使用(由对方的通信套接字)

getpeername函数是一个网络编程中常用的函数,它用于获取与某个套接字关联的远程协议地址。这个函数在网络通信中非常有用,尤其是在需要获取连接对方的IP地址和端口号时。

int getpeername(int sockfd, struct sockaddr *peeraddr, socklen_t *addrlen);

  • sockfd 是需要获取远程协议地址的套接字描述符。

  • peeraddr 是一个指向struct sockaddr结构的指针,该结构将被填充远程地址信息。

  • addrlen 是一个指向socklen_t 类型的变量,初始时表示peeraddr指向的缓冲区的大小,函数返回时,它包含远程地址的实际大小。

getpeername(conn,(SA),&cli,&len);

相关推荐
Felix Du1 小时前
操作系统 | Linux:第一章 初识Linux
linux·运维·服务器
想睡hhh3 小时前
Linux基础开发工具——vim工具
linux·vim·指令
阿加犀智能3 小时前
快速在高通芯片手机&平板本地部署大模型DeepSeek
android·linux·deepseek
牛奶咖啡133 小时前
配置Linux的网络为静态IP地址的一些方法
linux·配置linux的网络为静态ip·查看linux的网络是否畅通·桥接模式配置网络的方法步骤·连接linux系统的常用工具
xuanwojiuxin3 小时前
Linux 阻塞非阻塞
linux·运维·服务器
n33(NK)3 小时前
Linux从入门到精通
linux·运维·服务器
Invinciblenuonuo3 小时前
Linux【1】-----虚拟内存
linux·运维·服务器
2501_915373883 小时前
linux 更新ollama服务
linux·运维·服务器
Yana.nice3 小时前
Linux向文件每行结尾追加指定内容的方法
linux
弓长三虎3 小时前
linux 命令审计
linux·运维·服务器·前端