IO多路复用原理

(1)IO multiplexing
(2)用在什么地方?多路非阻塞式IO。
(3)select和poll
(4)外部阻塞式,内部非阻塞式自动轮询多路阻塞式IO

IO多路复用原理:
其实就是整个函数对外表现为阻塞式的,也就是我们调用这个函数,如果条件达不到一定

会被阻塞;但是其实内部并不是阻塞的,而是以一种非阻塞的方式工作的,内部能够实现

自动轮询,如果有任何一个IO设备达到条件即可返回到应用层。

用select函数实现IO多路复用:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

原理:通过阻塞监听设备文件是否有数据可操作,如果有数据可读则返回,当然也可以设
置超时时间,然后我们通过read函数去读取/写,所以其实这个就不涉及到read函数是否

是阻塞或者是非阻塞的了。因为select函数返回之后才能读取,所以有数据我们就去读取

,没有我们就不去读。
我们可以设置多路的IO操作,当调用select函数的时候进行阻塞,内部轮询监听是否可以

进行相应的操作,如果那个可以进行操作那么就操作哪个,然后返回到应用层去,结束

select函数的阻塞。

参数详解:
第一个参数:其实就是表示多路IO时,文件描述符最大的值+1,因为我们的fd是从0开始

的。
第二个参数:是一个指向fd_set结构体的指针,结构体需要我们通过以下的函数来填充:
void FD_CLR(int fd, fd_set *set); 用来清除描述词组set中相关fd 的位
int FD_ISSET(int fd, fd_set *set); 用来测试描述词组set中相关fd 的位是否为真
void FD_SET(int fd, fd_set *set); 用来设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); 用来清除描述词组set的全部位
也就是表示在我们打开的所有设备文件中要进行读操作的设备是那些,把他的fd填充进去

,如果所有的都不进行读操作,则写NULL。
第三个参数: 一般是NULL
第四个参数:一般是NULL
第五个参数:表示超时时间(阻塞时间),是一个结构体:如下:
struct timeval {
long tv_sec; /* seconds */
long tv_usec; /* microseconds */
};
有一个秒和一个微秒,是共同配合使用的,xx秒xx微秒

返回值:失败: -1 超时返回 0 成功:> 0

/*************************************************************************/

代码如下:

#include <stdio.h>

//According to POSIX.1-2001
#include <sys/select.h>

//According to earlier standards
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

#define FILE "/dev/input/mouse0"

int main(void)
{
int fd = -1;
int sele_ret = -1;
fd_set Fd_set;
struct timeval time = {0};
char buf[10] = {0};

//打开设备文件
fd = open(FILE, O_RDONLY);
if (-1 == fd)
{
perror("open error");
exit(-1);
}

//构建多路复用IO
FD_ZERO(&Fd_set); //清除全部fd
FD_SET(0, &Fd_set); //添加标准输入
FD_SET(fd, &Fd_set); //添加鼠标
time.tv_sec = 10; //设置阻塞超时时间为10秒钟
time.tv_usec = 0;

sele_ret = select(fd+1, &Fd_set, NULL, NULL, &time);
if (0 > sele_ret)
{
perror("select error");
exit(-1);
}
else if (0 == sele_ret)
{
printf("无数据输入,等待超时.\n");
}
else
{
if (FD_ISSET(0, &Fd_set)) //监听得到得到的结果若是键盘,则让去读取键盘的数据
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf)/2);
printf("读取键盘的内容是: %s.\n", buf);
}

if (FD_ISSET(fd, &Fd_set)) //监听得到得到的结果若是鼠标,则去读取鼠标的数据
{
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf)/2);
printf("读取鼠标的内容是: %s.\n", buf);
}
}

//关闭鼠标设备文件
close(fd);

return 0;
}

/********************************************************************************/

poll函数实现IO多路复用:

原型:int poll(struct pollfd *fds, nfds_t nfds, int timeout);

参数详解:
第一个参数 :struct pollfd {
int fd; //文件描述符
short events; //请求的事件
short revents; //返回的事件
};
其实就是我们多路复用IO的所有文件的fd,注意这里跟select不一样,他是在这个结构体

内进行区分的, events就是表示我们是进行那种操作,是用宏定义的, revents是内核

构建的,返回一个事件,所以我们就是通过 events == revents来确定是不是该fd发生了

可操作。
宏定义如下:
POLLIN 普通或优先级带数据可读
POLLRDNORM 普通数据可读
POLLRDBAND 优先级带数据可读
POLLPRI 高优先级数据可读
POLLOUT 普通数据可写
POLLWRNORM 普通数据可写
POLLWRBAND 优先级带数据可写
POLLERR 发生错误
POLLHUP 发生挂起
POLLNVAL 描述字不是一个打开的文件

第二个参数:就是select的第一个参数

第三个参数:阻塞超时时间,以毫秒为单位

返回值:跟select是一样的,失败-1 超时0 >0成功

代码如下:

