3.字符设备驱动------Poll机制
1.poll情景描述
以之前的按键驱动为例进行说明,用阻塞的方式打开按键驱动文件/dev/buttons,应用程序使用read()函数来读取按键的键值。
while ()
{
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
}
这样做的效果是:如果有按键按下了,调用该read()函数的进程,就成功读取到数据,应用程序得到继续执行;倘若没有按键按下,则要一直处于休眠状态,等待这有按键按下这样的事件发生。
这种功能在一些场合是适用的,但是并不能满足我们所有的需要,有时我们需要一个时间节点。倘若没有按键按下,那么超过多少时间之后,也要返回超时错误信息,进程能够继续得到执行,而不是没有按键按下,就永远休眠。这种例子其实还有很多,比方说两人相亲,男方等待女方给个确定相处的信,男方不可能因为女方不给信,就永远等待下去,双方需要一个时间节点。这个时间节点,就是说超过这个时间之后,不能再等了,程序还要继续运行,需要采取其他的行动来解决问题。
poll机制作用:相当于定时器,设置一定时间使进程等待资源,如果时间到了中断还处于睡眠状态(等待队列),poll机制就会唤醒中断,获取一次资源
2.poll机制内核框架
在用户层上,使用poll或select函数时,和open、read那些函数一样,也要进入内核sys_poll函数里,接下来我们分析sys_poll函数来了解poll机制(位于/fs/select.c)
2.1 sys_poll:
sys_poll
函数会对超时参数timeout_msecs
作简单处理后调用do_sys_poll
asmlinkage long sys_poll(struct pollfd __user *ufds, unsigned int nfds,long timeout_msecs)
{
if (timeout_msecs > ) //参数timeout>0
{
timeout_jiffies = msecs_to_jiffies(timeout_msecs); //通过频率来计算timeout时间需要多少计数值
}
else
{
timeout_jiffies = timeout_msecs; //如果timeout时间为0,直接赋值
}
return do_sys_poll(ufds, nfds, &timeout_jiffies); //调用do_sys_poll。
}
2.2 do_sys_poll
函数do_sys_poll
会调用poll_initwait
来初始化一个struct poll_wqueues table
,也就是table->pt->qproc = __pollwait
,__pollwait
将在驱动的poll
函数里用到,接着调用do_poll
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
struct poll_wqueues table;
int fdcount, err;
unsigned int i;
struct poll_list *head;
struct poll_list *walk;
/* Allocate small arguments on the stack to save memory and be
faster - use long to make sure the buffer is aligned properly
on 64 bit archs to avoid unaligned access */
long stack_pps[POLL_STACK_ALLOC/sizeof(long)];
struct poll_list *stack_pp = NULL; /* Do a sanity check on nfds ... */
if (nfds > current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
return -EINVAL; poll_initwait(&table); head = NULL;
walk = NULL;
i = nfds;
err = -ENOMEM;
while(i!=) {
struct poll_list *pp;
int num, size;
if (stack_pp == NULL)
num = N_STACK_PPS;
else
num = POLLFD_PER_PAGE;
if (num > i)
num = i;
size = sizeof(struct poll_list) + sizeof(struct pollfd)*num;
if (!stack_pp)
stack_pp = pp = (struct poll_list *)stack_pps;
else {
pp = kmalloc(size, GFP_KERNEL);
if (!pp)
goto out_fds;
}
pp->next=NULL;
pp->len = num;
if (head == NULL)
head = pp;
else
walk->next = pp; walk = pp;
if (copy_from_user(pp->entries, ufds + nfds-i,
sizeof(struct pollfd)*num)) {
err = -EFAULT;
goto out_fds;
}
i -= pp->len;
} fdcount = do_poll(nfds, head, &table, timeout); /* OK, now copy the revents fields back to user space. */
walk = head;
err = -EFAULT;
while(walk != NULL) {
struct pollfd *fds = walk->entries;
int j; for (j=; j < walk->len; j++, ufds++) {
if(__put_user(fds[j].revents, &ufds->revents))
goto out_fds;
}
walk = walk->next;
}
err = fdcount;
if (!fdcount && signal_pending(current))
err = -EINTR;
out_fds:
walk = head;
while(walk!=NULL) {
struct poll_list *pp = walk->next;
if (walk != stack_pp)
kfree(walk);
walk = pp;
}
poll_freewait(&table);
return err;
}
do_sys_poll
int do_sys_poll(struct pollfd __user *ufds, unsigned int nfds, s64 *timeout)
{
... ...
/*初始化一个poll_wqueues变量table*/
poll_initwait(&table);
... ...
fdcount = do_poll(nfds, head, &table, timeout);
... ...
}
2.2.1 进入poll_initwait
table ->pt-> qproc=__pollwait; //__pollwait将在驱动的poll函数里的poll_wait函数用到
poll_initwait(&table); void poll_initwait(struct poll_wqueues *pwq)
{
init_poll_funcptr(&pwq->pt, __pollwait);
pwq->error = ;
pwq->table = NULL;
pwq->inline_index = ;
} static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
{
pt->qproc = qproc;
} 也就是最终是 table->pt->qproc=__pollwait
那么_pollwait做了什么
/* Add a new entry */
static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
poll_table *p)
{
struct poll_table_entry *entry = poll_get_entry(p);
if (!entry)
return;
get_file(filp);
entry->filp = filp;
entry->wait_address = wait_address;
init_waitqueue_entry(&entry->wait, current);
add_wait_queue(wait_address, &entry->wait); /*将当前进程current挂到队列里面去*/
}
2.2.2 进入 do_poll
static int do_poll(unsigned int nfds, struct poll_list *list,
struct poll_wqueues *wait, s64 *timeout)
{
int count = ;
poll_table* pt = &wait->pt; /* Optimise the no-wait case */
if (!(*timeout))
pt = NULL; for (;;) {
struct poll_list *walk;
long __timeout; set_current_state(TASK_INTERRUPTIBLE);
for (walk = list; walk != NULL; walk = walk->next) {
struct pollfd * pfd, * pfd_end; pfd = walk->entries;
pfd_end = pfd + walk->len;
for (; pfd != pfd_end; pfd++) {
/*
* Fish for events. If we found one, record it
* and kill the poll_table, so we don't
* needlessly register any other waiters after
* this. They'll get immediately deregistered
* when we break out and return.
*/
if (do_pollfd(pfd, pt)) {
count++;
pt = NULL;
}
}
}
/*
* All waiters have already been registered, so don't provide
* a poll_table to them on the next loop iteration.
*/
pt = NULL;
if (count || !*timeout || signal_pending(current))
break;
count = wait->error;
if (count)
break; if (*timeout < ) {
/* Wait indefinitely */
__timeout = MAX_SCHEDULE_TIMEOUT;
} else if (unlikely(*timeout >= (s64)MAX_SCHEDULE_TIMEOUT-)) {
/*
* Wait for longer than MAX_SCHEDULE_TIMEOUT. Do it in
* a loop
*/
__timeout = MAX_SCHEDULE_TIMEOUT - ;
*timeout -= __timeout;
} else {
__timeout = *timeout;
*timeout = ;
} __timeout = schedule_timeout(__timeout);
if (*timeout >= )
*timeout += __timeout;
}
__set_current_state(TASK_RUNNING);
return count;
}
do_poll
static int do_poll(unsigned int nfds, struct poll_list *list, struct poll_wqueues *wait, s64 *timeout)
{
……
for (;;)
{
……
set_current_state(TASK_INTERRUPTIBLE); //设置为等待队列状态
......
for (; pfd != pfd_end; pfd++) { //for循环运行多个poll机制
/*将pfd和pt参数代入我们驱动程序里注册的poll函数*/
if (do_pollfd(pfd, pt)) //若返回非0,count++,后面并退出
{ count++;
pt = NULL; } } …… /*count非0(.poll函数返回非0),timeout超时计数到0,有信号在等待*/ if (count || !*timeout || signal_pending(current))
break;
……
/*进入休眠状态,只有当timeout超时计数到0,或者被中断唤醒才退出,*/
__timeout = schedule_timeout(__timeout); …… } __set_current_state(TASK_RUNNING); //开始运行
return count; }
2.2.2.1 do_poll中的do_pollfd函数到底是怎么将pfd和pt参数代入的?
if (do_pollfd(pfd, pt)) -->>>mask = file->f_op->poll(file, pwait); return mask;
static inline unsigned int do_pollfd(struct pollfd *pollfd, poll_table *pwait)
{
……
if (file->f_op && file->f_op->poll)
mask = file->f_op->poll(file, pwait); //f_op 即 file_operation结构体
…… return mask;
}
当poll进入休眠状态后,又是谁来唤醒它?这就要分析我们的驱动程序.poll函数
3.写驱动程序.poll函数,并分析.poll函数
在上一节驱动程序里添加以下代码:
static unsigned int forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); //将进程挂到队列里,不会立即休眠 if (ev_press) //中断事件标志, 1:退出休眠状态 0:进入休眠状态
mask |= POLLIN | POLLRDNORM; //普通数据可读|优先级带数据可读
return mask; //当超时,就返给应用层为0 ,被唤醒了就返回POLLIN | POLLRDNORM
} static struct file_operations forth_drv_fops = {
.owner = THIS_MODULE,
.open = forth_drv_open,
.read = forth_drv_read,
.release = forth_drv_close,
.poll = forth_drv_poll, //创建.poll函数
};
4.测试·应用程序
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <poll.h> /* forthdrvtest
*/
int main(int argc, char **argv)
{
int fd;
unsigned char key_val;
int ret; struct pollfd fds[]; fd = open("/dev/buttons", O_RDWR);
if (fd < )
{
printf("can't open!\n");
} fds[].fd = fd;
fds[].events = POLLIN;
while ()
{
ret = poll(fds, , );
if (ret == )
{
printf("time out\n");
}
else
{
read(fd, &key_val, );
printf("key_val = 0x%x\n", key_val);
}
} return ;
}
forthdrv_test.c
驱动程序
#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 <asm/hardware.h>
#include <linux/poll.h> static struct class *forthdrv_class;
static struct class_device *forthdrv_class_dev; volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat; volatile unsigned long *gpgcon;
volatile unsigned long *gpgdat; static DECLARE_WAIT_QUEUE_HEAD(button_waitq); /* 中断事件标志, 中断服务程序将它置1,forth_drv_read将它清0 */
static volatile int ev_press = ; struct pin_desc{
unsigned int pin;
unsigned int key_val;
}; /* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
static unsigned char key_val; struct pin_desc pins_desc[] = {
{S3C2410_GPF0, 0x01},
{S3C2410_GPF2, 0x02},
{S3C2410_GPG3, 0x03},
{S3C2410_GPG11, 0x04},
}; /*
* 确定按键值
*/
static irqreturn_t buttons_irq(int irq, void *dev_id)
{
struct pin_desc * pindesc = (struct pin_desc *)dev_id;
unsigned int pinval; pinval = s3c2410_gpio_getpin(pindesc->pin); if (pinval)
{
/* 松开 */
key_val = 0x80 | pindesc->key_val;
}
else
{
/* 按下 */
key_val = pindesc->key_val;
} ev_press = ; /* 表示中断发生了 */
wake_up_interruptible(&button_waitq); /* 唤醒休眠的进程 */ return IRQ_RETVAL(IRQ_HANDLED);
} static int forth_drv_open(struct inode *inode, struct file *file)
{
/* 配置GPF0,2为输入引脚 */
/* 配置GPG3,11为输入引脚 */
request_irq(IRQ_EINT0, buttons_irq, IRQT_BOTHEDGE, "S2", &pins_desc[]);
request_irq(IRQ_EINT2, buttons_irq, IRQT_BOTHEDGE, "S3", &pins_desc[]);
request_irq(IRQ_EINT11, buttons_irq, IRQT_BOTHEDGE, "S4", &pins_desc[]);
request_irq(IRQ_EINT19, buttons_irq, IRQT_BOTHEDGE, "S5", &pins_desc[]); return ;
} ssize_t forth_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
if (size != )
return -EINVAL; /* 如果没有按键动作, 休眠 */
wait_event_interruptible(button_waitq, ev_press); /* 如果有按键动作, 返回键值 */
copy_to_user(buf, &key_val, );
ev_press = ; return ;
} int forth_drv_close(struct inode *inode, struct file *file)
{
free_irq(IRQ_EINT0, &pins_desc[]);
free_irq(IRQ_EINT2, &pins_desc[]);
free_irq(IRQ_EINT11, &pins_desc[]);
free_irq(IRQ_EINT19, &pins_desc[]);
return ;
} static unsigned forth_drv_poll(struct file *file, poll_table *wait)
{
unsigned int mask = ;
poll_wait(file, &button_waitq, wait); // 不会立即休眠 if (ev_press)
mask |= POLLIN | POLLRDNORM; return mask;
} static struct file_operations sencod_drv_fops = {
.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = forth_drv_open,
.read = forth_drv_read,
.release = forth_drv_close,
.poll = forth_drv_poll,
}; int major;
static int forth_drv_init(void)
{
major = register_chrdev(, "forth_drv", &sencod_drv_fops); forthdrv_class = class_create(THIS_MODULE, "forth_drv"); forthdrv_class_dev = class_device_create(forthdrv_class, NULL, MKDEV(major, ), NULL, "buttons"); /* /dev/buttons */ gpfcon = (volatile unsigned long *)ioremap(0x56000050, );
gpfdat = gpfcon + ; gpgcon = (volatile unsigned long *)ioremap(0x56000060, );
gpgdat = gpgcon + ; return ;
} static void forth_drv_exit(void)
{
unregister_chrdev(major, "forth_drv");
class_device_unregister(forthdrv_class_dev);
class_destroy(forthdrv_class);
iounmap(gpfcon);
iounmap(gpgcon);
return ;
} module_init(forth_drv_init); module_exit(forth_drv_exit); MODULE_LICENSE("GPL");
forth_drv.c
5.相关参数:
POLLIN相关:
常量 说明 POLLIN 普通或优先级带数据可读 POLLRDNORM normal普通数据可读 POLLRDBAND 优先级带数据可读 POLLPRI Priority高优先级数据可读 POLLOUT 普通数据可写 POLLWRNORM normal普通数据可写 POLLWRBAND band优先级带数据可写 POLLERR 发生错误 POLLHUP 发生挂起 POLLNVAL 描述字不是一个打开的文件
POLLxxx
poll函数相关
int poll(struct pollfd *fds, nfds_t nfds, int timeout)
1) *fds:是一个poll描述符结构体数组(可以处理多个poll),结构体pollfd如下:
struct pollfd {
int fd; /* file descriptor 文件描述符*/
short events; /* requested events 请求的事件 --其中events=POLLIN
表示期待有数据读取.*/
short revents; /* returned events 返回的事件(函数返回值)*/
};
2) nfds:表示多少个poll,如果1个,就填入1
3) timeout:定时多少ms
6.调用流程
(1)当应用程序调用poll函数的时候,会调用到系统调用sys_poll函数,该函数最终调用do_poll函数
(2)do_poll函数中有一个死循环,在里面又会利用do_pollfd函数去调用驱动中的poll函数(fds中每个成员的字符驱动程序都会被扫描到),
(3)驱动程序中的Poll函数的工作 有两个
①调用poll_wait 函数,把进程挂到等待队列中去(这个是必须的,你要睡眠,必须要在一个等待队列上面,否则到哪里去唤醒你呢??),
②另一个是确定相关的fd是否有内容可读,如果可读,就返回1,否则返回0,如果返回1 ,do_poll函数中的count++
(4) do_poll函数中判断三个条件(if (count ||!timeout || signal_pending(current)))
①如果成立就直接跳出,
②如果不成立,就睡眠timeout个jiffes这么长的时间(调用schedule_timeout实现睡眠),
如果在这段时间内没有其他进程去唤醒它,那么第二次执行判断的时候就会跳出死循环。(????????不懂)
如果在这段时间内有其他进程唤醒它,那么也可以跳出死循环返回
(例如我们可以利用中断处理函数去唤醒它,这样的话一有数据可读,就可以让它立即返回)。
7.测试
加载驱动后可以发现超时后会打印超时,按键有输出
# insmod dri.ko
# ./test /dev/xyz0
time out
irq55
key_val = 0x3
irq55
key_val = 0x83
ps
查询是休眠状态,top
查询cpu
也比较低
#ps
PID Uid VSZ Stat Command
S ./test /dev/xyz0 #top
PID PPID USER STAT VSZ %MEM %CPU COMMAND
S % % ./test /dev/xyz0
参考文章:
linux字符驱动之poll机制按键驱动
8.中断按键驱动程序之poll机制(详解)
字符设备驱动(六)按键poll机制
3.字符设备驱动------Poll机制的更多相关文章
- Linux高级字符设备驱动 poll方法(select多路监控原理与实现)
1.什么是Poll方法,功能是什么? 2.Select系统调用(功能) Select系统调用用于多路监控,当没有一个文件满足要求时,select将阻塞调用进程. int selec ...
- 字符设备之poll机制
poll机制作用:相当于一个定时器.时间到了还没有资源就唤醒进程. 主要用途就是:进程设置一段时间用来等待资源,假设时间到了资源还没有到来,进程就立马从睡眠状态唤醒不再等待.当然这仅仅是使用于这段时间 ...
- 字符设备驱动(六)按键poll机制
title: 字符设备驱动(六)按键poll机制 tags: linux date: 2018-11-23 18:57:40 toc: true --- 字符设备驱动(六)按键poll机制 引入 在字 ...
- Linux字符设备驱动框架
字符设备是Linux三大设备之一(另外两种是块设备,网络设备),字符设备就是字节流形式通讯的I/O设备,绝大部分设备都是字符设备,常见的字符设备包括鼠标.键盘.显示器.串口等等,当我们执行ls -l ...
- Linux驱动设计——字符设备驱动(一)
Linux字符设别驱动结构 cdev结构体 struct cdev { struct kobject kobj; struct module *owner; const struct file_ope ...
- Linux字符设备驱动
一.字符设备基础 字符设备 二.字符设备驱动与用户空间访问该设备的程序三者之间的关系 三.字符设备模型 1.Linux内核中,使用 struct cdev 来描述一个字符设备 动态申请(构造)cdev ...
- 《linux设备驱动开发详解》笔记——6字符设备驱动
6.1 字符设备驱动结构 先看看字符设备驱动的架构: 6.1.1 cdev cdev结构体是字符设备的核心数据结构,用于描述一个字符设备,cdev定义如下: #include <linux/cd ...
- 浅析Linux字符设备驱动程序内核机制
前段时间在学习linux设备驱动的时候,看了陈学松著的<深入Linux设备驱动程序内核机制>一书. 说实话.这是一本非常好的书,作者不但给出了在设备驱动程序开发过程中的所须要的知识点(如对 ...
- (57)Linux驱动开发之三Linux字符设备驱动
1.一般情况下,对每一种设备驱动都会定义一个软件模块,这个工程模块包含.h和.c文件,前者定义该设备驱动的数据结构并声明外部函数,后者进行设备驱动的具体实现. 2.典型的无操作系统下的逻辑开发程序是: ...
随机推荐
- HDUOJ Let the Balloon Rise 1004
/* Let the Balloon Rise Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Ja ...
- Perl Learning 5 Hash
[本文原创,未经同意请勿转载] 哈希是一种数据结构,它和数组的相似之处在于能够容纳随意多的值并能按需取用,而它和数组的不同在于索引方式,数组是以数字来索引.哈希则以名字来索引.也就是说.哈希的索引值, ...
- xml方式封装通信数据方法
xml方式封装通信数据方法 public static function xmlToEncode($data) { $xml = ""; foreach($data as $key ...
- Android Design Support Library初探,NavigationView实践
前言 在前几天的IO大会上,Google带来了Android M,同时还有Android支持库的新一轮更新,其中更是增加一个全新的支持库Android Design Support Library,包 ...
- dubbo问题求解
各位大牛好,小弟公司开发中遇到一个很奇怪的问题望有大神指教一下,实在是已经搞了3天了一点头绪没有,公司使用的是eclipse+maven+zookeper+dubbo主要是dubbo的问题,刚开始使用 ...
- OpenGL编程逐步深入(二)在窗口中显示一个点
准备知识 在本文中我们将会接触到OpenGl的扩展库GLEW( OpenGL Extension Wrangler Library),GLEW可以帮助我们处理OpenGl中繁琐的扩展管理.一旦初始化后 ...
- sql中使用正则查询
- 应对加密js的三种方法
经常遇到网页在登录后会对用户输入的帐号和密码通过js进行加密,导致模拟登录这类网站时受到阻碍 这里小记一下当前解决该问题的三种方法 1.利用python实现js同等加密. 2.利用selenium模拟 ...
- react-native-swiper苹果正常显示,Android不显示
在使用react-native-swiper时,最好不要放到(FlatList , SectionList,ListView,ScrollView 等)组件中,否则Android 可能不会正常显示图片 ...
- 【Codeforces Round #424 (Div. 2) B】Keyboard Layouts
[Link]:http://codeforces.com/contest/831/problem/B [Description] 两个键盘的字母的位置不一样; 数字键的位置一样; 告诉你第一个键盘按某 ...