日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux 设备驱动总结,linux设备驱动归纳总结(三):3面向对象思想和lseek

發(fā)布時(shí)間:2025/3/15 linux 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux 设备驱动总结,linux设备驱动归纳总结(三):3面向对象思想和lseek 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

linux設(shè)備驅(qū)動(dòng)歸納總結(jié)(三):3.設(shè)備驅(qū)動(dòng)面向?qū)ο笏枷牒蚻seek的實(shí)現(xiàn)

一、結(jié)構(gòu)體struct

file和struct inode

在之前寫的函數(shù),全部是定義了一些零散的全局變量。有沒有辦法整合成到一個(gè)結(jié)構(gòu)體當(dāng)中?這樣的話,看起來和用起來都比較方便。接下來就要說這方面的問題。

不過先要介紹一下除了fops以外的兩個(gè)比較重要的結(jié)構(gòu)體:

1)struct

file

在內(nèi)核中,file結(jié)構(gòu)體是用來維護(hù)打開的文件的。每打開一次文件,內(nèi)核空間里就

會(huì)多增加一個(gè)file來維護(hù),當(dāng)文件關(guān)閉是釋放。

所以,在內(nèi)核中可以存在同一個(gè)文件的多個(gè)file,因?yàn)樵撐募粦?yīng)用程序打開被打

開。

在struct

file中有幾個(gè)重要的成員:

1)loff_tf_pos;

這是用來記錄文件的偏移量。在應(yīng)用程序中,打開文件時(shí)偏移量為0,每次的讀寫操作都會(huì)使偏移量增加。

從這個(gè)原因可以看出為什么每打開一次文件就新建一個(gè)file結(jié)構(gòu)體了。不然的話,每個(gè)打開文件的讀寫操作都修改同一個(gè)偏移量,那讀寫豈不是亂套了嗎?

2)void*private_data;

這是空類型的指針可以用于存放任何數(shù)據(jù),我會(huì)用這個(gè)指針來存放待會(huì)要定義的結(jié)構(gòu)體指針。

回想一下,文件操作結(jié)構(gòu)體fops中所有的函數(shù)成員里面都有一個(gè)參數(shù)是file結(jié)構(gòu)體,所以每個(gè)函數(shù)都可以在file->private_data中拿到我自己定義的結(jié)構(gòu)體了。

3)struct file_operations *fops;

打開文件后,內(nèi)核會(huì)把fops存放在這里,以后的操作就在這里在這里找函數(shù)了。

2)struct

inode

這個(gè)結(jié)構(gòu)體是用來保存一個(gè)文件的基本信息的結(jié)構(gòu)體,即使打開多個(gè)相同的文件,也只會(huì)有一個(gè)對(duì)應(yīng)的inode。

它也有兩個(gè)常用的成員:

1)dev_t i_rdev;

這里存放著這個(gè)文件的設(shè)備號(hào)。

2)struct cdev *i_cdev;

這個(gè)結(jié)構(gòu)體很熟悉吧,這就是注冊(cè)設(shè)備時(shí)用的cdev就存在這。這個(gè)結(jié)構(gòu)體的用處現(xiàn)在我還不好說,待會(huì)看程序就知道了。

二、面向?qū)ο蟮乃枷?/p>

接下來就封裝一下之前程序的數(shù)據(jù)類型吧:

18 struct _test_t{

19 char

kbuf[DEV_SIZE];//這里存放數(shù)據(jù)

20 unsigned int

major;//這里存放主設(shè)備號(hào)

21 unsigned int

minor;//這里存放次設(shè)備號(hào)

22 unsigned int

cur_size;//這里存放當(dāng)前的kbuf的大小

23 dev_t devno;//這里存放設(shè)備號(hào)

24 struct cdev

test_cdev;//這里存放cdev結(jié)構(gòu)體

25 };

定義了這樣的一個(gè)結(jié)構(gòu)體后,在操作函數(shù)中怎么拿到這個(gè)結(jié)構(gòu)體的指針呢?

先來個(gè)函數(shù):

#define container_of(ptr, type,

member) ({\

const typeof( ((type *)0)->member

) *__mptr = (ptr);\

(type *)( (char *)__mptr -

offsetof(type,member) );})

