4.13号驱动

IO模型

1. 非阻塞

1.1 概念

当一个应用层程序,读取硬件数据,不管数据有没有准备好,read函数都不会阻塞,而是立即返回数据。

1.2 模型

应用层:
    fd = open("/dev/myled",O_RDWR |  O_NONBLOCK); //以非阻塞方式打开
    read(fd,buf,sizeof(buf)); //读取硬件中的数据

内核层:
    if(file->f_flags & O_NONBLOCK) //判断是否以非阻塞方式打开
    {
            用户以非阻塞方式打开
            读取硬件数据
            不管硬件中的数据是否准备好,都会立即返回硬件中的数据
    }

硬件层:
    温湿度传感器值

2. 阻塞

2.1 工作原理

当一个应用层程序,去读取硬件的数据,如果硬件中的数据没有准备好,此时进程进入休眠状态。

当硬件中的数据准备好,硬件会产生中断。在中断处理函数中,唤醒休眠的进程。

当进程被唤醒之后,从硬件中读取数据,将数据读取到内核空间。

再次调用copy_to_user,将内核空间中的数据,拷贝到用户空间

2.2 模型

定义等待队列头

wait_queue_head_t wq;

初始化等待队列头

init_waitqueue_head(&wq);

让进程进入可中断的等待态

wait_event_interruptible(wq, condition);

condition = 0为假,表示数据没有准备好,数据不可读,进程进入休眠

condition = 1为真,表示数据准备好,数据可读

唤醒可中断的等待态

condition = 1;为真

wake_up_interruptible(&wq);

3. IO多路复用

3.1 工作原理

在同一个应用层程序,如果想同时读取多个文件描述数据,此时就需要使用IO多路复用机制

使用slect/poll/epoll可以监听多个文件描述符

如果所有的文件描述符数据没有准备好,进程进入休眠状态

如果有一个或者多个文件描述符数据准备好,唤醒休眠的进程

当进程唤醒之后,从内核中读取到准备好的文件描述符集合

从准备好的文件描述符集合中,找到准备好的文件描述符

从文件描述符中读取数据

3.2 模型

应用层:

    fd1 = open("/dev/myled0",O_RDWR); //打开led灯设备文件
    fd2 = open("/dev/input/mouse0",O_RDWR); //打开鼠标文件
    fd_set readfds; //定义读表集合
    FD_ZERO(&readfds); //清空读表集合
    FD_SET(fd1, &readfds); //将fd1加入读表集合中
    FD_SET(fd2, &readfds); //将fd2加入读表集合中
    select(fd2+1,&readfds,NULL,NULL,NULL);//监听
    if(FD_ISSET(fd1,&readfds))//判断读表集合中fd1数据是否准备好
    {
        //读取fd1文件描述符中的数据    
    }
    if(FD_ISSET(fd2,&readfds))//判断读表集合中fd2数据是否准备好
    {
        //读取fd2文件描述符中的数据    
    }
    close(fd1);
    close(fd2);

内核层:

应用层select/poll/epoll对应底层poll函数

__poll_t myled_poll (struct file *file, struct poll_table_struct *wait) 

{
         //定义mask变量返回值 __poll_t mask = 0;
         //调用poll_wait函数 poll_wait(file,&wq,wait);
         //判断数据是否可读
         //返回mask变量
 }

4. epoll的使用

4.1 select/poll/epoll区别

select

select监听最大文件描述符为1024个

select每次都有清空表的过程,需要反复构造表,反复将用户空间表拷贝到内核空间

当数据准备好之后,select需要在再次遍历表,找到准备好的文件描述符,效率低

poll

poll监听最大文件描述符没有个数限制

poll没有清空表的过程,不需要返回构造表

当数据准备好之后,poll需要在再次遍历表,找到准备好的文件描述符,效率低

epoll

epoll监听最大文件描述符没有个数限制

epoll没有清空表的过程,不需要返回构造表

当数据准备好之后,无需遍历,epoll可以直接找到准备好的对应的文件描述符,直接从准备好的文件描述符中读取数据,效率高

4.2 epoll模型

应用层

int epoll_create(int size);
函数功能:创建epoll实例
参数:
    size:没有实际含义,只需要填写大于0的值
返回值:
    成功返回创建epoll实例对应的文件描述符
    失败返回-1

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
函数功能:对epoll实例控制函数
参数:
    epfd:创建epoll的实例
    op:操作的方式
        EPOLL_CTL_ADD:向epoll实例中添加要监听的文件描述符
        EPOLL_CTL_MOD:修改epoll实例中监听的文件描述符
        EPOLL_CTL_DEL:删除epoll实例中监听的文件描述符
    fd:被操作的文件描述符
    event:事件结构体
       typedef union epoll_data {
               void        *ptr;
               int          fd; ==========> 被操作的文件描述
               uint32_t     u32;
               uint64_t     u64;
           } epoll_data_t;
        struct epoll_event {
           uint32_t     events;   //监听事件类型 EPOLLIN

           epoll_data_t data;  //用户数据    
        };
返回值:
    成功返回0,失败返回-1

int epoll_wait(int epfd, struct epoll_event *events,
int maxevents, int timeout)
函数功能:阻塞等待准备好的文件描述符
参数:
    epfd:创建epoll的实例
    events:返回准备好的事件结构体指针
    maxevents:监听最大文件描述符个数
    timeout:超时事件 填写-1,表示不关系超时事件
返回值:
    成功返回准备好的文件描述符个数
    失败返回-1   

5. 异步通知

5.1 引入

使用阻塞、非阻塞、IO多路复用,都是用户主要调用接口,等待数据返回

使用阻塞、非阻塞、IO多路复用,如果数据没有准备好,需要阻塞等待,并且进程进入休眠状态

在休眠的这段时间,任何其他的事情都不可以去做,所以内核中引入异步通知

能够在硬件的数据准备好的时候,底层给应用层发送一个通知

应用层接收到这个通知之后,调用read函数去读取数据

在没有接收到这个通知,应用层可以去做其他的事情,不需要一直阻塞等待

5.2 工作原理

用户需要在应用层空间需要注册信号函数

如果硬件的数据没有准备好,这个进程去做其他的事情就可以

如果硬件的数据准备好,硬件会产生一个中断

在中断处理函数中,给进程发送一个通知

进程接收到这个通知之后,执行信号处理函数

在信号处理函数中读取数据(信号的发送和应用层程序执行属于异步)

5.3 模型

应用层:
    fd = open("/dev/myled",O_RDWR); 
    signal(SIGIO,信号处理函数);
    //应用层的fcntl函数,对应底层fasync函数
    unsigned int flag = fcntl(fd,F_GETFL); //获取文件的属性
    fcntl(fd,F_SETFL, flag  | FASYNC);
    //告诉驱动接收信号的进程
    fcntl(fd,F_SETOWN,getpid());

内核层:linux@ubuntu:~/linux-5.10.61$ grep ".fasync = " * -nR
    drivers/char/random.c:1994:    .fasync = random_fasync,
    drivers/char/random.c:2003:    .fasync = random_fasync,
    struct fasync_struct *fasync;  
    int myled_fasync(int fd, struct file *filp, int on)
    {
        return fasync_helper(fd, filp, on, &fasync);    
    }
    kill_fasync(&fasync, SIGIO, POLL_IN)  //发信号

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值