好久没用I/O复用了,感觉差点儿相同都快忘完了。记得当初刚学I/O复用的时候花了好多时间。可是因为那会不太爱写博客,导致花非常多时间搞明确的东西,依旧非常easy忘记。俗话说眼过千遍不如手过一遍,的确。在以后的学习中,不管知识的难易亦或是重要程度怎样。我都会尽量义博客的形式记录下来,这样即能用博客来督促自己学习,也能加深对知识的理解俩全其美,好了废话不说了。

I/O复用的基本概述

I/O复用技术主要是用来同一时候监听多个套接字描写叙述符,使得我们的程序大幅度的提高性能,一般例如以下情况会用到I/O复用技术

(1)程序须要同一时候处理多个socket

(2)客户端程序需同一时候处理用户输入和网络连接

(3)TCPserver要同一时候处理监听socket和连接socket

(4)server要同一时候处理TCP和UDP请求

(5)server要同一时候处理多个端口

1.select系统调用

#include<sys/select.h>
int select(int nfds,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,struct timeval *timeout);

.ndf參数指定被监听文件描写叙述符个数,它通常被设为select监听的全部文件描写叙述符加1。

.readfds,writefds,exceptfds參数分别指向可读,可写和异常事件,应用程序通过将自己感兴趣的文件描写叙述符增加到相应的集合中去,select调用返回时。内核将改动他们来通知应用程序哪些文件描写叙述符已经就绪,timeout为超时时间,select调用成功返回就绪的文件描写叙述符个数

我们一般使用例如以下宏来訪问fd_set中的位

#include<sys/select.h>
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的位是否被设置

文件描写叙述符就绪条件

(1)socket内核接收缓冲区大于或等于其低水位标志SO_RCVLOWAT.此时我们能够无堵塞的读该socket

(2)socket通信的对方关闭连接,此时对该socket的读操作将返回0

(3)监听socket上有新的连接请求

(4)socket上有未处理的错误

(5)socket的内核发送缓冲区大于其低水位字节SO_SNDLOWAT

(6)socket使用非堵塞connect连接成功或失败之后

(7)socket上有未处理的错误

详细实例

參考伪代码例如以下

#include<stdio.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<unistd.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<sys/types.h>
#include<sys/select.h> int main(void)
{
if(argc < 3)
{
cout<<"參数有误"<<endl;
} char *ip = argv[1];
int port = atoi(argv[2]); int ret = 0;
struct sockaddr_in address;
bzero(&address,sizeof(adddress));
address.sin_family = AF_INET;
inet_pton(AF_INET,ip,&address.sin_addr);
address.sin_port = htons(port); int listenfd = socket(AF_INET,SOCK_STREAM,0);
assert(ret != -1); ret = bind(listenfd,(struct sockaddr *)&address,sizeof(address));
assert(ret != -1); ret = listen(listenfd,5);
assert(ret != -1); struct sockaddr_in client_address;
socklen_t len = sizeof(client_address); int connfd = accept(listenfd,(struct sockaddr *)&client_address,&len); if(connfd < 0)
{
cout<<"error"<<endl;
close(listenfd);
} char buf[1024];
fd_set readfds;
FD_ZERO(&readfds); while(1)
{
bzero(buf,1024); FD_SET(connfd,&readfds); ret = select(connfd + 1,&readfds,NULL,NULL,NULL);
if(ret < 0)
{
cout<<"error"<<endl;
} //判读可读事件是否发生
if(FD_ISSET(connfd,&readfds))
{
ret = recv(connfd,buf,sizeof(buf) - 1,0); if(ret <= 0)
{
break;
} cout<<buf<<endl;
} } close(listenfd);
close(connfd); return 0;
}

select的特点

select的内部实现调用了poll(以下会写道,所以读者能够先跳过这里,去读poll,然后在回头一起看这个)。全部它和poll的特点同样。仅仅是函数接口有所不同,本质一样

poll的特点例如以下

(1)将用户传入的pollfd数据(相应select的描写叙述符集合)复制到内核空间,这个拷贝过程事件复杂度为O(N)

(2)挨个查询每个文件描写叙述符的状态,假设无就绪的文件描写叙述符,则进程就会挂起等待,知道发生超时或设备驱动再次唤醒它。然后它再次遍历全部的文件描写叙述符,找出发生事件的文件描写叙述符。因为其共遍历2次文件文件描写叙述符,所以其事件复杂度为O(N)

