linux poll机制使用(一)

一、poll机制的作用

1.poll机制的作用

在前面的使用中断的的方式来读取按键值(linux 中断管理(四))。使用这种方式读取按键,如果按键没有按下的时候,应用程序会一直处于睡眠的状态。如果想要即使按键没有按下,在一定的时间后也能返回,要实现这种功能,可以使用poll机制。(select IO复用epoll也可以实现这种功能,这里只写poll机制)

二、poll机制的应用编程

1.应用层函数接口

1).API:

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

2).Paramet:

fds nfds timeout
参数类型:
????struct pollfd {
????int fd; /* 文件描述符 /
????short events; /
等待的发生事件类型 /
????short revents; /
实际返回的事件类型 */
????};
参数描述:
????fds是一个结构体指针,也就是poll函数可以同时等待一个或者多个文件描述符的事件
参数类型:
????nfds_t,其实就是int型
参数描述:
????用来说明poll同时监听fds的个数
参数类型:
????int
参数描述:
????等于-1:永久等待
????等于0:立即返回
????大于0:等待超时时间,以毫秒为单位

3).Return:

返回值 描述
<0 错误返回
=0 超时返回
>0 返回结构体中 revents 域不为 0 的文件描述符个数

2.应用程序

应用程序主要使用poll的方式读取按键的值,并且设置5000ms超时等待

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h>

/* poll机制测试
 */
int main(int argc, char **argv)
{
    int fd;
    unsigned char key_val;
    int ret;
    /* 定义一个 struct pollfd 结构体数组*/
    struct pollfd fds[1];
    /* 打开一个设备文件 */
    fd = open("/dev/my_button", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }
    /**************** 初始化 struct pollfd 结构体 *************/
    /* 初始化文件描述符 */
    fds[0].fd     = fd;
    /* 初始化监听的事件事件 */
    fds[0].events = POLLIN;
    while (1)
    {
        /* 调用poll函数,超时时间是5000ms */
        ret = poll(fds, 1, 5000);

        if (ret == 0){
            /* 超时返回 */
            printf("time out\n");
        }else if(ret<0){
            /* 出错返回 */
        printf("poll error\n");
        }else{
            /* 有数据可读,读取数据 */
            /* 这里为了简单就不对返回的事件revents,做判断和重置了 */
            read(fd, &key_val, 1);
            printf("key_val = %d\n", key_val);
        }
    }

    return 0;
}

三、驱动程序

驱动程序的编写主要在file_operationspoll成员添加一个函数接口button_poll。下面的程序是在linux中断管理(四)更改的(不过本章节只使用了一个按键)。更改的代码如下:

#include <linux/device.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <linux/cdev.h>
#include <linux/poll.h>

/* 申请一个等待队列头 */
DECLARE_WAIT_QUEUE_HEAD (button_waitq);
/* 事件触发标志 */
int ev_press;

/* 用一个结构体来描述一个按键,抽象的或许不是很好 */
struct but_irq_desc{
    /* 中断名称 */
    char *button_nane;
    /* 按键的状态,1松开,0按下 */
    unsigned char key_state;
    /* 按键所接的GPIO口 */
    unsigned long pin;
    /* 按键的中断号 */
    unsigned int irq;
};

struct but_irq_desc but_irq_descs={
    .button_nane="button1",
    .key_state = 1,
    .pin = S3C2410_GPF0,
    .irq=IRQ_EINT0,
};

static struct cdev *button_cdev;
static struct class *button_class;

struct file_operations *button_fops;

/* 中断处理函数 */
static irqreturn_t button_irq(int irq, void * dev_id)
{
    /* 获取这个按键的结构体 */
    struct but_irq_desc *btndesc = (struct but_irq_desc *)dev_id;
    unsigned int pinval;
    /* 读取按键的电平 */
    pinval = s3c2410_gpio_getpin(btndesc->pin);
    /* 如果是高电平*/
    if(pinval){
        /* 按键松开 */
        btndesc->key_state = 1;
    }else{
        btndesc->key_state = 0;
        /* 按键按下 */
    }
    /* 唤醒该等待队列里的进程 */
    wake_up_interruptible(&button_waitq);
    /* 将标志置1 */
    ev_press = 1;
    return IRQ_HANDLED;
}

int button_open (struct inode * inode, struct file *file){
    /* 注册驱动,中断为上升沿和下降沿触发 */
    request_irq(but_irq_descs.irq, button_irq,        IRQF_TRIGGER_RISING| IRQF_TRIGGER_FALLING,        but_irq_descs.button_nane,(void*)(&but_irq_descs));
    return 0;
}
int button_release (struct inode *inode, struct file * file){
    /* 释放中断 */
    free_irq(but_irq_descs.irq,(void*)(&but_irq_descs));
    return 0;
}

/* 驱动读函数 */
ssize_t button_read(struct file *file, char __user *buf, size_t size, loff_t * offset){
    unsigned char key_val;
    if(size!=1){
        return -EINVAL;
    }
    /* 等待按键按下,如果按键按下,则这个进程会被唤醒(以后有时间系一章等待队列的源码分析) */
    /* 如果ev_press等于1的时候则这个进程不会被挂起,等于0的时候这个进程才会被挂起 */
    wait_event_interruptible(button_waitq, ev_press);
    key_val = but_irq_descs.key_state;
    /* 将按键值返回给应用层 */
    copy_to_user(buf, &key_val, 1);
    /* 将标志置0将进程挂起,等待下一次唤醒 */
    ev_press = 0;
    return 1;
}

/*********************本章中增加的函数****************************/
static unsigned int button_poll(struct file *file, poll_table *wait)
{
    unsigned int res = 0;
    poll_wait(file, &button_waitq, wait);
    /************************ poll_wait函数定义如下 **********************/
    /* static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
     * {
     *     if (p && wait_address)
     *         p->qproc(filp, wait_address, p);
     *         //本质调用的是__pollwait(filp, wait_address, p);这个函数
     * }
     */
    /***********************************************************************/

    /* p->qproc 是一个函数指针,指向 __pollwait函数。
     * __pollwait 做的事情也就是将当前的进程添加到 button_waitq 等待队列中。
     * 这里只是添加到等待队列而已,并不会立即休眠。
     * (下一章分析poll机制源码的时候会详细分析)
     */

    /* 如果当前有按键按下,则返回POLLIN 和 POLLRDNORM事件,
     * 否则返回0
     * 返回非0则当前的进程不会休眠,返回0当前的进程会休眠
     */
    if (ev_press)
        res = POLLIN | POLLRDNORM;
    /* 返回res */
    return res;
}
/*********************本章中增加的函数****************************/

static dev_t dev_id;

/* 模块入口函数 */
static int __init my_button_init(void){

    /* 分配一个file_operations 结构体 */
    button_fops = kmalloc(sizeof(struct file_operations), GFP_KERNEL);
    /* 初始化接口函数 */
    button_fops->open = button_open;
    button_fops->release = button_release;
    button_fops->read = button_read;
    button_fops->poll = button_poll;
    /* 动态分配一个设备号 */
    alloc_chrdev_region(&dev_id, 0, 1, "my_button");
    /* 分配一个cdev结构体 */
    button_cdev = cdev_alloc();
    /* 将cdev结构体和 fops结构体绑定*/
    cdev_init(button_cdev, button_fops);
    /* 将驱动注册到内核中 */
    cdev_add(button_cdev, dev_id,1);
    /* 创建一个class */
    button_class = class_create(THIS_MODULE, "my_button");
    /* 根据class内容创建一个设备节点 my_button*/
    class_device_create(button_class, NULL, dev_id, NULL,"my_button");
    return 0;
}

/* 模块出口函数 */
static void __exit my_button_exit(void){

    /* 销毁设别节点 */
    class_device_destroy(button_class, dev_id);
    /* 销毁设备节点 */
    class_destroy(button_class);
    /* 释放cdev结构体空间 */
    cdev_del(button_cdev);
    /* 注销设备号 */
    unregister_chrdev_region(dev_id,1);
    /* 释放fops空间 */
    kfree(button_fops);

}

/* 声明模块入口 */
module_init(my_button_init);
/* 声明模块出口 */
module_exit(my_button_exit);
/* 遵循GPL协议 */
MODULE_LICENSE("GPL");

四:实验现象

1.按照前面的几章的步骤,编译驱动程序和应用程序。然后将编译好的内核模块和应用程序copy到文件系统。然后使用insmod xxx.ko插入内核模块,执行应用程序。
2.观察实验现象(如下图):

  • 按键按下时打印:key_val=0
  • 按键松开时打印:key_val=1
  • 如果5000毫秒后,没有按键按下也没有按键松开则打印time out

原文地址:https://www.cnblogs.com/gulan-zmc/p/12229159.html

时间: 2024-10-12 00:01:00

linux poll机制使用(一)的相关文章

linux poll机制分析(二)

一.回顾 在linux poll机制使用(一)写了个实现poll机制的简单例子.在驱动模块中需要实现struct file_operations的.poll成员.在驱动模块中xxx_poll函数的的作用是将当前进程添加到等待队列中:然后判断事件是否发生,发生则返回POLLIN | POLLRDNORM,否则返回0(可以看看上一章的例子):接下来分析一下 linux 内核中 poll 机制的实现. 二.poll机制分析 1.系统调用 当应用层调用poll函数时,linux发生系统调用(系统调用入口

Linux poll机制

所有的系统调用,基于都可以在它的名字前加上“sys_”前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架:对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制.1.sys_poll函数位于fs/select.c文件中,代码如下:asmlinkage long sys_poll(stru

linux驱动程序中的poll机制编程

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <asm/irq.h> #include <linux/interrupt.h> #include <asm/uaccess.h> #include

Linux嵌入式驱动学习之路(十二)按键驱动-poll机制

实现的功能是在读取按键信息的时候,如果没有产生按键,则程序休眠在read函数中,利用poll机制,可以在没有退出的情况下让程序自动退出. 下面的程序就是在读取按键信息的时候,如果5000ms内没有按键信息,则自己退出. 首先应用程序执行poll函数 kernel中的sys_poll do_sys_poll init_poll_funcptr-->do_poll do_poll for(;;) { if(do_pollfd(pfd,pt)) { count++; //如果驱动的poll返回非0值,

Linux通信之poll机制分析

poll机制分析 韦东山 2009.12.10 所有的系统调用,基于都可以在它的名字前加上"sys_"前缀,这就是它在内核中对应的函数.比如系统调用open.read.write.poll,与之对应的内核函数为:sys_open.sys_read.sys_write.sys_poll. 一.内核框架: 对于系统调用poll或select,它们对应的内核函数都是sys_poll.分析sys_poll,即可理解poll机制. sys_poll函数位于fs/select.c文件中,代码如下:

Linux之poll机制分析

应用程序访问1个设备文件时可用阻塞/非阻塞方式.如果是使用阻塞方式,则直接调用open().read().write(),但是在驱动程序层会判断是否可读/可写,如果不可读/不可写,则将当前进程休眠,直到被唤醒.如果是使用非阻塞方式,就需要采用poll/select机制,而且打开文件时标记文件的访问权限位为O_NONBLOCK. 1 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct t

入门级的按键驱动——按键驱动笔记之poll机制-异步通知-同步互斥阻塞-定时器防抖

文章对应视频的第12课,第5.6.7.8节. 在这之前还有查询方式的驱动编写,中断方式的驱动编写,这篇文章中暂时没有这些类容.但这篇文章是以这些为基础写的,前面的内容有空补上. 按键驱动——按下按键,打印键值: 目录 概要 poll机制 异步通知 同步互斥阻塞 定时器防抖 概要: 查询方式: 12-3 缺点:占用CPU99%的资源.中断方式:12-4 缺点:调用read函数后如果没有按键按下,该函数永远不会结束,一直在等待按键按下. 优点:使用到了休眠机制,占用cpu资源极少.poll机制: 1

字符设备之poll机制

poll机制作用:相当于一个定时器,时间到了还没有资源就唤醒进程. 主要用途就是:进程设置一段时间用来等待资源,如果时间到了资源还没有到来,进程就立刻从睡眠状态唤醒不再等待.当然这只是使用于这段时间以后资源对于该进程已经无用的情况. 内核中poll机制的实现过程: sys_poll函数在include/linux/syscalls.h中声明 //函数定义前加宏asmlinkage ,表示这些函数通过堆栈而不是通过寄存器传递参数. asmlinkage long sys_poll(struct p

字符设备驱动(六)按键poll机制

title: 字符设备驱动(六)按键poll机制 tags: linux date: 2018-11-23 18:57:40 toc: true --- 字符设备驱动(六)按键poll机制 引入 在字符设备驱动(五)按键休眠中的App中虽然使用了休眠,但是如果Read没有返回的话会一直死等,类似阻塞,我们期望等待一段时间后自动返回,等待的时候程序依然是睡眠的,这里引入poll机制 应用程序的open/close/write/read都有对应的系统内核的sys_open/sys_close/sys