欢迎访问个人GitHub博文
2020.12.19
一、非阻塞IO
1、阻塞式:进程条件没有满足时,进程一直在等待,满足条件才返回,期间也不占用资源。
常见的阻塞:wait pause sleep等
好处:有利于操作系统的性能释放。
2、非阻塞式:
希望提高效率;
实现:O_NONBLOCK(使用open打开文件)和fcntl
二、阻塞式IO
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
//读取键盘
//键盘就是标准输入,stdin
char buf[100];
memset(buf,0,sizeof(buf));
printf("before read.\n");
read(0,buf,2);
printf("读出的内容是:[%s].\n",buf);
return 0;
}
程序一直等待键盘输入,阻塞式;
如下图所示,移动鼠标就会有显示,说明这个文件就是鼠标输入文件。
读鼠标
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int fd = -1;
char buf[200];
fd = open("/dev/input/by-path/pci-0000:00:04.0-event-mouse",O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
memset(buf,0,sizeof(buf));
printf("before read.\n");
read(fd,buf,5);
printf("读出的内容是:[%s].\n",buf);
return 0;
}
(3)同时读鼠标和键盘
如果使用阻塞式程序,就无法实现鼠标和键盘的任意输入。
解决办法是并发式IO
三、并发式IO
1、非阻塞式IO
(1)实现非阻塞式读取键盘
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
int flag = -1;
char buf[100];
//把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0,F_GETFL); //先获取原来的galg
flag |= O_NONBLOCK; //添加非阻塞属性
fcntl(0,F_SETFL,flag); //更新flag
//这三步之后,0就变成了非阻塞式
memset(buf,0,sizeof(buf));
printf("before read.\n");
read(0,buf,5);
printf("读出的内容是:[%s].\n",buf);
return 0;
}
(2)读鼠标
使用open更加简单,只需要添加O_NONBLOCK属性
fd = open("/dev/input/by-path/pci-0000:00:04.0-event-mouse",O_RDONLY);
但这样的方式实现同时读取效率不高。
2、多路复用IO
3、异步通知(异步IO)
四、多路复用IO
1、IO multiplexing
为了解决多路非阻塞式io的问题,因为非阻塞式IO 是以一种轮询的方式实现,CPU需要反复的去查看。
因此使用多路复用IO,涉及到的函数有select和poll
实现原理:外部阻塞式(select和poll是阻塞式),内部非阻塞式自动轮询多路阻塞式
2、select函数
man 2 select
3、poll函数
man 2 poll
两个函数功能是一样的。
4、实战
2021.1.6
(1)select实现
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>
int main()
{
int fd = -1;
int ret =-1;
char buf[200];
fd_set myset;
struct timeval tm;
//读鼠标
fd = open("/dev/input/by-path/pci-0000:00:04.0-event-mouse",O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
//当前有两个fd,一个是fd一个是0(标准输入)
//使用之前处理myset
FD_ZERO(&myset);
FD_SET(fd,&myset);
FD_SET(0,&myset);
tm.tv_sec = 5;//秒
tm.tv_usec = 0;//微秒
ret = select(fd +1,&myset,NULL,NULL,&tm);//阻塞
if (ret < 0)
{
perror("select: ");
return -1;
}
else if (ret == 0)
{
printf("超时了\n");
}
else
{
//等到了一路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,5);
printf("鼠标读出的内容是:[%s].\n",buf);
}
}
return 0;
}
(2)poll实现
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>
int main()
{
int fd = -1,ret =-1;
char buf[200];
struct pollfd myfds[2] = {0};
//读鼠标
fd = open("/dev/input/by-path/pci-0000:00:04.0-event-mouse",O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
//初始化定义的数组
myfds[0].fd = 0; //键盘
myfds[0].events = POLLIN; //等待读操作
myfds[1].fd = fd; //鼠标
myfds[1].events = POLLIN; //等待读操作
ret = poll( myfds ,fd +1,10000);//10000ms
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,5);
printf("鼠标读出的内容是:[%s].\n",buf);
}
}
return 0;
}
五、异步IO
可以认为:异步IO就是操作系统用软件实现的一套中断响应系统。
工作方法:
(1)当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数)
(2)当异步事件发生后,系统停止正常的进程,转而去执行绑定的函数处理这个异步事件。
涉及到的函数:
(1)fcntl(F_GETFL、F_SETFL、O_ASYNC、F_SETOWN)
(2)signal或者sigaction(SIGIO)
实践:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int fd = -1;
//绑定SIGIO信号,函数处理异步通知事件
void func(int sig)
{
char buf[200] = {0};
if (sig != SIGIO)
{
return;
}
read(fd,buf,5);
printf("鼠标读出的内容是:[%s].\n",buf);
}
int main()
{
int fd = -1,ret =-1,flag = -1;
char buf[200] = {0};
//读鼠标
fd = open("/dev/input/by-path/pci-0000:00:04.0-event-mouse",O_RDONLY);
if (fd < 0)
{
perror("open");
return -1;
}
// 注册异步通知(把鼠标的文件描述符设置为可以接受异步IO)
flag = fcntl(fd, F_GETFL);
flag |= O_ASYNC;
fcntl(fd, F_SETFL,flag);
//把异步IO事件的接收进程设置为当前进程
fcntl(fd, F_SETOWN,getpid());
//注册当前进程的SIGIO信号捕获函数
signal(SIGIO,func);
//读键盘
while (1)
{
memset(buf,0,sizeof(buf));
read(0,buf,5);
printf("键盘读出的内容是:[%s].\n",buf);
}
return 0;
}
六、存储映射IO
(1)mmap函数
例如LCD显示和IPC之间共享内存。
(2)特点
1、共享不是复制,减少内存操作;
2、处理大文件时效率高。