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函数里面的操作需要用到等待队列,所以需要定义并初始化等待队列头。


2test_poll的实现:

test_poll的实现有两个步骤:

2.1)调同poll_wait,将进程添加到指定的等待队列(注意,仅仅是添加,没有休眠)。

poll_wait的原型是:

unsigned int test_poll (struct file *filp, struct poll_table_struct *table)

注意:这里的两个参数都不是用户传给它的,全部都是有内核传的。可以这样说,poll没有做实际的什么操作,只是返回些信息给内核来操作


自己总结:以上是驱动层的poll,应用层的poll是:

poll()函数:这个函数是某些Unix系统提供的用于执行与select()函数同等功能的函数,下面是这个函数的声明:
#include <poll.h>
int poll(struct pollfd fds[], nfds_t nfds, int timeout);
参数说明:
fds:是一个struct pollfd结构类型的数组,用于存放需要检测其状态的Socket描述符;每当调用这个函数之后,系统不会清空这个数组,操作起来比较方便;特别是对于socket连接比较多的情况下,在一定程度上可以提高处理的效率;这一点与select()函数不同,调用select()函数之后,select()函数会清空它所检测的socket描述符集合,导致每次调用select()之前都必须把socket描述符重新加入到待检测的集合中;因此,select()函数适合于只检测一个socket描述符的情况,而poll()函数适合于大量socket描述符的情况;
nfds:nfds_t类型的参数,用于标记数组fds中的结构体元素的总数量;
timeout:是poll函数调用阻塞的时间,单位:毫秒;
返回值:
>0:数组fds中准备好读、写或出错状态的那些socket描述符的总数量;
==0:数组fds中没有任何socket描述符准备好读、写,或出错;此时poll超时,超时时间是timeout毫秒;换句话说,如果所检测的socket描述符上没有任何事件发生的话,那么poll()函数会阻塞timeout所指定的毫秒时间长度之后返回,如果timeout==0,那么poll() 函数立即返回而不阻塞,如果timeout==INFTIM,那么poll() 函数会一直阻塞下去,直到所检测的socket描述符上的感兴趣的事件发生是才返回,如果感兴趣的事件永远不发生,那么poll()就会永远阻塞下去;
-1: poll函数调用失败,同时会自动设置全局变量errno;

2poll实现功能

poll和select实现功能差不多,但poll效率高,以后要多用poll
poll()接受一个指向结构'struct pollfd'列表的指针,其中包括了你想测试的文件描述符和事件。事件由一个在结构中事件域的比特掩码确定。当前的结构在调用后将被填写并在事件发生后返回。在SVR4(可能更早的一些版本)中的 "poll.h"文件中包含了用于确定事件的一些宏定义。事件的等待时间精确到毫秒 (但令人困惑的是等待时间的类型却是int),当等待时间为0时,poll()函数立即返回,-1则使poll()一直挂起直到一个指定事件发生。下面是pollfd的结构。
struct pollfd {
int fd; /*文件描述符*/
short events; /* 等待的需要测试事件 */
short revents; /* 实际发生了的事件,也就是返回结果 */
};
与select()十分相似,当返回正值时,代表满足响应事件的文件描述符的个数,如果返回0则代表在规定时间内没有事件发生。如发现返回为负则应该立即查看 errno,因为这代表有错误发生。
如果没有事件发生,revents会被清空,所以你不必多此一举。
poll函数可用的测试值
常量
说明
POLLIN
普通或优先级带数据可读
POLLRDNORM
普通数据可读
POLLRDBAND
优先级带数据可读
POLLPRI
高优先级数据可读
POLLOUT
普通数据可写
POLLWRNORM
普通数据可写
POLLWRBAND
优先级带数据可写
POLLERR
发生错误
POLLHUP
发生挂起
POLLNVAL
描述字不是一个打开的文件
例如fds[0].events = POLLIN; /*将测试条件设置成普通或优先级带数据可读*/
然后 int pollresult = poll(fds,xx,xx); //这样就可以监听fds里面文件描述符了,当满足特定条件就返回,并将结果保存在revents中。

3poll操作举例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#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)
{
  structpollfd fds[IN_FILES];
  charbuf[MAX_BUFFER_SIZE];
  inti,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));
      return1;
    }
  if((fds[2].fd=open("data2",O_RDONLY|O_NONBLOCK)) < 0)
    {
      fprintf(stderr,"open data2 error:%s",strerror(errno));
      return1;
    }
  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");
     return1;
    }
      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)
           {
             return1;
           }
        }
         elseif (!real_read)
        {
         close(fds[i].fd);
         fds[i].events = 0;
        }
         else
        {
         if(i == 0)
           {
             if((buf[0] == 'q') || (buf[0] =='Q'))
            {
             return1;
            }
           }
         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 filepoll_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函数,返回掩码POLLINselect调用成功

所以,这里得出两个结论

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又阻塞了


注:上面的驱动程序使用了两个等待队列头,细心可以发现,其实只要一个等待队列头,就可以实现阻塞型IOpoll了。具体就不讲解了,程序在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


五、总结:


今天内容有讲完了,讲了以下内容:

1poll的实现:

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 
019volatile unsigned long *gpfcon;
020volatile unsigned long *gpfdat;
021 
022static DECLARE_WAIT_QUEUE_HEAD(button_waitq);    //声明等待队列
023 
024/* 中断事件标志, 中断服务程序将它置1,poll_key_read将它清0 */
025static volatile int ev_press = 0;
026 
027//引脚描述结构
028struct pin_desc{
029    unsigned int pin;
030    unsigned int key_val;
031};
032 
033 
034/* 键值: 按下时, 0x01, 0x02, 0x03, 0x04 */
035/* 键值: 松开时, 0x81, 0x82, 0x83, 0x84 */
036static unsigned char key_val;
037 
038struct pin_desc pins_desc[4] = {
039    {S3C2410_GPF0, 0x01},
040    {S3C2410_GPF1, 0x02},
041    {S3C2410_GPF2, 0x03},
042    {S3C2410_GPF4, 0x04},
043};
044 
045 
046static unsigned poll_key_poll(structfile *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    returnmask;   //如果没有按键按下,就返回 0 。就是系统调用 poll 的返回值
055}
056 
057 
058/*
059  * 确定按键值
060  */
061static irqreturn_t buttons_irq(int irq, void *dev_id)
062{
063    structpin_desc * pindesc = (structpin_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    returnIRQ_RETVAL(IRQ_HANDLED);
084}
085/*打开的时候注册中断*/
086static int poll_key_open(structinode *inode, structfile *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    return0;
094}
095 
096ssize_t poll_key_read(structfile *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    return1;
109}
110 
111 
112int poll_key_close(structinode *inode, structfile *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    return0;
119}
120 
121 
122static 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 
131int major;
132static int poll_key_init(void)
133{
134    major = register_chrdev(0,"mykey", &key_fops);
135    gpfcon = (volatileunsigned long *)ioremap(0x56000050, 16);
136    gpfdat = gpfcon + 1;
137    printk("This is my poll_key_driver Loaded  ---===>>>  \n");
138    return0;
139}
140 
141static 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 
149module_init(poll_key_init);
150 
151module_exit(poll_key_exit);
152 
153MODULE_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 
07int main(intargc, char **argv)
08{
09    intfd;
10    unsigned char key_val;
11    intret;
12 
13    structpollfd 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    return0;
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 /]#