(3)将获得的数据拷贝至用户空间。时间复杂度又是O(N)

2.poll的使用

poll的原型例如以下

#include<poll.h>
int poll(struct pollfd *fds,nfds_t nfds,int timeout);

当中fds为pollfd类型的结构体数组其结构体定义例如以下

struct pollfd
{
int fd; //要监听文件描写叙述符
short events;//注冊的事件
short revents;//实际发生的事件,内核填充
}

poll可监听的事件类型例如以下(仅仅列出了经常使用的)

事件 描写叙述
POLLIN 数据可读
POLLOUT 数据可写
POLLRDHUB TCP连接被对方关闭,或对方关闭了写操作
POLLHUB 挂起,比方管道的写端被关闭
POLLERR 错误

nfds为监听的文件描写叙述符个数

timeout为超时事件

索引Poll返回的文件描写叙述符

int ret = poll(fds,MAX_EVENT_NUMBER,-1);

//必须遍历全部文件描写叙述符找到当中的就绪事件(也能够依据已知的就绪个数进行简单的优化)

for(int i = 0;i<MAX_EVENT_NUMBER,i++)
{
if(fds[i].revents & POLLIN)
{
int sock = fds[i];
}
}

关于poll的特点读者能够回到select去看,前面有写到,因为实在说其和select的内部实现机制是一样的所以不是必需多余写

3.epoll的使用

epoll是linux特有的I/O复用函数,他在实现和使用上与其它I/O复用有所不同。

它是使用一组函数来完毕任务的,其次epoll把用户关心的文件描写叙述符上的事件放在一个内核事件表中。

epoll须要使用一个额外的文件描写叙述符来来唯一的标识内核中的这个事件表,文件描写叙述符使用epoll_create()来创建

#include<sys/epoll.h>
int epoll_create(int size);

size參数告诉内核事件表须要多大,该函数返回的文件描写叙述符。将用于接下来全部函数的第一个參数

以下函数用来操作内核事件表

#include<sys/epoll.h>
int epoll_ctl(int epfd,int op,int fd,struct epoll_event *event);

fd为要操作的文件描写叙述符,op參数则指定操作类型,操作类型有例如以下几种

操作类型 详细描写叙述
EPOLL_CTL_ADD 往事件表中注冊fd上的事件
EPOLL_CTL_MOD 改动fd上的注冊事件
EPOLL_CTL_DEL 删除fd上的注冊事件

epoll_event结构体的定义例如以下

struct epoll_event
{
_uint32_t events //epoll事件
epoll_data_t //用户数据
}

当中epoll_data_t是个联合体其定义例如以下

typedef union epoll_data
{
void *ptr;
int fd;
uint32_t u32;
uint64_t u64;
}epoll_data_t

epoll_wait()函数

epoll事件调用的主要接口就是epoll_wait函数,它在一段超时事件内等待一组文件描写叙述符上的事件

#include<sys/epoll.h>
int epoll_wait(int epfd,struct epoll_event *events,int maxevents,int timeout);

该函数成功时返回就绪的文件描写叙述符个数

假设epoll_wait检測到就绪事件,就将全部的就绪事件赋值到第二个參数epoll_event数组当中,它仅仅用于输出检測到的就绪事件。不想poll的数组參数既用于传入用户注冊的事件,又用于输出内核检測的事件,这会极大的减少应用程序索引文件描写叙述符的效率

epoll的使用

int ret = epoll_wait(epollfd,events,MAX_EVENT_NUMBER,-1);
//仅仅需遍历ret个文件描写叙述符
for(int i = 0;i<ret;i++)
{
int sockfd = events[i].data.fd;
}
特别注意epoll对文件描写叙述符有俩中模式LT和ET。ET是epoll的高效模式

epoll的特点

epoll在内核实现中是依据每个fd上的俄callback函数来实现的,仅仅有活跃的fd才会主动调用callback,其它的fd则不会。

假设所监控的全部文件描写叙述符基本上都是活跃的那么epoll和select或poll差距不是太大。可是要是所监控的文件描写叙述符仅仅有少数活跃,epoll的效率要远高于他俩

