我们前面说到过文件IO,也进行过多次的使用,文件IO就是与文件有关的R/W操作接口。
非阻塞式IO,首先要先说明上什么是阻塞式IO,我的活干不完后面就全部等着,条件不足就挂起,就硬等。常见的阻塞函数:wait();pause();sleep()......以及read/write某些文件如IO设备。阻塞式的好处,有利于性能的发挥,进程挂起不会浪费CPU资源,且编程难度低。阻塞式的不足,a、b、c在等资源,b资源来了,但a在b的前面,a资源还没来,情况依旧是三个资源都在等待,多体现在多路IO的情况下。
默认标准输入就是键盘,fd=0,属于stdin。
char buf[100];
memset(buf,0,sizeof(buf));
read(0,buf,2);//阻塞式
printf("buf=[%s]\n",buf);
读取两个字符,但是输入内容位于缓冲区,输入多少东西不按下回车,都不进行读取,回车了,就读取前两个字符。
读鼠标:
int fd=-1;
fd=open("/dev/input/mouse1",O_RDONLY);
if(fd<0)
{
perror("open");
return -1;
}
memset(buf,0,sizeof(buf));
read(fd,buf,5);
读出来的东西是乱码,因为输入的方法是二进制的,但读出的方法为字符方法,方法不对。
同时读取鼠标和键盘的方法,把读鼠标放在读键盘前,用户先移动鼠标在输入键盘,否则err,这体现了阻塞的困境,不知道用户会先去干什么。
并发式IO解决了阻塞IO的这个困境:①非阻塞式IO;②多路复用IO;③异步通知IO。
非阻塞式IO,将fd=0 stdin变为非阻塞式,可以使用fcntl()函数。
int fcntl(int fd, int cmd, ... /* arg */ );
int flag=-1;
flag=fcntl(0,F_GETFL);//获取原flag
flag |=O_NONBLOCK;//添加非阻塞属性
fcntl(0,F_SETFL,flag);
read(0,buf,5);
printf("buf=[%s]\n",buf);
int fd=-1;
fd=open("/dev/input/mouse1",O_RDONLY|O_NONBLOCK);
//改造一下
while(1)
{
memset(buf,0,sizeof(buf));
ret=read(0,buf,5);
if(ret>0)//读取到键盘的内容了
{
......
ret=-1;
}
memset(buf,0,sizeof(buf));
ret=read(fd,buf,5);
if(ret>0)//读取到鼠标内容了
{
......
ret=-1;
}
}
通过改造问题就解决啦,键盘鼠标无论先操作谁都能实现,但while(1)性能太差,浪费cpu性能。
多路复用IO(IO Multiplexing)解决并发IO的方案①多路并行。②多路非阻塞。
select与poll都可以实现多路复用,本质是外部阻塞模式,内部非阻塞或自动轮询多路阻塞式IO。
有点像共享中断查询挂起flag。
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
int nfds,文件fd数字+1,因为表示的是多路文件描述符的轮询数量,从0开始所以+1,是一个监听范围,从0~fd_max+1。
fd_set是个结构体,放多个文件描述符,*readfds要读的fd,*writefds要写的fd,*exceptfds要监听异常的fd,struct timeval 超时结构体,*timeout超时设置,因为select为阻塞式,没输出/入的情况下就应该设置一个超市,returnval正确返回发送IO事情数。
fd_set以一些已有的操作进行实现:
void FD_CLR(int fd, fd_set *set);//set去除
int FD_ISSET(int fd, fd_set *set);//set置位检察
void FD_SET(int fd, fd_set *set);//set添加
void FD_ZERO(fd_set *set);//set清理
例如:
int fd=-1;
fd=open("/dev/input/mouse1",O_RDONLY|O_NONBLOCK);//鼠标
fd_set myset;
struct timeval tm;
tm.tv_sec=10;
tm.tv_uses=0;
FD_ZERO(&myset);
FD_SET(fd,&myset);
FD_SET(0,&myset);
ret=select(fd+1,&myset,NULL,NULL,&tm);
if(ret<0)
{
perror("select");
return -1;
}
else if(0==ret)
{
timeout
}
else
{
if(FD_ISSET(0,&myset))//检查是否是键盘IO触发
{
处理键盘
}
else if(FD_ISSET(fd,&myset))//检查是否是鼠标IO触发
{
处理鼠标
}
}
poll和select实际上在参数列表差不多:
int poll(struct pollfd *fds, nfds_t nfds, int timeout);
struct pollfd {
int fd; /* 哪个文件 */
short events; /* 检测事件 */
short revents; /* 返回事件(时间发生自动启动) */
};
returnval正常返回几路IO发生,0为超时,-1为err。
int fd=-1;
fd=open("/dev/input/mouse1",O_RDONLY|O_NONBLOCK);//鼠标
struct pollfd myfds[2]={0};
myfds[0].fd=0;//键盘
myfds[0].events=POLLIN;//输入事件
myfds[1].fd=fd;//鼠标
myfds[1].events=POLLIN;//输入事件
ret=poll(myfds,fd+1,10000);//10000就是10s
使用events==revents去判断是哪路IO到了
if(myfds[0].events==myfds[0].revents)
{
键盘操作
}
......后面的操作和select差不多
异步IO,当前进程注册一个异步IO事件,然后该干什么干什么,直到接受到SIGIO处理执行这个中断处理函数,可以视为是操作系统使用软件方式实现的中断响应系统。(signal注册一个信号SIGIO处理函数)。
int fcntl(int fd, int cmd, ... /* arg */ );//设置异步通知
F_GETFL (void)//获取fd状态
F_SETFL (int)//重新将设置好的fd状态写回
F_SETOWN (int)//接受异步通知
将一个IO作为主IO,另一个作为异步通知IO。
void funct(int sig)
{
char buf[200]={0};
if(SIGIO!=sig)
return;
......处理鼠标的方法......
read(moused,buf,50);
}
int main(void)
{
int fd=-1;
char buf[200]={0};
mousefd=open("......",O_RDONLY);
flag=fcntl(mousefd,F_GETFL);
flag|=O_ASYNC;
fcntl(mousefd,F_SETEL,flag);//文件描述符号fd支持异步IO
将异步IO事件的接受进程设为当前进程
flag=fcnt(mousefd,F_SETOWN,getpid());
注册当前进程SIGIO信号捕获
signal(SIGIO,func);
......IO操作读键盘......
}
本质上这些驱动是支持异步IO的。
存储映射IO,把一个文件与一段内存进程映射(eg:LCD文件与现存)。通过mmap函数去实现,图片内容在应用层。
把驱动层与应用层用同一款物理内存关联起来。
IPC进程间通信,多个进程之间大量信息江湖也可以用到存储映射IO,共享内存。
①共享不是复制,减少操作,减少占用。
②处理大文件时候的效率高,小文件不划算大文件例如图片一类的用于视频或监控传输。
多进程与多线程也可以做到进程并发的问题。