现在的位置: 首页 > 综合 > 正文

linux设备驱动归纳总结(三):6.poll和sellct

2013年12月12日 ⁄ 综合 ⁄ 共 5156字 ⁄ 字号 评论关闭

linux设备驱动归纳总结(三):6.pollsellct

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_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。看一下大概的操作:

抱歉!评论已关闭.