[arm驱动]Linux内核开发之阻塞非阻塞IO----轮询操作

简介:

《[arm驱动]Linux内核开发之阻塞非阻塞IO----轮询操作》涉及内核驱动函数二个,内核结构体零个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板二个,可参考的相关应用程序模板或内核驱动一个

一、概念:Poll是非阻塞IO----轮询操作
   非阻塞 I/O 的应用程序常常使用 poll, select, 和 epoll 系统调用. poll, select 和 epoll 本质上有相同的功能: 每个允许一个进程来决定它是否可读或者写一个或多个文件而不阻塞. 

   Tip:select()和poll(),epoll查询是否可对设备进行无阻塞的访问,这几个系统调用最终又会引发设备驱动中的poll()函数被执行

   PS:看到这感觉晕了,暂且不理会
二、使用场景:
   它们常常用在必须使用多输入输出流的应用程序(如调用read,write字符设备驱动文件/dev/****)。因为这些调用也可阻塞进程直到任何一个给定集合的文件描述符可用来读或写.
三、相关函数
1、内核函数
内核驱动函数一)a)poll()函数原型:


unsigned int (*poll) (struct file *filp, poll_table *wait);

  作用:调用poll_wait(),将可能引起设备文件状态变化的等待队列头添加到poll_table.
返回值:返回是否能对设备进行无阻塞读写访问的掩码

       放回值mask常量及函数

           常量    说明
           POLLIN    普通或优先级带数据可读
           POLLRDNORM    普通数据可读
           POLLRDBAND    优先级带数据可读
           POLLPRI    高优先级数据可读
           POLLOUT    普通数据可写
           POLLWRNORM    普通数据可写
           POLLWRBAND    优先级带数据可写
           POLLERR    发生错误
           POLLHUP    发生挂起
           POLLNVAL    描述字不是一个打开的文件    

内核驱动函数二)b)poll_wait()函数原型:    

void poll_wait(struct file *filp, wait_queue_head_t *queue, poll_table *wait);

   作用:将可能引起设备文件状态变化的等待队列头添加到poll_table
2、应用程序poll函数    

int poll(struct pollfd *fds, nfds_t nfds, int timeout)

  a) 参数:
fds 指向 struct pollfd 数组
   nfds 指定 pollfd 数组元素的个数,也就是要监测几个 pollfd
   timeout 时间参数,单位ms,1000ms=1s
   Tip:fds可以是很多个文件(如网卡,按键),poll可以论寻fds[n]

   b)结构体pollfd
   struct pollfd {
       int fd;
       short events;
       short revents;
   };

3、总结:从应用程序的调用来看,并不需要理会内核函数中的参数poll_table *wait是什么,只需要调用poll_wait()

四、使用模板
模板一)a)内核程序模板

static DECLARE_WAIT_QUEUE_HEAD(waitq);//定义结构体名称为waitq
poll(struct file *file, poll_table *wait){//返回mask
     unsigned int mask = 0;
    poll_wait(file, &waitq, wait);
     if(...)//可读
    {
          mask |= POLLIN | POLLRDNORM;    //标识数据可获得
     }
    if(...)//可写
    {
          mask |= POLLOUT | POLLRDNORM;    //标识数据可写入
     }
    return mask;
}

模板二)b)测试程序模板

struct pollfd fds[n];
fds[0].fd     = fd;
fds[0].events = POLLIN;
poll(fds, n, 5000);

   c)再次理解下面几句
   fds 指向 struct pollfd 数组
   nfds 指定 pollfd 数组元素的个数,也就是要监测几个 pollfd
   timeout 时间参数,单位ms,1000ms=1s
   Tip:fds可以是很多个文件(如网卡,按键),poll可以论寻fds[n]


实例一)五、案例jz2440中断非阻塞驱动实例

      1、 非阻塞内核按键驱动。

//“irq_drv”,"irq_","irq"
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/poll.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
static DECLARE_WAIT_QUEUE_HEAD(button_waitq);//定义结构体名称为button_waitq
static struct class *irq_class;
static struct class_device    *irq_class_dev;
static int ev_press = 0;
static unsigned char key_val;
struct pin_desc{
    unsigned int pin;
    unsigned int key_val;
};
struct pin_desc pins_desc[3] = {
    {S3C2410_GPF0, 0x01},
    {S3C2410_GPF2, 0x02},
    {S3C2410_GPG3, 0x03},
};
static irqreturn_t irq_handle(int irq, void *dev__id){
    //printk("irq = %d\n", irq);
    int pinval;
    struct pin_desc *pindesc = (struct pin_desc *)dev__id;
    pinval = s3c2410_gpio_getpin(pindesc->pin);
    if(!pinval){//按下
    key_val = pindesc->key_val;
    }else{//松开
    key_val = 0x80 | pindesc->key_val;
    }
    ev_press = 1;
    wake_up_interruptible(&button_waitq);
    return IRQ_RETVAL(IRQ_HANDLED);//warn:返回IRQ_HANDLED
}
static unsigned irq_drv_poll(struct file *file, poll_table *wait)
{
    unsigned int mask = 0;
    poll_wait(file, &button_waitq, wait); // 不会立即休眠
    if (ev_press)
        mask |= POLLIN | POLLRDNORM;
    return mask;
}
static int irq_drv_open(struct inode *inode, struct file *file)
{
    printk("irq_dev read\n");
//    request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char * devname, void * dev_id); dev_id随意
    request_irq(IRQ_EINT0, irq_handle, IRQ_TYPE_EDGE_BOTH, "s2", &pins_desc[0]);
    request_irq(IRQ_EINT2, irq_handle, IRQ_TYPE_EDGE_BOTH, "s3", &pins_desc[1]);
    request_irq(IRQ_EINT11, irq_handle, IRQ_TYPE_EDGE_BOTH, "s4", &pins_desc[2]);
    return 0;
}
static ssize_t irq_drv_read (struct file *file, char __user *buf, size_t count, loff_t *ppos){
    if(count != 1)return -EINVAL;
    wait_event_interruptible(button_waitq, ev_press);//ev_press标志(if!(ev_press)),那么一直休眠
    copy_to_user(buf, &key_val, 1);//一个 char 0xff
    ev_press = 0;
    return 1;//warn :return the size of val
}
static ssize_t irq_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    printk("irq_dev write\n");
    return 0;
}
static ssize_t irq_drv_release(struct inode *inode, struct file *file){
    free_irq(IRQ_EINT0, &pins_desc[0]);
    free_irq(IRQ_EINT2, &pins_desc[1]);
    free_irq(IRQ_EINT11, &pins_desc[2]);
    return 0;
}
static struct file_operations irq_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   irq_drv_open,
    .write =    irq_drv_write, 
    .read = irq_drv_read,
    .release = irq_drv_release,
    .poll = irq_drv_poll,
};
int major;
static int irq_drv_init(void)
{
    major = register_chrdev(0, "irq_drv", &irq_drv_fops); // 注册, 告诉内核
    if (major < 0) {
      printk(" can't register major number\n");
      return major;
    }
    irq_class = class_create(THIS_MODULE, "irq_drv");
    if (IS_ERR(irq_class))
        return PTR_ERR(irq_class);
    irq_class_dev = class_device_create(irq_class, NULL, MKDEV(major, 0), NULL, "irq"); /* /dev/xyz */
    if (IS_ERR(irq_class_dev))
        return PTR_ERR(irq_class_dev);
    return 0;
}
static void irq_drv_exit(void)
{
    unregister_chrdev(major, "irq_drv"); // 卸载
    class_device_unregister(irq_class_dev);
    class_destroy(irq_class);
}
module_init(irq_drv_init);
module_exit(irq_drv_exit);
MODULE_LICENSE("GPL");


   2、测试应用程序

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <poll.h>
/* irq
  */
