linux设备驱动:poll和sellct
来源:互联网 发布:安卓读屏软件 编辑:程序博客网 时间:2024/06/11 18:57
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
接下来会讲系统调用select在驱动中的实现,如果对系统调用select不太懂的话,建议先看书补习一下。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
一、系统调用select的简介
简单来说,select这个系统调用的作用就是在应用层调用驱动函数中的poll来检测指定的文件的状态(读、写和异常)。如果某个状态满足,select函数调用成功后返回,应用程序就可以通过指定的函数来判断现在的文件状态。注意的是:select可以指定判断的时间,指定时间内,应用程序会阻塞在select函数,直到状态满足或者超时。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
二、驱动函数poll的实现
先上代码:
9 #include <linux/poll.h>
10
11 #include <asm/uaccess.h>
12 #include <linux/errno.h>
。。。。。。省略。。。。。。
23 struct _test_t{
24 char kbuf[DEV_SIZE];
25 unsigned int major;
26 unsigned int minor;
27 unsigned int cur_size;
28 dev_t devno;
29 struct cdev test_cdev;
30 wait_queue_head_t test_queue;
31 wait_queue_head_t read_queue; //定义等待队列
32 };
。。。。。。省略。。。。。。。
70 ssize_t test_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
71 {
72 int ret;
73 struct _test_t *dev = filp->private_data;
74
75 if(copy_from_user(dev->kbuf, buf, count)){
76 ret = - EFAULT;
77 }else{
78 ret = count;
79 dev->cur_size += count;
80 P_DEBUG("write %d bytes, cur_size:[%d]\n", count, dev->cur_size);
81 P_DEBUG("kbuf is [%s]\n", dev->kbuf);
82 wake_up_interruptible(&dev->test_queue);
83 wake_up_interruptible(&dev->read_queue); //唤醒等待队列
84 }
85
86 return ret; //返回实际写入的字节数或错误号
87 }
88 /*poll的实现*/
89 unsigned int test_poll (struct file *filp, struct poll_table_struct *table)
90 {
91 struct _test_t *dev = filp->private_data;
92 unsigned int mask = 0;
93
94 poll_wait(filp, &dev->read_queue, table);
95
96 if(dev->cur_size > 0) //设备可读
97 mask |= POLLIN;
98
99 P_DEBUG("***maks[%d]***\n", mask);
100 return mask;
101 }
102
103 struct file_operations test_fops = {
104 .open = test_open,
105 .release = test_close,
106 .write = test_write,
107 .read = test_read,
108 .poll = test_poll, //切记要添加,不然多牛X的代码都不能执行
109 };
110
111 struct _test_t my_dev;
112
113 static int __init test_init(void) //模块初始化函数
114 {
115 int result = 0;
116 my_dev.cur_size = 0;
117 my_dev.major = 0;
118 my_dev.minor = 0;
119
120 if(my_dev.major){
121 my_dev.devno = MKDEV(my_dev.major, my_dev.minor);
122 result = register_chrdev_region(my_dev.devno, 1, "test new driver") ;
123 }else{
124 result = alloc_chrdev_region(&my_dev.devno, my_dev.minor, 1, "test alloc diver");
125 my_dev.major = MAJOR(my_dev.devno);
126 my_dev.minor = MINOR(my_dev.devno);
127 }
128
129 if(result < 0){
130 P_DEBUG("register devno errno!\n");
131 goto err0;
132 }
133
134 printk("major[%d] minor[%d]\n", my_dev.major, my_dev.minor);
135
136 cdev_init(&my_dev.test_cdev, &test_fops);
137 my_dev.test_cdev.owner = THIS_MODULE;
138 /*初始化等待队列头,注意函数调用的位置*/
139 init_waitqueue_head(&my_dev.test_queue);
140 init_waitqueue_head(&my_dev.read_queue);
141
142 result = cdev_add(&my_dev.test_cdev, my_dev.devno, 1);
143 if(result < 0){
144 P_DEBUG("cdev_add errno!\n");
145 goto err1;
146 }
147
148 printk("hello kernel\n");
149 return 0;
150
151 err1:
152 unregister_chrdev_region(my_dev.devno, 1);
153 err0:
154 return result;
155 }
。。。。。省略。。。。。
poll函数的实现同样需要使用等待队列,在这里没有把上节阻塞型IO代码注释掉,主要是想说明一个问题,它们两个的功能是不一样的,并不会冲突。后面会具体讲述。
上面的函数其实也就三部:
1定义并初始化等待队列头;
2实现test_poll;
3唤醒等待队列。
接下来先对照程序说一下poll函数的实现:
1)定义等待队列头:
poll_wait函数里面的操作需要用到等待队列,所以需要定义并初始化等待队列头。
2)test_poll的实现:
test_poll的实现有两个步骤:
2.1)调同poll_wait,将进程添加到指定的等待队列(注意,仅仅是添加,没有休眠)。
poll_wait的原型是:
unsigned int test_poll (struct file *filp, struct poll_table_struct *table)
注意:这里的两个参数都不是用户传给它的,全部都是有内核传的。可以这样说,poll没有做实际的什么操作,只是返回些信息给内核来操作。
自己总结:以上是驱动层的poll,应用层的poll是:
2poll实现功能
3poll操作举例
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <poll.h>
#include <fcntl.h>
#include <unistd.h>
#define MAX_BUFFER_SIZE 1024
#define IN_FILES 3
#define TIME_DELAY 60*5
#define MAX(a,b) ((a>b)?(a):(b))
int
main(
int
argc ,
char
**argv)
{
struct
pollfd fds[IN_FILES];
char
buf[MAX_BUFFER_SIZE];
int
i,res,real_read, maxfd;
fds[0].fd = 0;
if
((fds[1].fd=open(
"data1"
,O_RDONLY|O_NONBLOCK)) < 0)
{
fprintf
(stderr,
"open data1 error:%s"
,
strerror
(
errno
));
return
1;
}
if
((fds[2].fd=open(
"data2"
,O_RDONLY|O_NONBLOCK)) < 0)
{
fprintf
(stderr,
"open data2 error:%s"
,
strerror
(
errno
));
return
1;
}
for
(i = 0; i < IN_FILES; i++)
{
fds[i].events = POLLIN;//设备数据是否可读
}
while
(fds[0].events || fds[1].events || fds[2].events)
{
if
(poll(fds, IN_FILES, TIME_DELAY) <= 0)
{
printf
(
"Poll error\n"
);
return
1;
}
for
(i = 0; i< IN_FILES; i++)
{
if
(fds[i].revents)
{
memset
(buf, 0, MAX_BUFFER_SIZE);
real_read = read(fds[i].fd, buf, MAX_BUFFER_SIZE);
if
(real_read < 0)
{
if
(
errno
!= EAGAIN)
{
return
1;
}
}
else
if
(!real_read)
{
close(fds[i].fd);
fds[i].events = 0;
}
else
{
if
(i == 0)
{
if
((buf[0] ==
'q'
) || (buf[0] ==
'Q'
))
{
return
1;
}
}
else
{
buf[real_read] =
'\0'
;
printf
(
"%s"
, buf);
}
}
}
}
}
exit
(0);
}
来个代码来分析poll_wait究竟干了什么:
/*include/linux/poll.h */
31 typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);
32
33 typedef struct poll_table_struct { //poll_table_struct的原型
34 poll_queue_proc qproc;
35 } poll_table;
36
37 static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
38 {
39 if (p && wait_address)
40 p->qproc(filp, wait_address, p); //这里就断了线索
41 }
42
43 static inline void init_poll_funcptr(poll_table *pt, poll_queue_proc qproc)
44 {
45 pt->qproc = qproc;
46 }
47
48 struct poll_table_entry {
49 struct file *filp;
50 wait_queue_t wait;
51 wait_queue_head_t *wait_address;
52 };
。。。。。省略。。。。。
57 struct poll_wqueues {
58 poll_table pt;
59 struct poll_table_page *table;
60 struct task_struct *polling_task;
61 int triggered;
62 int error;
63 int inline_index;
64 struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
65 };
poll_wait执行了一个函数,但没找出函数是做什么的。在另外的文件我找到一点线索:
/*fs/select.c*/
85 struct poll_table_page {
86 struct poll_table_page * next;
87 struct poll_table_entry * entry;
88 struct poll_table_entry entries[0];
89 };
。。。。。。。。
198 /* Add a new entry */
199 static void __pollwait(struct file *filp, wait_queue_head_t *wait_address,
200 poll_table *p)
201 {
202 struct poll_wqueues *pwq = container_of(p, struct poll_wqueues, pt);
203 struct poll_table_entry *entry = poll_get_entry(pwq);
204 if (!entry)
205 return;
206 get_file(filp);
207 entry->filp = filp;
208 entry->wait_address = wait_address;
209 init_waitqueue_func_entry(&entry->wait, pollwake);
210 entry->wait.private = pwq;
211 add_wait_queue(wait_address, &entry->wait);
212 }
因为函数的传参和名字都差不多,我猜想内核是调用该函数的。
从上面的代码和《设备驱动程序》我得出来一下的结论:
1.应用层调用函数select,内核为了管理等待队列(有时候不止一个等待队列,因为select函数可以检测多个文件的状态),建立了一个poll_table_struct结构体(一个select系统调用对应一个结构体)。
2.poll_wait函数的调用,将三个参数传给了内核。内核中,通过结构体poll_table_struct找到另一个结构体poll_table_page,上面的代码可以看出来,这个结构体是一个维护多个poll_table_entry结构体的内存页链表,poll_wait函数的参数就是传到poll_table_entry结构体中。
3.再看一下poll_table_entry里面的成员,第一个成员srutct file是poll_wait的第一个参数,第二个成员就是定义了一个wait_queue_t的结构体,而这个结构体是正要添加到等待队列头中,也就是从poll_wait传来的第二个参数。
4.现在重头戏了,poll_wait的调用实际上调用了__pollwiat。看一下大概的操作:
4.1使用container_of函数,通过poll_table(即poll_table_struct)找到poll_wqueues,一看名字就猜到,它是存放等待队列的!poll_wqueues包含成员poll_table_page。
4.2通过传入的filp和等待队列头两个参数,新建一个poll_table_enter并添加到poll_table_page中。
2.2)对应设备的状态,返回相应的掩码。那就是说,如果设备可读,那就返回可读的掩码。
什么是掩码?有什么掩码?
掩码
含义
POLLIN
设备可读。
POLLRDNORM
数据可读。一般的,驱动可读,返回(POLLIN|POLLRDNORM),当然,只返回POLLIN也行,因为意思其实都可不多
POLLOUT
设备可写
POLLWRNORM
数据可写。一般的,驱动可写,返回(POLLOUT|POLLWRNORM),当然,只返回POLLOUT也行,因为意思其实都可不多
当然,还有其他的掩码,我这里就不意义介绍。
3)唤醒等待队列
其实一开始我也很奇怪为什么需要唤醒,毕竟poll_wait函数并不会导致休眠。为什么要唤醒?在哪里唤醒?
我上面的驱动函数,test_poll返回掩码,如果掩码为0,则表示设备不可读,这时,内核接到返回的掩码,知道设备不可读,此时select函数就会阻塞,进程休眠,等待有数据时被唤醒。所以,在写入数据后,需要唤醒等待队列头read_queue。此时设备可读了,就会再次调用test_poll函数,返回掩码POLLIN,select调用成功。
所以,这里得出两个结论:
1.test_poll并不会导致休眠,进程阻塞是系统调用select搞的鬼。
2.系统调用select的阻塞会导致test_poll被调用多次。
既然大概知道了函数怎么写的。那就验证一下程序吧。应用程序我就不贴了。在app目录下,直接来结果:
现象一:先写后读
[root: 1st]# insmod test.ko
major[253] minor[0]
hello kernel
[root: 1st]# mknod /dev/test c 253 0
[root: app]# ./monitor& //1.先后台运行检测程序monitor
<kernel>[test_poll]***maks[0]*** //2.在我还没写之前,test_poll被调度了两遍后阻塞
<kernel>[test_poll]***maks[0]***
[root: app]# ./app_write //3过了一段时间,我写入数据
<kernel>[test_write]write 10 bytes, cur_size:[10]
<kernel>[test_write]kbuf is [xiao bai]
<kernel>[test_poll]***maks[1]*** //4.test_poll再次被调用,掩码改变了!
<app>monitor:[device readable]
[root: app]# <kernel>[test_poll]***maks[1]***
<app>monitor:[device readable] //5select隔四秒就调用一遍,没有被阻塞
<kernel>[test_poll]***maks[1]***
<app>monitor:[device readable]
<kernel>[test_poll]***maks[1]***
<app>monitor:[device readable]
<kernel>[test_poll]***maks[1]***
<app>monitor:[device readable]
[root: app]# ./app_read //6我读数据
<kernel>[test_read]read data.....
<kernel>[test_read]read 10 bytes, cur_size:[0]
<app_read>[xiao bai]
[root: app]# <kernel>[test_poll]***maks[0]*** //7读完他又阻塞了。
现象二:先写后读
[root: app]# ./monitor& //1.先后台运行检测程序monitor
<kernel>[test_poll]***maks[0]*** //2.在我还没写之前,test_poll被调度了两遍后阻塞
<kernel>[test_poll]***maks[0]***
[root: app]# ./app_read& //3.再后台运行read
[root: app]# <kernel>[test_read]read data..... //4.它阻塞了,这里不关poll的原因,这是因为上节说的阻塞型IO
[root: app]# ./app_write //5.再写数据
<kernel>[test_write]write 10 bytes, cur_size:[10]
<kernel>[test_write]kbuf is [xiao bai]
<kernel>[test_poll]***maks[1]*** //select被唤醒,返回可读掩码
<kernel>[test_read]read 10 bytes, cur_size:[0] //test_read被唤醒,读取数据
<app>monitor:[device readable]
<app_read>[xiao bai]
[2] + Done ./app_read
[root: app]# <kernel>[test_poll]***maks[0]*** //没数据,select又阻塞了
注:上面的驱动程序使用了两个等待队列头,细心可以发现,其实只要一个等待队列头,就可以实现阻塞型IO和poll了。具体就不讲解了,程序在3rd_char_6/and,很简单的改动。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
三、poll同时检测可读和可写两个状态:
也很简单,直接上程序
/*3rd_char_6/2st/test.c*/
23 struct _test_t{
24 char kbuf[DEV_SIZE];
25 unsigned int major;
26 unsigned int minor;
27 unsigned int cur_size;
28 dev_t devno;
29 struct cdev test_cdev;
30 wait_queue_head_t test_queue;
31 wait_queue_head_t read_queue; //定义两个等待队列
32 wait_queue_head_t write_queue;
33 };
。。。。。。省略。。。。。。。
48 ssize_t test_read(struct file *filp, char __user *buf, size_t count, loff_t *offset)
49 {
50 int ret;
51 struct _test_t *dev = filp->private_data;
52
53 if(filp->f_flags & O_NONBLOCK)
54 return - EAGAIN;
55
56 P_DEBUG("read data.....\n");
57 if(wait_event_interruptible(dev->test_queue, dev->cur_size > 0))
58 return - ERESTARTSYS;
59
60 if (copy_to_user(buf, dev->kbuf, count)){
61 ret = - EFAULT;
62 }else{
63 ret = count;
64 dev->cur_size -= count;
65 P_DEBUG("read %d bytes, cur_size:[%d]\n", count, dev->cur_size);
66 wake_up_interruptible(&dev->write_queue);
67 }
68
69 return ret; //返回实际写入的字节数或错误号
70 }
71
72 ssize_t test_write(struct file *filp, const char __user *buf, size_t count, loff_t *offset)
73 {
74 int ret;
75 struct _test_t *dev = filp->private_data;
76
77 if(copy_from_user(dev->kbuf, buf, count)){
78 ret = - EFAULT;
79 }else{
80 ret = count;
81 dev->cur_size += count;
82 P_DEBUG("write %d bytes, cur_size:[%d]\n", count, dev->cur_size);
83 P_DEBUG("kbuf is [%s]\n", dev->kbuf);
84 wake_up_interruptible(&dev->test_queue);
85 wake_up_interruptible(&dev->read_queue);
86 }
87
88 return ret; //返回实际写入的字节数或错误号
89 }
90
91 unsigned int test_poll (struct file *filp, struct poll_table_struct *table)
92 {
93 struct _test_t *dev = filp->private_data;
94 unsigned int mask = 0;
95
96 poll_wait(filp, &dev->read_queue, table);
97 poll_wait(filp, &dev->write_queue, table);
98
99 if(dev->cur_size > 0) //设备可读
100 mask |= POLLIN;
101 if(dev->cur_size < DEV_SIZE) //设备可写
102 mask |= POLLOUT;
103
104 P_DEBUG("***************************\n");
105 return mask;
106 }
107
108 struct file_operations test_fops = {
109 .open = test_open,
110 .release = test_close,
111 .write = test_write,
112 .read = test_read,
113 .poll = test_poll,
114 };
115
116 struct _test_t my_dev;
117
118 static int __init test_init(void) //模块初始化函数
119 {
120 int result = 0;
121 my_dev.cur_size = 0;
122 my_dev.major = 0;
123 my_dev.minor = 0;
124
125 if(my_dev.major){
126 my_dev.devno = MKDEV(my_dev.major, my_dev.minor);
127 result = register_chrdev_region(my_dev.devno, 1, "test new driver") ;
128 }else{
129 result = alloc_chrdev_region(&my_dev.devno, my_dev.minor, 1, "test alloc diver");
130 my_dev.major = MAJOR(my_dev.devno);
131 my_dev.minor = MINOR(my_dev.devno);
132 }
133
134 if(result < 0){
135 P_DEBUG("register devno errno!\n");
136 goto err0;
137 }
138
139 printk("major[%d] minor[%d]\n", my_dev.major, my_dev.minor);
140
141 cdev_init(&my_dev.test_cdev, &test_fops);
142 my_dev.test_cdev.owner = THIS_MODULE;
143 /*初始化等待队列头,注意函数调用的位置*/
144 init_waitqueue_head(&my_dev.test_queue);
145 init_waitqueue_head(&my_dev.read_queue);
146 init_waitqueue_head(&my_dev.write_queue);
147
148 result = cdev_add(&my_dev.test_cdev, my_dev.devno, 1);
149 if(result < 0){
150 P_DEBUG("cdev_add errno!\n");
151 goto err1;
152 }
153
154 printk("hello kernel\n");
155 return 0;
156
157 err1:
158 unregister_chrdev_region(my_dev.devno, 1);
159 err0:
160 return result;
161 }
。。。。。。。省略。。。。。。。
验证一下:注意的是,这次的select并没有阻塞,原因很简单,要不就可读要不就可写,肯定有掩码返回,根本不用阻塞。
[root: app]# ./monitor& //1.执行建材程序
[root: app]# <kernel>[test_poll]***mask[4]***//2每个四秒调用一次select,没阻塞
<app>monitor:[device writeable]
<kernel>[test_poll]***mask[4]***
<app>monitor:[device writeable]
<kernel>[test_poll]***mask[4]***
<app>monitor:[device writeable]
[root: app]# ./app_write //3写入数据
<kernel>[test_write]write 10 bytes, cur_size:[10]
<kernel>[test_write]kbuf is [xiao bai]
[root: app]# <kernel>[test_poll]***mask[5]***
<app>monitor:[device readable] //4掩码改变,但没有阻塞
<app>monitor:[device writeable]
<kernel>[test_poll]***mask[5]***
<app>monitor:[device readable]
<app>monitor:[device writeable]
<kernel>[test_poll]***mask[5]***
<app>monitor:[device readable]
<app>monitor:[device writeable]
[root: app]# ./app_read //5读取数据
<kernel>[test_read]read data.....
<kernel>[test_read]read 10 bytes, cur_size:[0]
<app_read>[xiao bai]
[root: app]# <kernel>[test_poll]***mask[4]***
<app>monitor:[device writeable] //6掩码改变,但没有阻塞
<kernel>[test_poll]***mask[4]***
<app>monitor:[device writeable]
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
四、select的阻塞操作
上面说了select会造成休眠,接下来简单的谈谈。select里面会调用函数do_select。
贴上程序,并附上大致的运行顺序:
/*fs/select.c*/
365 int do_select(int n, fd_set_bits *fds, struct timespec *end_time)
366 {
367 ktime_t expire, *to = NULL;
368 struct poll_wqueues table; //1.这个就是前面说用来方等待队列的poll_wqueues
。。。。省略。。。。
380
381 poll_initwait(&table);
382 wait = &table.pt;
383 if (end_time && !end_time->tv_sec && !end_time->tv_nsec) {
384 wait = NULL;
385 timed_out = 1;
386 }
387
388 if (end_time && !timed_out)
389 slack = estimate_accuracy(end_time);
390
391 retval = 0;
392 for (;;) { //2.注意这个大循环,如果条件不成立休眠后,唤醒正在这个大循环里
393 unsigned long *rinp, *routp, *rexp, *inp, *outp, *exp;
394
395 inp = fds->in; outp = fds->out; exp = fds->ex;
396 rinp = fds->res_in; routp = fds->res_out; rexp = fds->res_ex;
397
398 for (i = 0; i < n; ++rinp, ++routp, ++rexp) {
。。。。。省略。。。。。
411 for (j = 0; j < __NFDBITS; ++j, ++i, bit <<= 1) {
412 int fput_needed;
413 if (i >= n)
414 break;
415 if (!(bit & all_bits))
416 continue;
417 file = fget_light(i, &fput_needed);
418 if (file) { //3.循环里面里边所有所有被检测的filp
419 f_op = file->f_op;
420 mask = DEFAULT_POLLMASK;
421 if (f_op && f_op->poll) //4.调用我们实现的poll函数,这也是poll被多次调用的原因,以为他在循环里面。
422 mask = (*f_op->poll)(file, retval ? NULL : wait);
423 fput_light(file, fput_needed);
424 if ((mask & POLLIN_SET) && (in & bit)) { //5.判断poll返回的掩码,只要掩码不是0,下面的起码有一个条件会实现。retval++。
425 res_in |= bit;
426 retval++;
427 }
428 if ((mask & POLLOUT_SET) && (out & bit)) {
429 res_out |= bit;
430 retval++;
431 }
432 if ((mask & POLLEX_SET) && (ex & bit)) {
433 res_ex |= bit;
434 retval++;
435 }
436 }
437 }
438 if (res_in)
439 *rinp = res_in;
440 if (res_out)
441 *routp = res_out;
442 if (res_ex)
443 *rexp = res_ex;
444 cond_resched();
445 }
446 wait = NULL;
447 if (retval || timed_out || signal_pending(current))//6.如果条件成立或者 延时或者被中断
448 break; //7.调用break跳出大循环,do_select调用完毕
449 if (table.error) {
450 retval = table.error;
451 break;
452 }
453
454 /*
455 * If this is the first loop and we have a timeout
456 * given, then we convert to ktime_t and set the to
457 * pointer to the expiry value.
458 */
459 if (end_time && !to) {
460 expire = timespec_to_ktime(*end_time);
461 to = &expire;
462 }
463 /*8如果上面的条件没有成立,走到这里,进程状态改变有TASK_INTERRUPTIBLE,并加入指定的等待队列头,让出CPU,进程休眠,等待唤醒*/
464 if (!poll_schedule_timeout(&table, TASK_INTERRUPTIBLE,
465 to, slack))
466 timed_out = 1;
467 }
468
469 poll_freewait(&table);
470
471 return retval;
472 }
上面只是想说明:poll只是做了一个判断工作,真正的阻塞在select中。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
五、总结:
今天内容有讲完了,讲了以下内容:
1、poll的实现:
1.1调用poll_wait
1.2返回掩码。
2.poll_wait的实现。
3.select中的阻塞。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
linux驱动之poll机制 按键测试
以下是实例
驱动程序
001
#include <linux/module.h>
002
#include <linux/kernel.h>
003
#include <linux/fs.h>
004
#include <linux/init.h>
005
#include <linux/delay.h>
006
#include <linux/poll.h>
007
#include <linux/irq.h>
008
#include <asm/irq.h>
009
#include <linux/interrupt.h>
010
#include <asm/uaccess.h>
011
#include <mach/regs-gpio.h>
012
#include <mach/hardware.h>
013
#include <linux/platform_device.h>
014
#include <linux/cdev.h>
015
#include <linux/miscdevice.h>
016
#include <linux/mm.h>
017
#include <asm/io.h> //ioremap()
018
019
volatile
unsigned
long
*gpfcon;
020
volatile
unsigned
long
*gpfdat;
021
022
static
DECLARE_WAIT_QUEUE_HEAD(button_waitq);
//声明等待队列
023
024
/* 中断事件标志, 中断服务程序将它置1,poll_key_read将它清0 */
025
static
volatile
int
ev_press = 0;
026
027
//引脚描述结构
028
struct
pin_desc{
029
unsigned
int
pin;
030
unsigned
int
key_val;
031
};
032
033
034
/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
035
/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
036
static
unsigned
char
key_val;
037
038
struct
pin_desc pins_desc[4] = {
039
{S3C2410_GPF0, 0x01},
040
{S3C2410_GPF1, 0x02},
041
{S3C2410_GPF2, 0x03},
042
{S3C2410_GPF4, 0x04},
043
};
044
045
046
static
unsigned poll_key_poll(
struct
file *file, poll_table *wait)
047
{
048
unsigned
int
mask = 0;
049
poll_wait(file, &button_waitq, wait);
// 不会立即休眠,将进程放入等待队列 button_waitq
050
051
if
(ev_press)
052
mask |= POLLIN | POLLRDNORM;
053
054
return
mask;
//如果没有按键按下,就返回 0 。就是系统调用 poll 的返回值
055
}
056
057
058
/*
059
* 确定按键值
060
*/
061
static
irqreturn_t buttons_irq(
int
irq,
void
*dev_id)
062
{
063
struct
pin_desc * pindesc = (
struct
pin_desc *)dev_id;
064
unsigned
int
pinval;
065
066
pinval = s3c2410_gpio_getpin(pindesc->pin);
067
068
if
(pinval)
069
{
070
/* 松开 */
071
key_val = 0x80 | pindesc->key_val;
072
}
073
else
074
{
075
/* 按下 */
076
key_val = pindesc->key_val;
077
}
078
079
ev_press = 1;
/* 表示中断发生了 */
080
wake_up_interruptible(&button_waitq);
/* 唤醒休眠的进程 */
081
082
083
return
IRQ_RETVAL(IRQ_HANDLED);
084
}
085
/*打开的时候注册中断*/
086
static
int
poll_key_open(
struct
inode *inode,
struct
file *file)
087
{
088
request_irq(IRQ_EINT0, buttons_irq, IRQ_TYPE_EDGE_BOTH,
"S1"
, &pins_desc[0]);
089
request_irq(IRQ_EINT1, buttons_irq, IRQ_TYPE_EDGE_BOTH,
"S2"
, &pins_desc[1]);
090
request_irq(IRQ_EINT2, buttons_irq, IRQ_TYPE_EDGE_BOTH,
"S3"
, &pins_desc[2]);
091
request_irq(IRQ_EINT4, buttons_irq, IRQ_TYPE_EDGE_BOTH,
"S4"
, &pins_desc[3]);
092
093
return
0;
094
}
095
096
ssize_t poll_key_read(
struct
file *file,
char
__user *buf,
size_t
size, loff_t *ppos)
097
{
098
if
(size !=
sizeof
(key_val))
099
return
-EINVAL;
100
101
/* 如果没有按键动作, 休眠 */
102
wait_event_interruptible(button_waitq, ev_press);
103
104
/* 如果有按键动作, 返回键值 */
105
copy_to_user(buf, &key_val,
sizeof
(key_val));
106
ev_press = 0;
107
108
return
1;
109
}
110
111
112
int
poll_key_close(
struct
inode *inode,
struct
file *file)
113
{
114
free_irq(IRQ_EINT0, &pins_desc[0]);
115
free_irq(IRQ_EINT1, &pins_desc[1]);
116
free_irq(IRQ_EINT2, &pins_desc[2]);
117
free_irq(IRQ_EINT4, &pins_desc[3]);
118
return
0;
119
}
120
121
122
static
struct
file_operations key_fops = {
123
.owner = THIS_MODULE,
/* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
124
.open = poll_key_open,
125
.read = poll_key_read,
126
.release = poll_key_close,
127
.poll = poll_key_poll,
128
};
129
130
131
int
major;
132
static
int
poll_key_init(
void
)
133
{
134
major = register_chrdev(0,
"mykey"
, &key_fops);
135
gpfcon = (
volatile
unsigned
long
*)ioremap(0x56000050, 16);
136
gpfdat = gpfcon + 1;
137
printk(
"This is my poll_key_driver Loaded ---===>>> \n"
);
138
return
0;
139
}
140
141
static
void
poll_key_exit(
void
)
142
{
143
unregister_chrdev(major,
"mykey"
);
144
printk(
"This is my pll_key_driver UN_Loaded ---===>>> \n"
);
145
iounmap(gpfcon);
146
}
147
148
149
module_init(poll_key_init);
150
151
module_exit(poll_key_exit);
152
153
MODULE_LICENSE(
"GPL"
);
测试程序
01
#include <sys/types.h>
02
#include <sys/stat.h>
03
#include <fcntl.h>
04
#include <stdio.h>
05
#include <poll.h>
06
07
int
main(
int
argc,
char
**argv)
08
{
09
int
fd;
10
unsigned
char
key_val;
11
int
ret;
12
13
struct
pollfd fds[1];
14
15
fd = open(
"/dev/buttons"
, O_RDWR);
16
if
(fd < 0)
17
{
18
printf
(
"can't open!\n"
);
19
}
20
21
fds[0].fd = fd;
22
fds[0].events = POLLIN;
23
24
while
(1)
25
{
26
ret = poll(fds, 1, 3000);
27
if
(ret == 0)
28
{
29
printf
(
"3S time out\n"
);
30
}
31
else
32
{
33
read(fd, &key_val, 1);
34
printf
(
"key_val = 0x%x\n"
, key_val);
35
}
36
}
37
38
return
0;
39
}
Makefile还是依旧。。。改下名字即可
测试
安装
[root@EmbedSky /]# insmod mykey.ko
This is my poll_key_driver Loaded ---===>>>
创建节点
[root@EmbedSky /]# cat /proc/devices
Character devices:
1 mem
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
29 fb
81 video4linux
89 i2c
90 mtd
116 alsa
128 ptm
136 pts
180 usb
188 ttyUSB
189 usb_device
204 tq2440_serial
252 mykey 我们的驱动
253 usb_endpoint
254 rtc
Block devices:
[root@EmbedSky /]# mknod /dev/buttons c 252 0
运行测试
[root@EmbedSky /]# ./pollkeytest key_val = 0x2 按键的反应
key_val = 0x82
key_val = 0x4
key_val = 0x84
key_val = 0x3
key_val = 0x83
key_val = 0x1
key_val = 0x1
key_val = 0x81
3S time out 不按键会时间溢出
3S time out
3S time out
^C
[root@EmbedSky /]#
- linux设备驱动:poll和sellct
- linux设备驱动:poll和sellct
- 转载_linux设备驱动:poll和sellct
- 【Linux开发】linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct (2010-12-27 21:32)
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- linux设备驱动归纳总结(三):6.poll和sellct
- Linux设备驱动模板:poll
- Linux设备驱动---poll函数
- (三)6 poll和sellct
- LINUX字符设备驱动2------POLL机制
- 黑马程序员.Net学习笔记 07篇.Winform练习
- linux启动时挂载rootfs的几种方式 .
- quartz的原理实现
- google ProtoBuf 开发者指南
- 项目管理十要素
- linux设备驱动:poll和sellct
- 基于google Zxing实现二维码、条形码扫描,仿微信二维码扫描效果
- java 经纬度解析地址地理位置信息
- 从开机加电到执行main函数之前的过程
- JVM体系结构
- Java的JDBC事务
- oracle event --- 10704
- 安全度量标准
- vs2010配置opencv