linux应用编程高级IO基础使用
`
前言
今天分享的是linux应用编程下的高级IO的一些使用,主要会从阻塞IO/非阻塞IO/多路复用IO(select、poll) /异步IO等进行相应的事例demo演示。
下面每种情况的dmeo都是基于鼠标键盘的读取操作
基础文件描述符
0 ->input (标准输入)
1->output (标准输出)
2->error(标准错误)
下面对每个知识点的讲解可能不会很清楚,自己也是才接触,分享的都是一些简单的认识和一些小练习demo,大家可以去看这位博主的。
linux 高级IO
一、阻塞式IO
阻塞IO形象的来讲就是,当你去车站买票 然后你的前面有很多人,你一直在哪里等前面的人走了后你才可以购买到,而你一直等待那段时间就是阻塞,什么都做不了。放到我们进程中来讲就是,当一个进程在等待莫一事件发生然后在执行相应的程序,如果这个触发事件没有发生,这个进程就会被阻塞,函数不能被立即返回,而被阻塞的进程是不占用cpu资源的。
下面结构图来自其他博主,可以去看这位博主的
//查看鼠标相关设备
ls /dev/input 可能大家的鼠标设备名称会不同我的是mouse0
cat /dev/input/mouse0 查看是否正确
//移动鼠标会有信息打印,则说明当前这个设备名称就是我们的鼠标设备
/*==================read mouse=============*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
//阻塞式IO
int main(void)
{
//读取鼠标
int fd = -1;
char buf[128];
fd = open("/dev/input/mouse0",O_RDONLY);
if(fd < 0)
{
printf("perror:\n");
return -1;
}
memset(buf,0,sizeof(buf));
printf("read before\n");
read(fd,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
return 0;
}
/*================read keyboard===========*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
//阻塞式IO
int main(void)
{
//读取键盘
//键盘就是标准输入,stdin
//ssize_t read(int fd, void *buf, size_t count);
char buf[128];
memset(buf,0,sizeof(buf));
printf("read before\n");
read(0,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
return 0;
}
二、非阻塞式IO
非阻塞式IO还是以买票形象的来讲就是,我们每隔一段时间就去看看还没有人,而其他的时间我们可以做其他的事情不会被影响。每次进程查看的时候都会有一个返回结果,效率比起阻塞式IO高很多。
进行先关函数API调用的时候我们通过man手册进行使用查看
man 2 open
/*=================read mouse==============*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(void)
{
//读取鼠标
int fd = -1;
char buf[128];
fd = open("/dev/input/mouse0",O_RDONLY | O_NONBLOCK); //一只读非组撒式打开
if(fd < 0)
{
printf("perror:\n");
return -1;
}
memset(buf,0,sizeof(buf));
printf("read before\n");
read(fd,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
return 0;
}
/*=================read keyboard=============*/
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
int main(void)
{
//读取键盘
//键盘就是标准输入,stdin
//ssize_t read(int fd, void *buf, size_t count);
char buf[128];
int flag = -1;
memset(buf,0,sizeof(buf));
// int fcntl(int fd, int cmd, ... /* arg */ );
//把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0,F_GETFL); //先获取原来的flag
flag |= O_NONBLOCK; //添加阻塞属性
fcntl(0,F_SETFL,flag); //更新flag
//这三步之后0 就变成了非阻塞的
printf("read before\n");
read(0,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
return 0;
}
三、并发式IO解决
并发这个词很形象从上面的demo来讲的话,就是鼠标和键盘我们可以同时进行。还是先进行阻塞式的demo然后进行非阻塞式并发IO
阻塞式读键盘和鼠标
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
//阻塞式IO
int main(void)
{
//读取鼠标
int fd = -1;
char buf[128];
fd = open("/dev/input/mouse0",O_RDONLY);
if(fd < 0)
{
printf("perror:\n");
return -1;
}
memset(buf,0,sizeof(buf));
printf("read mouse before\n");
read(fd,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
//读取键盘
memset(buf,0,sizeof(buf));
printf("read before\n");
read(0,buf,sizeof(buf));
printf("read text:[%s] \n",buf);
return 0;
}
非阻塞式读键盘鼠标
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
int main(void)
{
int fd = -1;
int flag = -1;
int ret = -1;
char buf[128];
//把0号文件描述符(stdin)变成非阻塞式的
flag = fcntl(0,F_GETFL); //先获取原来的flag
flag |= O_NONBLOCK; //添加阻塞属性
fcntl(0,F_SETFL,flag); //更新flag
fd = open("/dev/input/mouse0",O_RDONLY | O_NONBLOCK);
if(fd < 0)
{
printf("perror:\n");
return -1;
}
while(1)
{
//读取鼠标
memset(buf,0,sizeof(buf));
//printf("read mouse before\n");
ret = read(fd,buf,sizeof(buf));
if(ret > 0)
{
printf("read text:[%s] \n",buf);
}
//读取键盘
memset(buf,0,sizeof(buf));
//printf("read keyboard before\n");
ret = read(0,buf,sizeof(buf));
if(ret > 0)
{
printf("read text:[%s] \n",buf);
}
}
return 0;
}
四、IO多路复用
主要是select、poll、epoll;对一个IO端口,两次调用,两次返回,比阻塞IO并没有什么优越性;关键是能实现同时对多个IO端口进行监听。下面会以select 和poll进行演示
select io 多路复用
首先我们先进行相关函数API使用查看
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/time.h>
int main(void)
{
int fd = -1;
int ret = -1;
char buf[128];
fd_set myset;
struct timeval tm;
fd = open("/dev/input/mouse0",O_RDONLY | O_NONBLOCK);
if(fd < 0)
{
perror("open:");
}
// int select(int nfds, fd_set *readfds, fd_set *writefds,
// fd_set *exceptfds, struct timeval *timeout);
//当前有两个Fd 一个是0一个是fd
FD_ZERO(&myset); //先清零
FD_SET(fd,&myset); //将Fd添加到myset
FD_SET(0,&myset); //将0添加到Myset
tm.tv_sec = 10;
tm.tv_usec = 0;
ret = select(fd + 1,&myset,NULL,NULL,&tm);
if(ret < 0)
{
perror("select\n");
}
else if(ret == 0)
{
printf("over time\n");
}
else
{
//等到了一路IO ,然后去监听到底是哪个IO到了进行处理
if(FD_ISSET(0,&myset))
{
//这里处理键盘
memset(buf,0,sizeof(buf)); //先将缓存清空
read(0,buf,sizeof(buf)); //读取数据
printf("read keyboard:[%s].\n",buf);
}
if(FD_ISSET(fd,&myset))
{
//这里处理鼠标
memset(buf,0,sizeof(buf)); //先将缓存清空
read(0,buf,sizeof(buf)); //读取数据
printf("read mouse:[%s].\n",buf);
}
}
return 0;
}
poll 多路IO复用
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <poll.h>
int main(void)
{
int fd = -1;
int ret = -1;
char buf[128];
struct pollfd myfds[2]= {0};
fd = open("/dev/input/mouse0",O_RDONLY | O_NONBLOCK);
if(fd < 0)
{
perror("open:");
}
//int poll(struct pollfd *fds, nfds_t nfds, int timeout);
//当前有两个Fd 一个是0一个是fd
//初始化myfds
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\n");
return -1;
}
else if(ret == 0)
{
printf("over time\n");
}
else
{
//等到了一路IO ,然后去监听到底是哪个IO到了进行处理
if(myfds[0].events == myfds[0].revents)
{
//这里处理键盘
memset(buf,0,sizeof(buf)); //先将缓存清空
read(0,buf,sizeof(buf)); //读取数据
printf("read keyboard:[%s].\n",buf);
}
if(myfds[1].events == myfds[1].revents)
{
//这里处理鼠标
memset(buf,0,sizeof(buf)); //先将缓存清空
read(fd,buf,sizeof(buf)); //读取数据
printf("read mouse:[%s].\n",buf);
}
}
return 0;
}
五、异步IO
异步IO就是操作系统用软件实现的一套中断响应系统
异步IO的工作方法是:我们当前进程注册一个异步IO事件(使用signal注册一个信号SIGIO的处理函数),然后当前进程可以正常处理自己的事情,当异步事件发生后当前进程会收到一个SIGIO信号从而执行绑定的处理函数去处理这个异步事件。
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>
int mousefd = -1;
char buf[128];
//绑定SIGIO 信号,在函数内处理异步通知事件
void func(int sig)
{
if(sig != SIGIO)
{
return;
}
memset(buf,0,sizeof(buf)); //先将缓存清空
read(mousefd,buf,sizeof(buf)); //读取数据
//printf("read mouse:[%s].\n",buf);
}
int main(void)
{
int flag = -1;
mousefd = open("/dev/input/mouse0",O_RDONLY | O_NONBLOCK);
if(mousefd < 0)
{
perror("open:");
}
// int fcntl(int fd, int cmd, ... /* arg */ );
//把鼠标的文件描述符设置为可以接受异步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,sizeof(buf)); //读取数据
printf("read keyboard:[%s].\n",buf);
}
return 0;
}