使用:

已知一個(gè)結(jié)構(gòu)體里面一個(gè)成員的指針ptr,同時(shí),這個(gè)成員也是另外一個(gè)結(jié)構(gòu)體類型中的一個(gè)成員,這個(gè)結(jié)構(gòu)體的類型是type,而這個(gè)成員以member這個(gè)名字命名。就可以通過這個(gè)函數(shù)找到指向類型是type的結(jié)構(gòu)體的指針。

返回值:

返回值就是指向type結(jié)構(gòu)體類型的數(shù)據(jù)的指針。

如:現(xiàn)在定義這樣的兩個(gè)結(jié)構(gòu)體:

struct A {

int *xiaobai_a;

};

struct B {

int xiaobai_b;

};

struct A a;

在遙遠(yuǎn)的另一處有這樣的定義:struct

B b;

并且,a.xiaobai_a = &b.xiaobai_b;

這樣,在不知道b只知道a的情況下也可以找到b的位置:

struct B *bb =

container_of(a.xiaobai_a, struct B, xiaobai_b);

估計(jì)被上面的解釋說暈了吧。我還是舉個(gè)例比較方便:

雖然一個(gè)函數(shù)不值得說這么久,但是我覺得這種思想很不錯(cuò),內(nèi)核中很多時(shí)候都用到這個(gè)函數(shù),如在內(nèi)核鏈表中。

來個(gè)邪惡的例子名字——老板與小秘:

老板他請(qǐng)了個(gè)年輕的小秘,他就跟客戶說:“我電話號(hào)碼經(jīng)常換,你記著我小秘的電話,想找我嘛,找我小秘就可以了!”

于是,客戶想找老板了,就打通小秘的電話,說:“我知道你是秘書小紅,我想找你老板小黑,麻煩給他的電話號(hào)碼我。”

這樣,客戶就拿到了老板最新的電話號(hào)碼了。

想象老板和客戶是個(gè)結(jié)構(gòu)體,秘書和他的電話號(hào)碼是個(gè)各自成員,電話號(hào)碼想象成指針:

老板的電話 =

container_of(秘書的電話, 老板,小秘)

說了半天還沒進(jìn)入正題,這個(gè)函數(shù)用在哪里呢?誰當(dāng)小秘呢?

就是那個(gè)說了半天都不知道能做什么還經(jīng)常出現(xiàn)的struct

cdev!

而我把cdev添加到了我自己建的結(jié)構(gòu)體struct

_test_t中,所喲struct

_test_t就是老板!

而struct

inode就是客戶了,因?yàn)樗某蓡T里面有小秘的電話號(hào)碼:struct

cdev *i_cdev;

所以,如果想得到_test_t,只要調(diào)用這個(gè)函數(shù)就行了。

下面看一下改良后的open函數(shù)

27 int test_open(struct inode *node,

struct file *filp)

28 {

29 struct _test_t *dev;

30 dev =

container_of(node->i_cdev, struct _test_t, test_cdev);

31 filp->private_data

= dev;

32 return 0;

33 }

上面還有一句,將獲得的結(jié)構(gòu)體指針存放到filp的private_data中。

這是因?yàn)?#xff0c;struct

file_operations中的每個(gè)函數(shù)的第一個(gè)參數(shù)就是struct

file,只要有file,每個(gè)函數(shù)都可以從private_data中得到數(shù)據(jù)了。相反,struct

inode這個(gè)參數(shù)并不是file_operations中所有的函數(shù)都有。

下面貼上部分代碼:1st/test.c

18 struct _test_t{

19 char kbuf[DEV_SIZE];

20 unsigned int major;

21 unsigned int minor;

22 unsigned int cur_size;

23 dev_t devno;

24 struct cdev test_cdev;

25 };

26

27 int test_open(struct inode *node,

struct file *filp)

28

{/*open操作需要給把拿到的結(jié)構(gòu)體指針賦值給private_data*/

29 struct _test_t *dev;

30 dev =

container_of(node->i_cdev, struct _test_t, test_cdev);

31 filp->private_data = dev;

32 return 0;

33 }

34

35 int test_close(struct inode

*node, struct file *filp)

36 {

37 return 0;

38 }

39