/*****************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <poll.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define FILE "/dev/input/mouse0"

int main(void)
{
int fd = -1;
int poll_ret = 0;
struct pollfd poll_fd[2] = {0};
char buf[100] = {0};

//打开设备文件
fd = open(FILE, O_RDONLY);
if (-1 == fd)
{
perror("open error");
exit(-1);
}

//构建多路复用IO
poll_fd[0].fd = 0; //键盘
poll_fd[0].events = POLLIN; //定义请求的事件为读数据
poll_fd[1].fd = fd; //鼠标
poll_fd[1].events = POLLIN; //定义请求的事件为读数据
int time = 10000; //定义超时时间为10秒钟

poll_ret = poll(poll_fd, fd+1, time);
if (0 > poll_ret)
{
perror("poll error");
exit(-1);
}
else if (0 == poll_ret)
{
printf("阻塞超时.\n");
}
else
{
if (poll_fd[0].revents == poll_fd[0].events) //监听得到得到的结果若是键盘,则让去读取键盘的数据
{
memset(buf, 0, sizeof(buf));
read(0, buf, sizeof(buf)/2);
printf("读取键盘的内容是: %s.\n", buf);
}

if (poll_fd[1].revents == poll_fd[1].events) //监听得到得到的结果若是鼠标,则去读取鼠标的数据
{
memset(buf, 0, sizeof(buf));
read(fd, buf, sizeof(buf)/2);
printf("读取鼠标的内容是: %s.\n", buf);
}
}

//关闭文件
close(fd);

return 0;
}

时间: 2024-08-01 07:53:07

IO多路复用原理的相关文章

理论铺垫:阻塞IO、非阻塞IO、IO多路复用/事件驱动IO(单线程高并发原理)、异步IO

完全来自:http://www.cnblogs.com/alex3714/articles/5876749.html 同步IO和异步IO,阻塞IO和非阻塞IO分别是什么,到底有什么区别?不同的人在不同的上下文下给出的答案是不同的.所以先限定一下本文的上下文. 本文讨论的背景是Linux环境下的network IO. 一 概念说明 在进行解释之前,首先要说明几个概念:- 用户空间和内核空间- 进程切换- 进程的阻塞- 文件描述符- 缓存 I/O 用户空间与内核空间 现在操作系统都是采用虚拟存储器,

IO多路复用——select

IO多路复用 是同步IO的一种,用一个进程一次等待多个IO就绪事件的发生,加大概率,尽可能高效的等. 适用场景 (1)当客户处理多个描述字时(一般是交互式输入和网络套接口),必须使用I/O复用. (2)当一个客户同时处理多个套接口时,而这种情况是可能的,但很少出现. (3)如果一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O复用. (4)如果一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用. (5)如果一个服务器要处理多个服务或多个协议,一般要使用I/O

转一贴,今天实在写累了,也看累了--【Python异步非阻塞IO多路复用Select/Poll/Epoll使用】

下面这篇,原理理解了, 再结合 这一周来的心得体会,整个框架就差不多了... http://www.haiyun.me/archives/1056.html 有许多封装好的异步非阻塞IO多路复用框架,底层在linux基于最新的epoll实现,为了更好的使用,了解其底层原理还是有必要的.下面记录下分别基于Select/Poll/Epoll的echo server实现.Python Select Server,可监控事件数量有限制: 1 2 3 4 5 6 7 8 9 10 11 12 13 14

Python网络编程:IO多路复用

io多路复用:可以监听多个文件描述符(socket对象)(文件句柄),一旦文件句柄出现变化,即可感知. 1 sk1 = socket.socket() 2 sk1.bind(('127.0.0.1',8001)) 3 sk1.listen() 4 5 # sk2 = socket.socket() 6 # sk2.bind(('127.0.0.1',8002)) 7 # sk2.listen() 8 while True: 9 conn,address = sk.accept()#阻塞等待客户端

IO多路复用的几种实现机制的分析

http://blog.csdn.net/zhang_shuai_2011/article/details/7675797 select,poll,epoll都是IO多路复用的机制.所谓I/O多路复用机制,就是说通过一种机制,可以监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作.但select,poll,epoll本质上都是同步I/O,因为他们都需要在读写事件就绪后自己负责进行读写,也就是说这个读写过程是阻塞的,而异步I/O则无需自己负责进行读写,异步

python(十)下:事件驱动与 阻塞IO、非阻塞IO、IO多路复用、异步IO

上节的问题: 协程:遇到IO操作就切换. 但什么时候切回去呢?怎么确定IO操作完了? 一.事件驱动模型介绍 通常,我们写服务器处理模型的程序时,有以下几种模型: (1)每收到一个请求,创建一个新的进程,来处理该请求: (2)每收到一个请求,创建一个新的线程,来处理该请求: (3)每收到一个请求,放入一个事件列表,让主进程通过非阻塞I/O方式来处理请求 第三种就是协程.时间驱动的方式,一般普遍认为第(3)种方式是大多数网络服务器采用的方式 论事件驱动模型 在UI编程中,,常常要对鼠标点击进行相应,

Python全栈开发-Day10-进程/协程/异步IO/IO多路复用

本节内容 多进程multiprocessing 进程间的通讯 协程 论事件驱动与异步IO Select\Poll\Epoll--IO多路复用   1.多进程multiprocessing Python的线程用的是操作系统的原生线程,同样python的进程用的是操作系统的原生进程. 多进程之间没有锁的概念,多进程之间数据不能互相访问,所以不存在互斥锁.GIL问题又是仅仅出现在多线程中. 所以如果我们启动8个进程,每个进程有一个主线程,即8个线程,分别运行在8个CPU上,就可以充分利用多核的优势了.

IO多路复用和local概念

一.local 在多个线程之间使用threading.local对象,可以实现多个线程之间的数据隔离 import time import random from threading import Thread,local loc = local() def func1(): global loc print(loc.name,loc.age) def func2(name,age): global loc loc.name = name loc.age = age time.sleep(ran

Java网络编程和NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型

Java网络编程与NIO详解2:JAVA NIO一步步构建IO多路复用的请求模型 知识点 nio 下 I/O 阻塞与非阻塞实现 SocketChannel 介绍 I/O 多路复用的原理 事件选择器与 SocketChannel 的关系 事件监听类型 字节缓冲 ByteBuffer 数据结构 场景 接着上一篇中的站点访问问题,如果我们需要并发访问10个不同的网站,我们该如何处理? 在上一篇中,我们使用了java.net.socket类来实现了这样的需求,以一线程处理一连接的方式,并配以线程池的控制