系统相关
首页 > 系统相关> > 个人学习-Linux-IO多路复用

个人学习-Linux-IO多路复用

作者:互联网

Linux I/O的多路复用

参考链接:

[1]confirmwz博客:Epoll原理解析https://blog.csdn.net/armlinuxww/article/details/92803381;

[2]hechen知乎专栏: 一文看懂IO多路复用https://zhuanlan.zhihu.com/p/115220699;

weixin_39934085博客: io多路复用的原理和实现_彻底理解 IO 多路复用实现机制https://blog.csdn.net/weixin_39934085/article/details/110715861;

[3]UNIX网络编程:卷1[M].[美]W.Richard Stevens,Bill Fenner, Andrew M.Rudoff著;

什么是IO多路复用?为什么需要IO多路复用

IO多路复用,实际就是通过单线程或者单进程检测若干个文件描述符时候可以执行IO的能力;

当Linux作为服务端,使用socket和客户端进行通信时,执行顺序是,服务端socket启动,调用监听套接字,然后等待服务器链接;

// 伪代码
int lfd = socket();
// 绑定;
bind();
// 监听;
listen(lfd, 128);
// 等待链接:
int cfd = accept();
/* 使用cfd进行通信 */

​ 主线程此时会阻塞等待客户端建立连接,当和客户端完成连接,服务器还需要和别的客户端完成连接,因此使用子线程/进程的方式,和服务器进行通信,是对性能的一种损耗;

​ 多路IO的思路,实际就是把所有需要操作的文件描述符交付内核监控,一旦文件描述符准备就绪,就通知应用程序进行处理,没有就绪,则应用程序阻塞,Linux主要存在三种方式select, poll, epoll 进行管理。(多路指多路网络连接,复用指同一进程)

select函数

函数原型:

SYNOPSIS
     #include <sys/select.h>
     void
     FD_CLR(fd, fd_set *fdset);  // 把文件描述符从fd_set中清楚
     void
     FD_COPY(fd_set *fdset_orig, fd_set *fdset_copy);  
     int
     FD_ISSET(fd, fd_set *fdset);  // 判断fd是否在set中
     void
     FD_SET(fd, fd_set *fdset);   // 把fd插入fd_set
     void
     FD_ZERO(fd_set *fdset);  //  把fd_set全赋0进行初始化;
     int
     select(int nfds, fd_set *restrict readfds, fd_set *restrict writefds,
         fd_set *restrict errorfds, struct timeval *restrict timeout);
		/*
			nfds 最大文件描述符;
			readfds : 读文件描述符集;
			writefds: 写文件描述符集;
			errorfds: 标准错误文件描述符集;
			timeout: 检索遍历超时时间;
		*/

RETURN VALUES
     select() returns the number of ready descriptors that are contained in the
     descriptor sets, or -1 if an error occurred.  If the time limit expires,
     select() returns 0.  If select() returns with an error, including one due
     to an interrupted call, the descriptor sets will be unmodified and the
     global variable errno will be set to indicate the error.

调用过程:

内核监控的fd_set被唤起的条件:

​ readfds:该文件描述符里的读缓冲区存在可读数据;

​ writers: 该描述符写缓冲区可写;

​ errorfds: 见识文件错误异常;

缺点:

select本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理。这样所带来的缺点是:

epoll

相关API原型:

 #include <sys/epoll.h>
int epoll_create(int size);
int epoll_create1(int flags);
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 */
};
op:
EPOLL_CTL_ADD  // 添加文件描述符和相应event到epfd;
EPOLL_CTL_MOD  // 修改对应文件描述符事件;
EPOLL_CTL_DEL  // 删除相应文件描述符;
events:
EPOLLIN
The associated file is available for read(2) operations.
EPOLLOUT
The associated file is available for write(2) operations.
EPOLLRDHUP (since Linux 2.6.17)
Stream socket peer closed connection, or shut down writing half of connection.  (This flag is especially useful for writing simple  code to detect peer shutdown when using Edge Triggered monitoring.)
EPOLLPRI
There is urgent data available for read(2) operations.
EPOLLERR
Error  condition  happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it is not necessary to set it in events.
EPOLLHUP
Hang up happened on the associated file descriptor.  epoll_wait(2) will always wait for this event; it is not necessary to set it in events.
EPOLLET
Sets  the  Edge Triggered behavior for the associated file descriptor.  The default behavior for epoll is Level Triggered.  See epoll(7) for more detailed information about Edge and Level Triggered event distribution architectures.
int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);

调用过程:

1.调用poll_create,Linux会创建一个结构体,每一个epoll对象都会又个epoll_event结构体,来存放添加进来的事件,挂在红黑树下;

2.所有事件都会和设备驱动程序建立回调,相应驱动事件发生时,回调用相应的回调函数;

3.当epoll_ctl添加的fd和event发生时,就把发生的事件复制回用户空间。

优点:

epoll 有 EPOLLLT 和 EPOLLET 两种触发模式,LT 是默认的模式(水平),ET 是 “高速” 模式(边沿)。

标签:set,多路复用,epoll,int,描述符,fd,IO,Linux,FD
来源: https://www.cnblogs.com/Albert-lihai/p/16593436.html