40 ssize_t test_read(struct file

*filp, char __user *buf, size_t count, loff_t *offset)

41 {

42 int ret;

43 struct _test_t *dev =

filp->private_data;

44

45 if(!dev->cur_size){

46 return 0;

47 }

48

49 if (copy_to_user(buf,

dev->kbuf, count)){

50 ret = - EFAULT;

51

}else{/*read函數(shù)成功讀取后要修改cur_size*/

52 ret = count;

53 dev->cur_size -=

count;

54 }

55 P_DEBUG("cur_size:[%d]\n",

dev->cur_size);

56

57 return ret;

58 }

59

60 ssize_t test_write(struct file

*filp, const char __user *buf, size_t count, loff_t *offset)

61 {

62 int ret;

63 struct _test_t *dev =

filp->private_data;

64

65 if(copy_from_user(dev->kbuf,

buf, count)){

66 ret = - EFAULT;

67

}else{/*write函數(shù)成功寫入后也要修改cur_size*/

68 ret = count;

69 dev->cur_size +=

count;

70 P_DEBUG("kbuf is

[%s]\n", dev->kbuf);

71

P_DEBUG("cur_size:[%d]\n", dev->cur_size);

72 }

73

74 return ret;

//返回實(shí)際寫入的字節(jié)數(shù)或錯(cuò)誤號(hào)

75 }

上面的程序其實(shí)就多了比上一個(gè)程序多了三步:

1)封裝了一個(gè)結(jié)構(gòu)體。

2)open函數(shù)要獲得結(jié)構(gòu)體并存放到private_data中。

3)read和write函數(shù)成功后要更新cur_size這個(gè)值。

這樣,一個(gè)像樣點(diǎn)的程序出來了,寫個(gè)應(yīng)用程序驗(yàn)證一下:

1 #include

2 #include

3 #include

4 #include

5

6 int main(void)

