首页 > 代码库 > IO多路复用原理
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;
}
IO多路复用原理