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

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_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

源代码: 3rd_char_6.rar

阅读(1) | 评论(0) | 转发(0) |

0

上一篇:嵌入式C编程经验 之 全局变量猛于虎

下一篇:SPI读写总结

相关热门文章

  • SHTML是什么_SSI有什么用...
  • shell中字符串操作
  • 卡尔曼滤波的原理说明...
  • 关于java中的“错误:找不到或...
  • shell中的特殊字符

热门推荐

    -->

    给主人留下些什么吧!~~

    评论热议

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

    时间: 2024-07-31 14:34:58

    linux设备驱动归纳总结(三):6.poll和sellct的相关文章

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

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-61749.html linux设备驱动归纳总结(三):6.poll和sellct xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 接下来会讲系统调用select在驱动中的实现,如果对系统调用select不太懂的话,建议先看书补习一下. xxxxxxxxxxxxxxxxxxxx

    linux设备驱动归纳总结(三):7.异步通知fasync【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-62725.html linux设备驱动归纳总结(三):7.异步通知fasync xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 异步通知fasync是应用于系统调用signal和sigaction函数,下面我会使用signal函数.简单的说,signal函数就是让一个信号与与

    linux设备驱动归纳总结(三):2.字符型设备的操作open、close、read、write【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-59417.html linux设备驱动归纳总结(三):2.字符型设备的操作open.close.read.write 一.文件操作结构体file_operations 继续上次没讲完的问题,文件操作结构体到底是什么东西,为什么我注册了设备之后什么现象都没有?可以验证文件操作结构体的内容. file_operations是一个函数指针的集合,用于存放我们定义的用于操作设备的函数的指针,如果我们不定义,

    linux设备驱动归纳总结(三):5.阻塞型IO实现【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-60025.html linux设备驱动归纳总结(三):5.阻塞型IO实现 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 一.休眠简介: 进程休眠,简单的说就是正在运行的进程让出CPU.休眠的进程会被内核搁置在在一边,只有当内核再次把休眠的进程唤醒,进程才会会重新在CPU运行

    linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现【转】

    本文转自自:http://blog.chinaunix.net/uid-25014876-id-59418.html linux设备驱动归纳总结(三):3.设备驱动面向对象思想和lseek的实现 一.结构体struct file和struct inode 在之前写的函数,全部是定义了一些零散的全局变量.有没有办法整合成到一个结构体当中?这样的话,看起来和用起来都比较方便.接下来就要说这方面的问题. 不过先要介绍一下除了fops以外的两个比较重要的结构体: 1)struct file 在内核中,f

    linux设备驱动归纳总结

    前言: (总结已经基本写完,这段时间我会从新排版和修正.错误总会有的,望能指正!) 前段时间学习了嵌入式驱动,趁着没开始找工作,这段时间我会每天抽出时间来复习. 我的总结是根据学习时的笔记(李杨老师授课).<linux内核设计与实现>第三版.<linux设备驱动程序>第三版和<linux设备驱动开发详解>第一版来归纳的.文章中涉及一些自己的想法,并不能保证所说的一定正确. 我也是一位linux初学者,在这里发博也是想跟大家分享技术,同时也希望别人能够指正错误. 我把一些

    linux设备驱动归纳总结(七):1.时间管理与内核延时【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-100005.html linux设备驱动归纳总结(七):1.时间管理与内核延时 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这节将介绍一些很枯燥的内核,大体是内核中时间的概念和内核延时的使用,并没有源代码. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

    linux设备驱动归纳总结(四):5.SMP下的竞态和并发

    linux设备驱动归纳总结(四):5.多处理器下的竞态和并发 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这节将在上一节的基础上介绍支持多处理器和内核抢占的内核如何避免并发.除了内核抢占和中断外,由于多处理起的缘故,它可以做到多个程序同时执行.所以,进程除了要防自己的处理器外,还要防别的处理器,这个就是这节要介绍的内容. xxxxxxxxxxxxxxxxxxxxxxx

    linux设备驱动归纳总结(九):1.platform总线的设备和驱动【转】

    本文转载自:http://blog.chinaunix.net/uid-25014876-id-111745.html linux设备驱动归纳总结(九):1.platform总线的设备和驱动 xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 这一节可以理解是第八章的延伸,从这节开始介绍platform设备驱动. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx