以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。
#include
#include
#include
#include
#include
#include
int main(void)
{
// 读取鼠标
int fd = -1;
int flag = -1;
char buf[200];
int ret = -1;
fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
perror("open:");
return -1;
}
// 把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0, F_GETFL); // 先获取原来的flag
flag |= O_NONBLOCK; // 添加非阻塞属性
fcntl(0, F_SETFL, flag); // 更新flag
// 这3步之后,0就变成了非阻塞式的了
while (1)
{
// 读鼠标
memset(buf, 0, sizeof(buf));
ret = read(fd, buf, 50);
if (ret > 0)
{
printf("鼠标读出的内容是:[%s].\n", buf);
}
// 读键盘
memset(buf, 0, sizeof(buf));
ret = read(0, buf, 5);
if (ret > 0)
{
printf("键盘读出的内容是:[%s].\n", buf);
}
}
return 0;
}
/*
int main(void)
{
// 读取鼠标
int fd = -1;
char buf[200];
fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);
if (fd < 0)
{
perror("open:");
return -1;
}
memset(buf, 0, sizeof(buf));
printf("before read.\n");
read(fd, buf, 50);
printf("读出的内容是:[%s].\n", buf);
return 0;
}
*/
/*
int main(void)
{
// 读取键盘
// 键盘就是标准输入,stdin
char buf[100];
int flag = -1;
// 把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0, F_GETFL); // 先获取原来的flag
flag |= O_NONBLOCK; // 添加非阻塞属性
fcntl(0, F_SETFL, flag); // 更新flag
// 这3步之后,0就变成了非阻塞式的了
memset(buf, 0, sizeof(buf));
printf("before read.\n");
read(0, buf, 5);
printf("读出的内容是:[%s].\n", buf);
return 0;
}
*/
(1)英文为:IO multiplexing
(2)用在什么地方?
(3)涉及select函数、poll函数。
(4)实现原理:外部阻塞式(select函数本身是阻塞式的),内部非阻塞式自动轮询(select自动轮询时A,B)多路阻塞式IO(键盘A,鼠标B,这两者本身是阻塞式的)。
(1)用poll函数实现同时读取键盘鼠标
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
// 读取鼠标
int fd = -1, ret = -1;
char buf[200];
struct pollfd myfds[2] = {0};
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 0)
{
perror("open:");
return -1;
}
// 初始化我们的pollfd
myfds[0].fd = 0; // 键盘
myfds[0].events = POLLIN; // 等待读操作
myfds[1].fd = fd; // 鼠标
myfds[1].events = POLLIN; // 等待读操作
ret = poll(myfds, fd+1, 10000);
if (ret < 0)
{
perror("poll: ");
return -1;
}
else if (ret == 0)
{
printf("超时了\n");
}
else
{
// 等到了一路IO,然后去监测到底是哪个IO到了,处理之
if (myfds[0].events == myfds[0].revents)
{
// 这里处理键盘
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
if (myfds[1].events == myfds[1].revents)
{
// 这里处理鼠标
memset(buf, 0, sizeof(buf));
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
}
return 0;
}
(2)用select函数实现同时读取键盘鼠标
#include
#include
#include
#include
#include
#include
#include
#include
int main(void)
{
// 读取鼠标
int fd = -1, ret = -1;
char buf[200];
fd_set myset;
struct timeval tm;//设置超时时间,即阻塞的时间不能太长,如果很久都没有IO来激活select,则表明超时了。
fd = open("/dev/input/mouse1", O_RDONLY);
if (fd < 0)
{
perror("open:");
return -1;
}
// 当前有2个fd,一共是fd一个是0
// 处理myset
FD_ZERO(&myset);
FD_SET(fd, &myset);
FD_SET(0, &myset);
tm.tv_sec = 10;
tm.tv_usec = 0;
ret = select(fd+1, &myset, NULL, NULL, &tm);//+1,是因为0~fd,则共有fd+1个文件描述符
if (ret < 0)//错误
{
perror("select: ");
return -1;
}
else if (ret == 0)//表明超时
{
printf("超时了\n");
}
else//>0表示有一路IO激活了
{
// 等到了一路IO,然后去监测到底是哪个IO到了,处理之
if (FD_ISSET(0, &myset))
{
// 这里处理键盘
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
if (FD_ISSET(fd, &myset))
{
// 这里处理鼠标
memset(buf, 0, sizeof(buf));
read(fd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
}
return 0;
}
(1)几乎可以认为,异步IO就是操作系统用软件实现的一套中断响应系统。类比硬件中断。
(2)异步IO的工作方法
(1)fcntl函数,主要设置异步通知。
(2)signal或sigaction函数(SIGIO)
#include
#include
#include
#include
#include
#include
#include
int mousefd = -1;
// 绑定到SIGIO信号,在函数内处理异步通知事件
void func(int sig)
{
char buf[200] = {0};
if (sig != SIGIO)
return;
read(mousefd, buf, 50);
printf("鼠标读出的内容是:[%s].\n", buf);
}
int main(void)
{
// 读取鼠标
char buf[200];
int flag = -1;
mousefd = open("/dev/input/mouse1", O_RDONLY);
if (mousefd < 0)
{
perror("open:");
return -1;
}
// 把鼠标的文件描述符设置为可以接受异步IO
flag = fcntl(mousefd, F_GETFL);
flag |= O_ASYNC;
fcntl(mousefd, F_SETFL, flag);
// 把异步IO事件的接收进程设置为当前进程
fcntl(mousefd, F_SETOWN, getpid());
// 注册当前进程的SIGIO信号捕获函数
signal(SIGIO, func);
// 读键盘,在这里是当前进程
while (1)
{
memset(buf, 0, sizeof(buf));
read(0, buf, 5);
printf("键盘读出的内容是:[%s].\n", buf);
}
return 0;
}
(1)共享而不是复制,减少内存操作。
(2)处理大文件时效率高(一般用于视频处理),小文件不划算。