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

相关文章
|
11月前
|
安全 网络协议 Linux
深入理解Linux内核模块:加载机制、参数传递与实战开发
本文深入解析了Linux内核模块的加载机制、参数传递方式及实战开发技巧。内容涵盖模块基础概念、加载与卸载流程、生命周期管理、参数配置方法,并通过“Hello World”模块和字符设备驱动实例,带领读者逐步掌握模块开发技能。同时,介绍了调试手段、常见问题排查、开发规范及高级特性,如内核线程、模块间通信与性能优化策略。适合希望深入理解Linux内核机制、提升系统编程能力的技术人员阅读与实践。
971 1
|
11月前
|
监控 Ubuntu Linux
什么Linux,Linux内核及Linux操作系统
上面只是简单的介绍了一下Linux操作系统的几个核心组件,其实Linux的整体架构要复杂的多。单纯从Linux内核的角度,它要管理CPU、内存、网卡、硬盘和输入输出等设备,因此内核本身分为进程调度,内存管理,虚拟文件系统,网络接口等4个核心子系统。
1080 0
|
11月前
|
Web App开发 缓存 Rust
|
11月前
|
Ubuntu 安全 Linux
Ubuntu 发行版更新 Linux 内核,修复 17 个安全漏洞
本地攻击者可以利用上述漏洞,攻击 Ubuntu 22.10、Ubuntu 22.04、Ubuntu 20.04 LTS 发行版,导致拒绝服务(系统崩溃)或执行任意代码。
|
10月前
|
Linux 应用服务中间件 Shell
二、Linux文本处理与文件操作核心命令
熟悉了Linux的基本“行走”后,就该拿起真正的“工具”干活了。用grep这个“放大镜”在文件里搜索内容,用find这个“探测器”在系统中寻找文件,再用tar把东西打包带走。最关键的是要学会使用管道符|,它像一条流水线,能把这些命令串联起来,让简单工具组合出强大的功能,比如 ps -ef | grep 'nginx' 就能快速找出nginx进程。
1031 1
二、Linux文本处理与文件操作核心命令
|
10月前
|
Linux
linux命令—stat
`stat` 是 Linux 系统中用于查看文件或文件系统详细状态信息的命令。相比 `ls -l`,它提供更全面的信息,包括文件大小、权限、所有者、时间戳(最后访问、修改、状态变更时间)、inode 号、设备信息等。其常用选项包括 `-f` 查看文件系统状态、`-t` 以简洁格式输出、`-L` 跟踪符号链接,以及 `-c` 或 `--format` 自定义输出格式。通过这些选项,用户可以灵活获取所需信息,适用于系统调试、权限检查、磁盘管理等场景。
672 137
|
10月前
|
安全 Ubuntu Unix
一、初识 Linux 与基本命令
玩转Linux命令行,就像探索一座新城市。首先要熟悉它的“地图”,也就是/根目录下/etc(放配置)、/home(住家)这些核心区域。然后掌握几个“生存口令”:用ls看周围,cd去别处,mkdir建新房,cp/mv搬东西,再用cat或tail看文件内容。最后,别忘了随时按Tab键,它能帮你自动补全命令和路径,是提高效率的第一神器。
1633 58
|
9月前
|
存储 安全 Linux
Linux卡在emergency mode怎么办?xfs_repair 命令轻松解决
Linux虚拟机遇紧急模式?别慌!多因磁盘挂载失败。本文教你通过日志定位问题,用`xfs_repair`等工具修复文件系统,三步快速恢复。掌握查日志、修磁盘、验重启,轻松应对紧急模式,保障系统稳定运行。
1513 2