linux下select,poll,epoll的使用与重点分析的更多相关文章

  1. Linux下select&poll&epoll的实现原理(一)

    最近简单看了一把 linux-3.10.25 kernel中select/poll/epoll这个几个IO事件检测API的实现.此处做一些记录.其基本的原理是相同的,流程如下 先依次调用fd对应的st ...

  2. Linux下select&poll&epoll的实现原理(一)【转】

    转自:http://www.cnblogs.com/lanyuliuyun/p/5011526.html 最近简单看了一把 linux-3.10.25 kernel中select/poll/epoll ...

  3. linux下select/poll/epoll机制的比较

    select.poll.epoll简介 epoll跟select都能提供多路I/O复用的解决方案.在现在的Linux内核里有都能够支持,其中epoll是Linux所特有,而select则应该是POSI ...

  4. (转)Linux下select, poll和epoll IO模型的详解

    Linux下select, poll和epoll IO模型的详解 原文:http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll ...

  5. Linux下select, poll和epoll IO模型的详解

    http://blog.csdn.net/tianmohust/article/details/6677985 一).Epoll 介绍 Epoll 可是当前在 Linux 下开发大规模并发网络程序的热 ...

  6. Linux内核中网络数据包的接收-第二部分 select/poll/epoll

    和前面文章的第一部分一样,这些文字是为了帮别人或者自己理清思路的.而不是所谓的源代码分析.想分析源代码的,还是直接debug源代码最好,看不论什么文档以及书都是下策. 因此这类帮人理清思路的文章尽可能 ...

  7. Linux IO模式以及select poll epoll详解

    一 背景 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network ...

  8. Linux 网络编程的5种IO模型:多路复用(select/poll/epoll)

    Linux 网络编程的5种IO模型:多路复用(select/poll/epoll) 背景 我们在上一讲 Linux 网络编程的5种IO模型:阻塞IO与非阻塞IO中,对于其中的 阻塞/非阻塞IO 进行了 ...

  9. Linux I/O复用中select poll epoll模型的介绍及其优缺点的比較

    关于I/O多路复用: I/O多路复用(又被称为"事件驱动"),首先要理解的是.操作系统为你提供了一个功能.当你的某个socket可读或者可写的时候.它能够给你一个通知.这样当配合非 ...

随机推荐

  1. 多线程通信(wait/notify)

    线程通信概念:线程是操作系统中独立的个体,但这些个体如果不经过特殊的处理就不能成为一个整体,线程间的通信就成为整体的必用方式之一.当线程存在通信指挥,系统间的交互性会更强大,在提高CPU利用率的同时就 ...

  2. C# 对象克隆,DataTable转LIST

    public class ConvertHelper<T> where T : new() { private static string module = "ConvertHe ...

  3. jquery插件之倒计时-团购秒杀

      1.1 帮助文档关键字 倒计时 秒杀 timer 1.2.  使用场景 这样的倒计时在购物网站中会经常使用到,比如秒杀,限时抢购,确认收货倒计时. 这个功能并不难实现,就是利用js的定时执行,搜了 ...

  4. CSS样式优先级和权重问题(部分)

    内联样式: <div style="font-size: 12px;">姓名</div> 外部样式: <link rel="styleshe ...

  5. 移动端弹性滑动以及vue记录滑动位置

    -webkit-overflow-scrolling介绍 -webkit-overflow-scrolling: auto | touch; auto: 普通滚动,当手指从触摸屏上移开,滚动立即停止 ...

  6. os的进程调度算法(抄袭的)

    package me.letterwish.test; import java.io.BufferedInputStream; import java.io.FileInputStream; impo ...

  7. C#设置开机启动项、取消开机启动项

    如果想你写的程序随系统开机一起启动的话,那么你可以照下面这个方法来做. RunWhenStart(false, Application.ProductName, Application.Startup ...

  8. 【sqli-labs】 less24 POST- Second Order Injections *Real treat* -Stored Injections (POST型二阶注入 *真的好玩?* 存储注入)

    简单登陆浏览一遍后,发现是一个登陆注册修改密码的应用 审查一下代码 登陆页面的username,password使用了转义 注册页面的参数也进行了转义处理 但是在修改password的页面,直接从se ...

  9. 我所理解的monad(4):函子(functor)是什么--可把范畴简单的看成高阶类型

    大致介绍了幺半群(monoid)后,我们重新回顾最初引用wadler(haskell委员会成员,把monad引入haskell的家伙)的那句话: 现在我们来解读这句话中包含的另一个概念:自函子(End ...

  10. spring IOC bean中注入bean

    俩个实体 package com.java.test4; /** * @author nidegui * @create 2019-06-22 14:45 */ public class People ...