首页 > 编程知识 正文

linux驱动开发详解(linux驱动开发流程)

时间:2023-05-04 04:11:44 阅读:88737 作者:3026

请关注“技术概要”,一步步学习linux内核驱动程序。

在linux操作系统中,一切都是文件。 文件是文件,目录是文件,设备是文件,套接字是文件,管道也是文件。

linux操作系统将所有这一切都用文件抽象出来,文件成为了这些实体的编程接口。 因此,基于linux的编程将成为面向文件的编程,对Linux APP应用程序开发者来说无需爽快。

但是,对内核开发者来说未必如此。 虽然APP应用层可以通过open、write和read进行全部操作,但内核中需要不同的部分(或驱动程序)来实际实现这一切。

在本文中,作为linux驱动程序开发的第一次,Linux APP应用程序的write ) )函数如何被调用) )编写了最简单的文字设备驱动程序,最后几个

上图简单说明,呼叫流程如下。

APP层----系统调用----驱动

如果在APP中没有定义普通函数,则必须在c库中定义普通函数。 c库因情况而异。 像一些文字处理函数一样,c库实现它们; 但是,就像write函数一样,c库只需要进行一些检查,就会陷入write的系统调用中,系统调用会通过软中断陷入内核空间中执行。

APP空间和内核空间相互隔离,不能互相看到对方,也不能访问对方的数据,这是为了安全。 因此,在write函数的情况下,从用户空间通过系统调用进入内核空间后,为了使内核空间能够处理该数据,必须将从APP应用程序传递到write的数据复制到内核空间中,即copy _ from _ susse

整个过程在上图中已经很清楚了,但也有问题。 OS内的系统调用怎么知道最终应该调用哪个驱动程序内的write函数呢? linux驱动开发第一次:带你去制作最简单的字符设备驱动程序,运行测试程序时,调用测试程序中的open、write和read函数时,分别为hello驱动程序中的hello

当然不是!

让我先从逻辑上说明这个问题。

如果没有记错,hello驱动器定义了主设备号和次设备号。

int reg_major=232;

int reg_minor=0;

int hello _ init (语音) ) )。

{

devnum=mkdev(reg_major,reg_minor );

gdev=kz alloc (sizeof (结构光盘),GFP_KERNEL );

g文件=kz分配(结构文件操作,GFP _内核);

.

cdev_init(gdev,g文件);

cdev_add(gdev,dev号,3 );

在hello_init中,将主设备编号232和该设备编号0组合为devNum。

cdev_init(gdev,g文件); 建立了gDev和gFile的逻辑关系

cdev_add(gdev,dev号,3 ); 建立了gDev和devNum的逻辑关系

其实打开代码详细看,上述两个代码实际上是gFile和devNum的对应关系,也就是file_operations和devNum的对应关系,也就是file_operation和主设备编号(232,0 )的对应关系

注:在linux中,APP应用层中打开的文件用文件句柄或fd表示,而内核中打开的文件用结构文件表示,对该文件的操作用结构文件_操作表示fd和struct file是一一对应的,struct file和struct file_operations也是一一对应的。 这是结构文件_操作的结构定义。

结构文件操作

结构模块*所有者;

loff_t(*llseek ) )结构文件*,loff_t,整数);

size_t读) )结构文件*、字符用户*、大小_ t、关闭_ t * );

size_t写) )结构文件*,常量字符用户*,大小_ t,洛夫_ t * );

结构文件*,结构虚拟机_区域结构*;

int (*开放)结构节点*,结构文件*;

结构文件*,fl _所有者标识符;

int (*发行)结构

t inode *, struct file *); int (*fsync) (struct file *, loff_t, loff_t, int datasync); int (*fasync) (int, struct file *, int); ... };

在上一讲的例子里,我们打开的文件名字是/dev/hello,这是一个设备文件,对应的主次设备号分别为232和0。所以,lcddyg打开/dev/hello之后,就已经建立了这个文件和hello驱动里的 struct file 的对应关系,也就建立了这个文件和hello驱动里的struct file_operations的对应关系。

好,了解以上的背景之后,我们来看看代码。

我们从内核里write系统调用的实现部分开始阅读:

相关的代码在:fs/read_write.c

备注:SYSCALL_开头表示是系统调用。

关键代码在vfs_write。所以,我们继续跟进入:

继续跟入__vfs_write:

ssize_t __vfs_write(struct file *file, const char __user *p, size_t count, loff_t *pos) { if (file->f_op->write) return file->f_op->write(file, p, count, pos); else if (file->f_op->write_iter) return new_sync_write(file, p, count, pos); else return -EINVAL;

关键代码在这里:

if (file->f_op->write) return file->f_op->write(file, p, count, pos);

上面提到建立了/dev/hello和file_operations的关系。所以这里其实就是判断hello驱动里有没有定义write函数,如果有,那就调用hello驱动里的write函数。

所以,按照如上的路径,应用程序里的write就顺利的调用到了hello驱动里的write函数。因为我们驱动里的hello_write和hello_read里都返回了0。所以,应用程序里的write和read也返回了0。

ssize_t hello_write(struct file *f, const char __user *u, size_t s, loff_t *l) { printk(KERN_EMERG"hello_writern"); return 0; } ssize_t hello_read(struct file *f, char __user *u, size_t s, loff_t *l) { printk(KERN_EMERG"hello_readrn"); return 0; }

如果你想让测试程序里的write和read返回非零值,只要把驱动里的return 0,改为任意值就好了,大家可以自己测试一下。

关注“技术简说”,带你一步一步开发linux内核驱动。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。