int main(int argc, char **argv)
{
    int fd;
    unsigned char key_val;
    int cnt = 0;
    int ret;
    struct pollfd fds[1];
    fd = open("/dev/irq", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
        exit(1);
    }
    fds[0].fd = fd;
    fds[0].events = POLLIN;
    while (1)
    {
        ret = poll(fds, 1, 5000);
        if(ret == 0){
        printf("time out!\n");
        }else{
        read(fd, &key_val, 1);
        printf("key_Vals = 0x%x\n", key_val);
            }
    }
    return 0;
}
Makefile
#myirq.bin
objs := $(patsubst %c, %o, $(shell ls *.c))
myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc
myirq.bin:$(objs)
    $(myarmgcc) -o $@ $^
    cp *.bin /opt/fsmini/
%.o:%.c
    $(myarmgcc) -c -o $@ $<
clean:
    rm -f  *.bin *.o

本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1392082,如需转载请自行联系原作者

相关文章
|
5月前
|
Java Unix Linux
什么是阻塞IO和非阻塞IO
什么是阻塞IO和非阻塞IO
182 3
|
3月前
|
Linux 网络安全 开发工具
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
本文介绍了如何基于Linux Kernel 5.15.102版本和BusyBox创建一个自定义的迷你Linux ARM系统,并使用QEMU进行启动和调试,包括内核和BusyBox的编译配置、根文件系统的制作以及运行QEMU时的命令和参数设置。
306 0
内核实验(二):自定义一个迷你Linux ARM系统,基于Kernel v5.15.102, Busybox,Qemu
|
2月前
|
Go 开发工具 git
在Qemu+ARM上运行Minix3内核
在Qemu+ARM上运行Minix3内核
|
4月前
|
Linux 开发工具
CPU-IO-网络-内核参数的调优
CPU-IO-网络-内核参数的调优
74 7
|
3月前
|
Ubuntu Linux
内核实验(九):添加IO驱动的阻塞读写功能
本文通过修改内核模块代码,介绍了如何在Linux内核中为IO驱动添加阻塞读写功能,使用等待队列和条件唤醒机制来实现读写操作的阻塞和非阻塞模式,并在Qemu虚拟机上进行了编译、部署和测试。
19 0
|
4月前
|
安全 Java Linux
(七)Java网络编程-IO模型篇之从BIO、NIO、AIO到内核select、epoll剖析!
IO(Input/Output)方面的基本知识,相信大家都不陌生,毕竟这也是在学习编程基础时就已经接触过的内容,但最初的IO教学大多数是停留在最基本的BIO,而并未对于NIO、AIO、多路复用等的高级内容进行详细讲述,但这些却是大部分高性能技术的底层核心,因此本文则准备围绕着IO知识进行展开。
165 1
|
4月前
|
缓存 网络协议 算法
【Linux系统编程】深入剖析:四大IO模型机制与应用(阻塞、非阻塞、多路复用、信号驱动IO 全解读)
在Linux环境下,主要存在四种IO模型,它们分别是阻塞IO(Blocking IO)、非阻塞IO(Non-blocking IO)、IO多路复用(I/O Multiplexing)和异步IO(Asynchronous IO)。下面我将逐一介绍这些模型的定义:
221 2
|
4月前
|
存储 Java Unix
(八)Java网络编程之IO模型篇-内核Select、Poll、Epoll多路复用函数源码深度历险!
select/poll、epoll这些词汇相信诸位都不陌生,因为在Redis/Nginx/Netty等一些高性能技术栈的底层原理中,大家应该都见过它们的身影,接下来重点讲解这块内容。
|
5月前
Reactor为什么必须使用非阻塞io?
Reactor为什么必须使用非阻塞io?
47 0
Reactor为什么必须使用非阻塞io?
|
6月前
|
监控 Java
Java一分钟之-NIO:非阻塞IO操作
【5月更文挑战第14天】Java的NIO(New IO)解决了传统BIO在高并发下的低效问题,通过非阻塞方式提高性能。NIO涉及复杂的选择器和缓冲区管理,易出现线程、内存和中断处理的误区。要避免这些问题,可以使用如Netty的NIO库,谨慎设计并发策略,并建立标准异常处理。示例展示了简单NIO服务器,接收连接并发送欢迎消息。理解NIO工作原理和最佳实践,有助于构建高效网络应用。
80 2
下一篇
无影云桌面