7 {

8 char buf[20];

9 int fd;

10 fd = open("/dev/test",

O_RDWR);

11 if(fd < 0)

12 {

13 perror("open");

14 return -1;

15 }

16

17 read(fd, buf, 10);

18 printf("buf

is [%s]\n", buf);

19

20 write(fd, "xiao bai",

10);

21

22 read(fd, buf, 10);

23 printf("buf

is [%s]\n", buf);

24

25 close(fd);

26 return 0;

27 }

運(yùn)行一下:

[root: 1st]# insmod test.ko

major[253] minor[0]

hello kernel

[root: 1st]# mknod /dev/test c 253 0

[root: 1st]# ./app

buf is

[]//第一次讀取時(shí)cur_size==0,沒數(shù)據(jù)就會(huì)返回

[test_write]kbuf is

[xiao bai]//成功寫入

[test_write]cur_size:[10]//更新cur_size

[test_read]cur_size:[0]//read讀取成功,跟新cur_size

buf is [xiao

bai]//應(yīng)用程序返回讀到的內(nèi)容

[root: 1st]#

三、read、write的改進(jìn)

上面的函數(shù)還是不完善的,想象一下,平時(shí)的read、write函數(shù)會(huì)增加偏移量,但上面的函數(shù)是不會(huì)的。這是因?yàn)檫€有一個(gè)參數(shù)我沒用上,就是"loff_t

offset"。

"loff_t

offset"這個(gè)參數(shù)是內(nèi)核在調(diào)用函數(shù)時(shí),從"struct

file"的成員"f_ops"拿到指針并當(dāng)作參數(shù)傳入。這樣的做法讓用戶不用再從"struct

file"提取成員,直接拿參數(shù)用就行了!

通過這個(gè)參數(shù),我們就可以改進(jìn)并且實(shí)現(xiàn)三個(gè)函數(shù):

1test_read:當(dāng)應(yīng)用程序調(diào)用read時(shí)內(nèi)核會(huì)調(diào)用test_read。讀取數(shù)據(jù)的同時(shí),偏移量會(huì)增加。

2test_write:當(dāng)應(yīng)用程序調(diào)用write時(shí)內(nèi)核會(huì)調(diào)用test_write。寫入數(shù)據(jù)的同時(shí),偏移量也會(huì)增加。

3test_llseek:這是跟應(yīng)用程序的lseek對(duì)應(yīng)的,用來修改偏移量的位置。

有了上面的三個(gè)函數(shù)的功能,這樣才算是個(gè)像樣的函數(shù)!

先改進(jìn)一下read、write函數(shù)

40 ssize_t test_read(struct file

*filp, char __user *buf, size_t count, loff_t *offset)

41 {

42 int ret;

43 struct _test_t *dev =

filp->private_data;

44

45 if(*offset >=

DEV_SIZE){//如果偏移量已經(jīng)超過了數(shù)組的容量

46 return count ? - ENXIO :

0; //count為0則返回0,表示讀取0個(gè)數(shù)據(jù)成功

47 }

//count不為0則分會(huì)錯(cuò)誤號(hào),地址越界

48 if(*offset + count >

DEV_SIZE){ //如果讀取字節(jié)數(shù)超過了最大偏移量

49 count = DEV_SIZE -

*offset; //則減少讀取字節(jié)數(shù)。

50 }

51 /*copy_to_user的參數(shù)也要改一下*/

52 if (copy_to_user(buf,

dev->kbuf + *offset, count)){

53 ret = - EFAULT;

54 }else{

55 ret = count;

56 dev->cur_size -=

count; //讀取后數(shù)組的字節(jié)數(shù)減少

57 *offset

+= count; //偏移量增加

58 P_DEBUG("read %d

bytes, cur_size:[%d]\n", count,dev->cur_size);

59 }

60

61 return ret;

//返回實(shí)際寫入的字節(jié)數(shù)或錯(cuò)誤號(hào)

62 }

63

64 ssize_t test_write(struct file

*filp, const char __user *buf, size_t count, loff_t *offset)

65 {

66 int ret;

67 struct _test_t *dev =

filp->private_data;

68 /*copy_from_user的參數(shù)也要改一下*/

69 if(*offset >=

DEV_SIZE){//如果偏移量已經(jīng)超過了數(shù)組的容量

70 return count ? - ENXIO :

0; //count為0則返回0,表示讀取0個(gè)數(shù)據(jù)成功

71 }

//count不為0則分會(huì)錯(cuò)誤號(hào),地址越界

72 if(*offset + count >

DEV_SIZE){ //如果讀取字節(jié)數(shù)超過了最大偏移量

73 count = DEV_SIZE -

*offset; //則減少讀取字節(jié)數(shù)。

74 }

75

76 if(copy_from_user(dev->kbuf,

buf, count)){

77 ret = - EFAULT;

78 }else{

79 ret = count;

80 dev->cur_size +=

count; //寫入后數(shù)組的字節(jié)數(shù)增加

81 *offset

+= 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 }

85

86 return ret;

//返回實(shí)際寫入的字節(jié)數(shù)或錯(cuò)誤號(hào)

87 }

話說得好,越是需要檢測出錯(cuò),代碼就會(huì)幾何級(jí)增加,如果不想看這么多代碼,把這兩個(gè)函數(shù)前面的兩個(gè)if(45-50、69-74)都刪掉!反正寫應(yīng)用程序的時(shí)候小心翼翼一點(diǎn)就好了。這個(gè)程序只是為了驗(yàn)證"offset"的作用。

再來個(gè)小心翼翼的應(yīng)用程序:

1 #include

2 #include

3 #include

4 #include

5

6 int main(void)

7 {

8 char buf[20];

9 int fd;

10 fd = open("/dev/test",

O_RDWR);

11 if(fd < 0)

12 {

13 perror("open");

14 return -1;

15 }

16

17 write(fd, "xiao bai",

10);

18

19 read(fd, buf, 10);

20 printf("buf

is [%s]\n", buf);

21

22 close(fd);

23 return 0;

24 }

驗(yàn)證一下:

[root: 2nd]# insmod test.ko

major[253] minor[0]

hello kernel

[root: 2nd]# mknod /dev/test c 253 0

[root: 2nd]# ./app

[test_write]write 10

bytes, cur_size:[10]//寫入

[test_write]kbuf is

[xiao bai]

[test_read]read 10

bytes, cur_size:[0]//但讀不出,因?yàn)槠屏吭黾?/p>

buf is []

上面的read函數(shù)根本讀不出數(shù)據(jù),這是因?yàn)槠屏吭黾恿恕_@個(gè)時(shí)候需要一個(gè)函數(shù)來把偏移量移到開頭,lseek函數(shù)就用上場了。下面就講一下。

四、lseek函數(shù)的實(shí)現(xiàn)

應(yīng)用層的函數(shù)lseek函數(shù)對(duì)應(yīng)驅(qū)動(dòng)的函數(shù)是llseek(為什么多了一個(gè)l我也想不懂)。

內(nèi)核驅(qū)動(dòng):loff_t (*llseek)

(struct file * filp, loff_t offset, int whence);

對(duì)應(yīng)應(yīng)用層:off_t lseek(int

fd, off_t offset, int whence);

使用:

一看參數(shù)就知道,這兩個(gè)函數(shù)的第二和第三個(gè)參數(shù)就是對(duì)應(yīng)的,當(dāng)應(yīng)用層調(diào)用函數(shù)時(shí),對(duì)應(yīng)的參數(shù)就會(huì)讓內(nèi)核傳給驅(qū)動(dòng)的函數(shù)llseek。

參數(shù):

offset:一看這個(gè)參數(shù)不是指針,就知道和read、write的參數(shù)不一樣。這是應(yīng)用層傳來的參數(shù),并不是"struct

file"的偏移量"f_ops"。

whence:這個(gè)也跟應(yīng)用層的參數(shù)一樣,指定從哪個(gè)位置開始偏移。

從開頭位置:#define

SEEK_SET0

從當(dāng)前位置:#define

SEEK_CUR1

從文件末端:#define

SEEK_END2

返回值:成功返回當(dāng)前的更新的偏移量,失敗返回錯(cuò)誤號(hào),而應(yīng)用層會(huì)返回-1。

下面來個(gè)程序:/3rd_char/3rd_char_3/3rd/test.c

/*test_llseek*/

89 loff_t test_llseek (struct file

*filp, loff_t offset, int whence)

90 {

91 loff_t new_pos;

//新偏移量

92 loff_t old_pos = filp->f_pos;

//舊偏移量

93

94 switch(whence){

95 case SEEK_SET:

96 new_pos = offset;

97 break;

98 case SEEK_CUR:

99 new_pos = old_pos +

offset;

100 break;

101 case SEEK_END:

102 new_pos = DEV_SIZE +

offset;

103 break;

104 default:

105 P_DEBUG("unknow

whence\n");

106 return - EINVAL;

107 }

108

109 if(new_pos < 0 || new_pos

> DEV_SIZE){ //如果偏移量越界,返回錯(cuò)誤號(hào)

110 P_DEBUG("f_pos

failed\n");

111 return - EINVAL;

112 }

113

114 filp->f_pos = new_pos;

115 return

new_pos;//正確返回新的偏移量

116 }

再來個(gè)應(yīng)用程序:/3rd_char/3rd_char_3/3rd/app.c

1 #include

2 #include

3 #include

4 #include

5

6 int main(void)

7 {

8 char buf[20];

9 int fd;

10 int ret;

11

12 fd = open("/dev/test",

O_RDWR);

13 if(fd < 0)

14 {

15 perror("open");

16 return -1;

17 }

18

19 write(fd, "xiao bai",

10);

20 /*讓偏移量移至開頭,這樣才能讀取數(shù)據(jù)*/

21 ret = lseek(fd, 0, SEEK_SET);

22

23 read(fd, buf, 10);

24 printf("buf

is [%s]\n", buf);

25

26 close(fd);

27 return 0;

28 }

驗(yàn)證一下:

[root: 2nd]# ./app

[test_write]write 10

bytes, cur_size:[10]

[test_write]kbuf is

[xiao bai]

[test_read]read 10

bytes, cur_size:[0]//讀到數(shù)據(jù)了!

buf is [xiao

bai]//讀到數(shù)據(jù)了!

五、總結(jié)

拉風(fēng)的時(shí)序圖我就不畫了。

上面講的東西不多:

1)container_of的使用

2)怎么使用偏移量"filp->f_ops"。

3)llseek的編寫。

=========================================================

總結(jié)

以上是生活随笔為你收集整理的linux 设备驱动总结,linux设备驱动归纳总结(三):3面向对象思想和lseek的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。