linux poll设计,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

#include

#include

#include

#include

/* 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_operations的poll成员添加一个函数接口button_poll。下面的程序是在linux中断管理(四)更改的(不过本章节只使用了一个按键)。更改的代码如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

/* 申请一个等待队列头 */

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

d845d4e8719326b82e6f01bad30571e9.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值