当前位置:网站首页 > Go语言开发 > 正文

linux驱动开发(linux驱动开发工资)



驱动程序是专用于控制和管理特定硬件设备的软件,因此也被称作设备驱动程序。从操作系统的角度来看,它可以位于内核空间(以特权模式运行),也可以位于用户空间(具有较低的权限) 。

对于 Linux 驱动程序来说,其运行在内核空间,把硬件功能提供给用户程序。

本篇文章主要介绍Linux驱动程序的一些基础知识。

内核空间和用户空间的概念有点抽象,主要涉及内存的访问权限。内核是有特权的,而用户应用程序则是受限制的。

内核空间

内核驻留和运行的地址空间。

内核内存受访问标志保护,只能由内核访问,用户应用程不能访问。另一方面,内核可以访问整个系统内存,因为它在系统上以更高的优先级运行。在内核模式下,CPU可以访问整个内存(内核空间和用户空间)

用户空间

用户程序运行的地址空间。

在用户模式下,CPU只能访问标有用户空间访问权限的内存。用户应用程序运行到内核空间的唯一方法是通过系统调用。

当进程执行系统调用时,软件中断被发送到内核,这将打开特权模式,以便该进程可以在内核空间中运行。系统调用返回时,内核关闭特权模式,进程再次受限。

Linux内核可以在运行时扩展。当系统运行时,我们可以向内核添加、删除功能。

可以在运行时添加到内核中的代码被称为“模块”。内核模块是即插即用的,一旦插入就可以使用。

模块要运行,应该先把它加载到内核,可以用 insmod 或 modprobe 来实现,前者需要指定模块路径作为参数,这是开发期间的首选;后者更智能化,是生产系统中的首选。

Linux系统的模块有三种基本类型:

对应的设备设备驱动程序:

字符设备是个能够像字节流一样被访问的设备,由字符设备驱动程序来实现。

块设备每次只能传输一个或者多个完整的块,每块包含512字节(或者2的更高次幂字节的数据)。

网络接口由内核中的网络子系统驱动,负责发送和接收数据包。网络驱动程序不需要知道各个连接的相关信息,它只负责处理数据包即可。

当然还有其他划分驱动程序模块的方法,此处不再赘述。

Linux 驱动程序是有固定框架的,我们按照既定的框架,填写内容即可。

先看一个简单的内核模块程序 helloworld.c

内核驱动程序与用户空间的程序是有很大区别的。

内核模块驱动程序有入口点和出口点,函数名字可以任意。用户程序的入口函数名称一般为 main()。

对于内核模块程序来说,需要开发人员指定入点和出点函数。在上例中,module_init()用于声明模块加载(使用 insmod 或 modprobe)时应该调用的函数为 helloworld_init,入口函数中要完成的操作是定义模块的行为。

module_exit() 用于声明模块卸载(使用 rmmod )时应该调用的函数为  helloworld_exit。

模块加载或者卸载后,init 函数或者 exit 函数立即运行一次。

在编写驱动程序的时候,需要包含很多头文件,以便获取函数、数据类型、变量的定义。有几个头文件是专门用于模块的:

module.h包含可装载模块需要的大量符号和函数的定义。init.h 用于指定入口函数和出口函数。

模块信息

内核模块使用其 .modinfo 部分来存储关于模块的信息,所有MODULE_*宏都用参数传递的值更新这部分的内容 。

其中一些宏是 MODULE_DESCRIPTION()、MODULE_AUTHOR() 和 MODULE_LICENSE()。

MODULE_LICENSE() 告诉内核模块采用何种许可,他对模块行为有影响,如果与指定的许可不兼容将导致内核模块被污染。

MODULE_AUTHOR() 用于声明模块的作者。

MODULE_DESCRIPTION() 简要描述模块的功能。


【文章福利】小编推荐自己的Linux内核技术交流群:【】整理了一些个人觉得比较好的学习书籍、视频资料共享在群文件里面,有需要的可以自行添加哦!!!(含视频教程、电子书、实战项目及代码)  

