[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,如需转载请自行联系原作者

目录
打赏
0
0
0
0
69
分享
相关文章
Linux C/C++之IO多路复用(select)
这篇文章主要介绍了TCP的三次握手和四次挥手过程,TCP与UDP的区别,以及如何使用select函数实现IO多路复用,包括服务器监听多个客户端连接和简单聊天室场景的应用示例。
148 0
Termux安卓终端美化与开发实战:从下载到插件优化,小白也能玩转Linux
Termux是一款安卓平台上的开源终端模拟器,支持apt包管理、SSH连接及Python/Node.js/C++开发环境搭建,被誉为“手机上的Linux系统”。其特点包括零ROOT权限、跨平台开发和强大扩展性。本文详细介绍其安装准备、基础与高级环境配置、必备插件推荐、常见问题解决方法以及延伸学习资源,帮助用户充分利用Termux进行开发与学习。适用于Android 7+设备,原创内容转载请注明来源。
103 19
Linux C/C++之IO多路复用(aio)
这篇文章介绍了Linux中IO多路复用技术epoll和异步IO技术aio的区别、执行过程、编程模型以及具体的编程实现方式。
231 1
Linux C/C++之IO多路复用(aio)
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
(已成功解决)Linux环境报错—bash: wget: command not found;常见Linux发行版本,Linux中yum、rpm、apt-get、wget的区别;Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
1231 68
(已解决)Linux环境—bash: wget: command not found; Docker pull报错Error response from daemon: Get https://registry-1.docker.io/v2/: net/http: request canceled
【Linux】进程IO|系统调用|open|write|文件描述符fd|封装|理解一切皆文件
本文详细介绍了Linux中的进程IO与系统调用,包括 `open`、`write`、`read`和 `close`函数及其用法,解释了文件描述符(fd)的概念,并深入探讨了Linux中的“一切皆文件”思想。这种设计极大地简化了系统编程,使得处理不同类型的IO设备变得更加一致和简单。通过本文的学习,您应该能够更好地理解和应用Linux中的进程IO操作,提高系统编程的效率和能力。
92 34
|
8月前
|
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
查看Linux系统架构的命令,查看linux系统是哪种架构:AMD、ARM、x86、x86_64、pcc 或 查看Ubuntu的版本号
1283 3
|
4月前
|
Linux基础IO
Linux基础IO操作是系统管理和开发的基本技能。通过掌握文件描述符、重定向与管道、性能分析工具、文件系统操作以及网络IO命令等内容,可以更高效地进行系统操作和脚本编写。希望本文提供的知识和示例能帮助读者更深入地理解和运用Linux IO操作。
95 14
|
6月前
|
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
184 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
6月前
|
Linux C/C++之IO多路复用(poll,epoll)
这篇文章详细介绍了Linux下C/C++编程中IO多路复用的两种机制:poll和epoll,包括它们的比较、编程模型、函数原型以及如何使用这些机制实现服务器端和客户端之间的多个连接。
131 0
Linux C/C++之IO多路复用(poll,epoll)