自学内容网 自学内容网

笔记整理—linux进程部分(7)高级IO(非阻塞式IO、多路复用IO、异步IO、存储映射IO)

        我们前面说到过文件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,共享内存。

        ①共享不是复制,减少操作,减少占用。

        ②处理大文件时候的效率高,小文件不划算大文件例如图片一类的用于视频或监控传输。

        多进程与多线程也可以做到进程并发的问题。 


原文地址:https://blog.csdn.net/qq_35229394/article/details/142689283

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!