零声白金VIP体验卡(含基础架构/高性能存储/golang/QT/音视频/Linux内核)课程:  

在模块函数处理过程中,一定要检测返回值,确保所有的请求操作已经真正成功。

当遇到错误时,必须撤销在这个错误发生之前的所有设置。通常的做法是使用goto语句。

若模块装载过程中出错,要将出错之前的任何注册工作全部撤销,否则内核会处于一种不稳定的状态,因为内核中包含了一些指向并不存在的代码内部指针。

错误有时会跨越内核空间,传播到用户空间。如果返回的错误是对系统调用(open、read、ioctl、mmap)的响应,则该值将自动赋给用户空间 errno 全局变量,在该变量上调用 strerror(errno) 可以将错误转换为可读字符串

当返回指针的函数返回错误时,通常返回的是NULL 指针。而去检查为什么会返回空指针是没有任何意义的,因为无法准确了解为什么会返回空指针。为此,内核提供了3个函数 ERR_PTR、IS_ERR 和 PTR_ERR:

ERR_PTR 函数实际上把错误值作为指针返回。假若函数在内存申请失败后要执行语句 return -ENOMEM,则必须改为这样的语句:return ERR_PTR (-ENOMEM);。

IS_ERR 函数用于检查返回值是否是指针错误:if(IS_ERR(foo))。

PTR_ERR 函数返回实际错误代码:return PTR_ERR(foo);。

消息打印--printk()

不同于用户空间的 printf()函数。printk()是在内核空间使用的,其作用和在用户空间使用 printf() 一样,执行 dmesg 命令可以显示 printk() 写入的信息。

根据所打印消息的重要性不同,可以选用 include/linux/kern_levels.h 中定义的八个级别的日志消息,每个级别对应iyge字符串格式的数字,其优先级与该数字的值成反比:

举例:

实际上可以使用以下宏,其名称更有意义,它们是对前面所定义内容的包装—— pr_emerg、pr_alert、pr_crit、pr_err、pr_warning、pr_notice、pr_info和 pr_debug。

printk() 的实现是这样的:调用它时,内核会将消息日志级别与当前控制台的日志级别进行比较;如果前者比后者更高(值更低),则消息会立即打印到控制台。

像用户程序一样,内核模块也可以接收命令行参数。这样能够根据给定的参数动态地改变模块的行为,开发者不必在测试/调试期间无限期地修改/编译模块。 

为了对此进行设置,首先应该声明用于保存命令行参数值的变量,并在每个变量上使用 module_param() 宏:

当使用模块参数时,应该用 MODULE_PARM_DESC 描述每个参数。这个宏将把每个参数的描述填充到模块信息部分。

举例:

要在加载该模块时提供参数,请执行以下操作:

原文作者:【一起学嵌入式

到此这篇linux驱动开发(linux驱动开发工资)的文章就介绍到这了,更多相关内容请继续浏览下面的相关推荐文章,希望大家都能在编程的领域有一番成就!

版权声明


相关文章:

  • bsp驱动开发(bsp驱动开发怎么学)2025-02-06 21:18:08
  • 嵌入式驱动开发面试题(嵌入式驱动程序开发实验报告)2025-02-06 21:18:08
  • 微信hook开源(hook开发)2025-02-06 21:18:08
  • 好玩的代码游戏超级马里奥(超级马里奥游戏开发)2025-02-06 21:18:08
  • vs怎么用c语言(vs怎么使用c语言)2025-02-06 21:18:08
  • 开发者账号注册(开发者账号注册流程中需要注意什么)2025-02-06 21:18:08
  • bsp开发和驱动开发的区别(bsp工程师和驱动工程师的区别)2025-02-06 21:18:08
  • 预训练语言模型(预训练语言模型可以解决什么问题)2025-02-06 21:18:08
  • linux内核驱动开发教程(linux内核驱动程序)2025-02-06 21:18:08
  • linux内核驱动开发 vscode(linux内核驱动开发前景)2025-02-06 21:18:08
  • 全屏图片