您的位置:首页 > 其它

LDD3源码分析之llseek分析

2014-12-18 16:24 260 查看
本博客转载于:/article/1358166.html
[作者:刘昊昱]

本文分析LDD3第6章的llseek函数。


一、用户空间的lseek函数

要理解驱动中llseek函数的实现,必须先清楚对应的用户空间中lseek函数的用法,lseek函数函数原型如下:

[cpp]viewplaincopyoff_tlseek(intfd,off_toffset,intwhence);
第一个参数fd是要操作的文件描述符。

第二个参数指定文件操作指针的偏移量。注意,文件的读和写使用的是同一个文件操作指针。

第三个参数指定移动文件操作指针的参考点。这个参数通常取值为以下宏:

SEEK_SET:表示相对文件起始位置。

SEEK_CUR:表示相对文件操作指针当前位置。

SEEK_END:表示相对文件结束位置。

下面先来看一个用户空间测试程序llseek_test.c的实现,这个程序用来测试scull的定位功能,其代码如下:

[cpp]viewplaincopy1#include<stdio.h>
2#include<unistd.h>
3#include<fcntl.h>
4#include<string.h>
5#include<sys/types.h>
6#include<sys/stat.h>
7
8#defineBUF_SIZE50
9#defineDEVICE_FILE"/dev/scull"
10
11intmain(intargc,char*argv[])
12{
13intfd;
14intnum;
15charbuf[BUF_SIZE];
16
17fd=open(DEVICE_FILE,O_RDWR);
18if(fd<0)
19{
20printf("openscullerror!\n");
21return-1;
22}
23
24memset(buf,0,BUF_SIZE);
25num=read(fd,buf,BUF_SIZE);
26buf[num]=0;
27printf("%s\n",buf);
28
29lseek(fd,2,SEEK_SET);
30write(fd,"aa",2);
31num=read(fd,buf,BUF_SIZE);
32buf[num]=0;
33printf("%s\n",buf);
34
35lseek(fd,2,SEEK_SET);
36num=read(fd,buf,BUF_SIZE);
37buf[num]=0;
38printf("%s\n",buf);
39
40lseek(fd,0,SEEK_SET);
41lseek(fd,2,SEEK_CUR);
42num=read(fd,buf,BUF_SIZE);
43buf[num]=0;
44printf("%s\n",buf);
45
46lseek(fd,0,SEEK_SET);
47lseek(fd,0,SEEK_END);
48memset(buf,0,BUF_SIZE);
49printf("readreturnvalueis%d.\n",read(fd,buf,BUF_SIZE));
50
51return0;
52}

这个程序很简单,主要关注一下lseek函数是怎样移动文件操作指针的。

第29行,使用SEEK_SET宏,将文件操作指针移动到文件起始位置加上2个字节处。

第30行,写入两个字符’a’。

第41行,使用SEEK_CUR宏,将文件操作指针移动到文件操作指针当前位置加上2个字节处。

第47行,使用SEEK_END宏,将文件操作指针移动到文件结束处。

第49行,打印read的返回值,当文件操作指针在文件结束处时,read返回0。

下图是使用llseek_test测试scull设备的定位功能的过程:




这里需要说明的一点是,从上面的输出信息可以看出,对文件的read和write操作使用的是同一个文件操作指针。


二、驱动程序中llseek函数的实现

用户空间的lseek函数的定位功能在驱动程序中是由llseek函数实现的。注意,要完成对文件的定位操作,还需要read、write函数的配合,读写完成后必须更新文件操作指针位置。

即使驱动程序中没有实现llseek函数,有某些情况下,设备也是可以完成定位操作的,内核通过修改filp->f_pos来执行定位,filp->f_pos是文件的当前读写位置。但是,如果定位操作需要涉及设备的物理操作,就必须实现llseek函数了。scull设备的llseek函数代码如下:

[cpp]viewplaincopy523loff_tscull_llseek(structfile*filp,loff_toff,intwhence)
524{
525structscull_dev*dev=filp->private_data;
526loff_tnewpos;
527
528switch(whence){
529case0:/*SEEK_SET*/
530newpos=off;
531break;
532
533case1:/*SEEK_CUR*/
534newpos=filp->f_pos+off;
535break;
536
537case2:/*SEEK_END*/
538newpos=dev->size+off;
539break;
540
541default:/*can'thappen*/
542return-EINVAL;
543}
544if(newpos<0)return-EINVAL;
545filp->f_pos=newpos;
546returnnewpos;
547}

这里唯一与设备相关的操作就是第538行,取得设备文件的大小。同时,我们在前面的文章中分析过,scull的read和write函数读写文件后,总是更新文件操作指针的位置,定位功能需要llseek与read、write的配合。

对于某些设备文件来说,定位功能是没有意义的,例如键盘。对于这些设备,我们不能简单地不实现llseek函数,因为默认方法是允许通过filp->f_pos定位的。我们应该在我们的open函数中调用nonseekable_open,通知内核设备不支持llseek。该函数的函数原型如下:

[cpp]viewplaincopyintnonseekable_open(structinode*inode;structfile*filp);

另外,为完整起见,我们还应该将file_operations结构中的llseek方法设置为特殊的辅助函数no_llseek。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: