《Linux高性能服务器编程》——I/O多路复用

本文介绍了Linux下的I/O多路转接技术,包括select、poll和epoll三种系统调用的使用,以及LT和ET两种事件触发模式。select用于监听多个文件描述符的读写事件,但存在效率限制;poll改进了select的文件描述符数量限制;epoll则更高效,支持边缘触发(ET)模式,且EPOLLONESHOT能避免多线程竞争同一连接。
摘要由CSDN通过智能技术生成

阅读《Linux高性能服务器编程》的笔记

I/O多路转接

一 基础概念

  • I/O复用能同时监听多个文件描述符,但它本身是阻塞的
  • 当多个文件描述符同时阻塞时,如果不采取额外的措施,只能依次处理,这样服务器看起来就像串行工作

二 select系统调用

  1. 用途

    • 在一段指定时间内,监听文件描述符上的可读、可写和异常事件
  2. API

    #include <sys/select.h>
    int select(int nfds, fd_set* readfds, fd_set* writefds, fd_set* exceptfds, struct timeval* timeout);
    /*
    nfds:被监听的文件描述符总数。通常为所有文件描述符中最大值+1,因为文件描述符从0开始计数
    readfds、writefds、exceptfds:可读、可写、异常事件对应的文件描述符集合。
    timeout:超时时间,结构体中时间=0立即返回,NULL(一直阻塞)
    返回值:
    	>0:就绪文件描述符总数
    	=0:在超时时间内没有文件描述符就绪
    	=-1:失败,设置errno
    	如果在select等待期间,程序收到信号,select立即返回-1,errno=EINTR
    */
    
    FD_ZERO(fd_set *fdset);//清除fdset所有位
    FD_SET(int fd, fd_set* fdset);//设置fdset的位fd
    FD_CLR(int fd, fd_set* fdset);//清楚fdset的位fd
    int FD_ISSET(int fd, fd_set* fdset);//测试fdset的位fd是否被设置
    
    struct timeval
    {
    	long tv_sec; //秒数
    	long tv_usec; //微秒数
    };
    
    
  3. 文件描述符就绪条件
    在这里插入图片描述

    //select读取带外数据
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&exception_fds);
    
    FD_SET(cfd, &read_fds);
    FD_SET(cfd, &exception_fds);
    
    int ret = select(cfd + 1, &read_fds, NULL, &exception_fds, NULL);
    if(ret < 0) 
    {
    	//error
    }
    
    if(FD_ISSET(cfd, &read_fds))
    {
    	ret = recv(cfd, buf, sizeof(buf) - 1, 0)
    	if (ret <= 0)
    	{
    		break;
    	}
    	printf();
    }
    //对于异常事件,采用带MSG_OOB标志的recv函数读取带外数据
    else if (FD_ISSET(cfd, &exception_fds))
    {
    	ret = recv(cfd, buf, sizeof(buf) - 1, MSG_OOB);
    	if(ret <= 0)
    	{
    		break;
    	}
    	printf();
    }
    
    

三 poll系统调用

  1. 用途

    • 在指定时间内轮询一定数量的文件描述符,以测试其中是否有就绪者
  2. API

    #include <poll.h>
    int poll(struct pollfd* fds, nfds_t nfds, int timeout);
    /*
    	fds:指定所有我们感兴趣的文件描述符上发生的可读、可写、异常事件
    	nfds:指定被监听事件集合fds的大小
    	timeout:超时时间,毫秒。-1(poll阻塞)。0(poll立即返回)
    	返回值和select相同
    */
    
    struct pollfd
    {
    	int fd; //文件描述符
    	short events; //注册的事件,一系列事件的按位或
    	short revents; //实际发生的事件,由内核填充
    }
    
    //通常应用程序需要根据recv调用的返回值区分socket上接收到的是有效数据还是对方关闭连接的请求
    //POLLRDHUP事件:socket接收到关闭连接请求时触发
    

四 epoll系列系统调用

  1. API

    #include <sys/epoll.h>
    int epoll_create(int size)
    /*
    	size:内核事件表大小,只是一个提示
    	返回值:一个根节点,表示内核事件表
    */
    
    int epoll_ctl(int efd, int op, int fd, struct epoll_event* event);
    /*
    	epfd:根节点,epoll_create的返回值
    	op:操作类型。EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL
    	fd:需要操作的文件描述符
    	event:指定事件
    	返回值:0(成功),-1(失败,设置errno)
    */
    
    struct epoll_event
    {
    	_unit32_t events; //epoll事件
    	epoll_data_t data; //用户数据
    }
    /*
    	events:EPOLLIN(可读)、EPOLLOUT(可写)、EPOLLET、EPOLLONESHOT
    */
    typedef union epoll_data
    {
    	void *ptr;//指定与fd相关的用户信息
    	int fd;//事件从属的目标文件描述符
    	uint32_t u32;
    	uint64_t u64;
    }epoll_data_t;
    
    int epoll_wait(int epfd, struct epoll_event* events, int maxevents, int timeout);
    /*
    	maxevents:最多监听多少个事件
    	返回值:成功(返回就绪的文件描述符个数),-1(失败,设置errno)
    */
    //epoll_wait如果检测到事件,就将所有就绪的事件从内核事件表(内核态)中复制到events中(用户态)
    
    

五 LT和ET模式

  1. LT模式
    • 默认工作模式,epoll相当于一个效率较高的poll
    • 当epoll_wait检测到有事件发生并将此事件通知应用程序后,**应用程序可以不立即处理该事件。**当应用程序下次调用epoll_wait时,epoll_wait还会再次向应用程序通告此事件,直到该事件被处理。
  2. ET模式
    • EPOLLET
    • 当epoll_wait检测到其上有事件发生并将该事件通知给应用程序后,**应用程序必须立即处理该事件,**因为后续的epoll_wait调用将不再向应用程序通知这一事件
    • 因此ET模式比LT模式效率要高。因为ET模式很大程度上降低了同一个epoll事件被重复触发的次数。
    • ET模式下每个文件描述符都应该是非阻塞轮询的,因为后面epoll不会再去通知该事件了

六 EPOLLONESHOT事件

  • 一个线程读取完某个socket上的数据后开始处理这些数据,处理过程中,该socket上又有新数据可读(EPOLLIN再次被触发),此时另一个线程也来读取数据,就导致了两个线程操作同一个socket
  • EPOLLONESHOT:一个socket连接在任一时刻都只被一个线程处理。
  • 注册了EPOLLONESHOT事件的socket一旦被一个线程处理完,就应该立即重置该socket上的EPOLLONESHOT事件,却保下次能让其他工作线程有机会处理这个socket。

七 三组I/O复用函数的比较

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值