设备驱动程序基础

设备驱动程序基础

​ 驱动程序是专用于控制和管理特定硬件设备的软件,因此也被称作设备驱动程序。从操作系统的角度来看,它可以位于内核空间(以特权模式运行),也可以位于用户空间(具有较低的权限)。在编写设备驱动程序之前,应该了解一些概念。C语言编程技巧是必需的,至少需要熟悉指针,并熟悉一些处理函数和必要的硬件知识。

  • 模块的构建过程及其加载和卸载
  • 驱动程序框架以及调试消息管理
  • 驱动程序中的错误处理

1. 内核空间和用户空间

内核空间:内存驻留和运行的地址空间。内核内存是由内核拥有的内存范围,受访问标志保护,防止任何用户应用程序有意或无意间与内核搞混。在系统上以更高的优先级运行。

用户空间:正常程序被限制运行的地址(位置)空间。可以将其视为沙盒或监狱,以便用户程序不能混用其他程序拥有的内存或任何其他资源。在用户模式下,CPU只能访问标有用户空间访问权限的内存。

分享图片

2.模块的相关概念

模块对于Linux而言就像插件和用户软件一样,模块动态扩展了内核的功能。大多数情况下,内核模块是即插即用的。内核中的模块可以提供函数或变量,在内核构建过程中运行depmod工具可以生成模块依赖文件。它通过读取/lib/modules/<kernel_release>/中的每个模块来确定它应该导出哪些符号以及它需要什么符号。处理得到的结果写入文件modules.dep.在这里插入图片描述
【模块加载】模块运行需要先加载到内核,可以使用insmod 或modprobe 来实现,前者需要指定模块路径作为参数,作为开发期间的首选;后者更加智能化,是生产系统中的首选。

  • 手动加载

    手动加载需要用户的干预,该用户应该拥有root访问权限。具体方法如下:

    1.使用insmod来加载模块,并给出所加载模块的路径:
    在这里插入图片描述

    2.insmod这种模块加载形式低级,相反,系统管理员或在生产系统中常用的modprobe.modprobe更加智能,它会加载指定的模块之前解析文件modules.dep,以便首先加载依赖关系。
    在这里插入图片描述

    • 自动加载

      depmod实用程序的作用不只是构建modules.dep 和modules.dep.bin 文件。内核开发人员实际编写驱动程序时已经明确知道该驱动程序将要支持的硬件。将驱动程序支持的所有设备的产品和厂商ID提供给该驱动程序。depmod还处理模块文件来提取和收集该信息,并生成modules.alias文件,该文件将设备映射到其对应的驱动程序。

【模块卸载】常用的模块卸载命令是rmmod,使用这个命令来卸载insmod命令加载的模块。

在这里插入图片描述

而另一个更高级的模块卸载命令是modprobe -r ,它会自动卸载未使用的相关依赖模块:
在这里插入图片描述

在开发中还有一个常用的命令lsmod,可以用来检查模块是否已加载:
在这里插入图片描述

3.驱动程序框架

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>

static int __init helloworld_init(void)
{
	pr_info("Hello world!\n");
    return 0;
}
static void __exit helloworld_exit(void)
{
    pr_info("End of the world\n");
}

/* 模块的入点和出点 */
module_init(helloworld_init);
module_init(helloworld_exit);
/* 模块相关信息 */
MODULE_AUTHOR("***** <*****@mail.com>");
MODULE_DESCRIPTION("Hello world! Module");
/* 许可 */
MODULE_LICENSE("GPL");

4.错误和消息打印

​ 错误代码有内核空间应用程序(error变量)解释。错误处理在软件开发中非常重要,而不仅仅实在内核开发中。在内核当中提供了几种错误,几乎涵盖了可能出现的错误,有时需要将它们打印出来以帮助调试。

4.1 错误处理

由于给定的错误返回错误代码会导致内核或用户空间应用产生不必要的行为,从而做出错误的决定。为了方便处理这些错误,内核树中预定义的错误几乎涵盖了可能遇到的情况。它们被定义在路径为 include/upia/asm-generic/errno-base.h 的头文件中 ,下面是定义的所有错误定义代码:

#ifndef _ASM_GENERIC_ERRNO_BASE_H
#define _ASM_GENERIC_ERRNO_BASE_H

#define	EPERM		 1	/* Operation not permitted */
#define	ENOENT		 2	/* No such file or directory */
#define	ESRCH		 3	/* No such process */
#define	EINTR		 4	/* Interrupted system call */
#define	EIO		 5	/* I/O error */
#define	ENXIO		 6	/* No such device or address */
#define	E2BIG		 7	/* Argument list too long */
#define	ENOEXEC		 8	/* Exec format error */
#define	EBADF		 9	/* Bad file number */
#define	ECHILD		10	/* No child processes */
#define	EAGAIN		11	/* Try again */
#define	ENOMEM		12	/* Out of memory */
#define	EACCES		13	/* Permission denied */
#define	EFAULT		14	/* Bad address */
#define	ENOTBLK		15	/* Block device required */
#define	EBUSY		16	/* Device or resource busy */
#define	EEXIST		17	/* File exists */
#define	EXDEV		18	/* Cross-device link */
#define	ENODEV		19	/* No such device */
#define	ENOTDIR		20	/* Not a directory */
#define	EISDIR		21	/* Is a directory */
#define	EINVAL		22	/* Invalid argument */
#define	ENFILE		23	/* File table overflow */
#define	EMFILE		24	/* Too many open files */
#define	ENOTTY		25	/* Not a typewriter */
#define	ETXTBSY		26	/* Text file busy */
#define	EFBIG		27	/* File too large */
#define	ENOSPC		28	/* No space left on device */
#define	ESPIPE		29	/* Illegal seek */
#define	EROFS		30	/* Read-only file system */
#define	EMLINK		31	/* Too many links */
#define	EPIPE		32	/* Broken pipe */
#define	EDOM		33	/* Math argument out of domain of func */
#define	ERANGE		34	/* Math result not representable */
#endif

4.2 消息打印

printk()是在内核空间中使用的,其作用和在用户空间使用printf()一样。根据所打印消息的重要性不同,可以选用include/linux/kern_levels.h中定义的八个级别的消息日志。下面是所列出的内核日志级别,每个级别对应一个字符串格式的数字,其优先级与该数字的值成反比。

0具有较高的优先级,以此类推,7的优先级最低。

#define KERN_SOH	"\001"		/* ASCII Start Of Header */
#define KERN_SOH_ASCII	'\001'

#define KERN_EMERG	KERN_SOH "0"	/* system is unusable */
#define KERN_ALERT	KERN_SOH "1"	/* action must be taken immediately */
#define KERN_CRIT	KERN_SOH "2"	/* critical conditions */
#define KERN_ERR	KERN_SOH "3"	/* error conditions */
#define KERN_WARNING	KERN_SOH "4"	/* warning conditions */
#define KERN_NOTICE	KERN_SOH "5"	/* normal but significant condition */
#define KERN_INFO	KERN_SOH "6"	/* informational */
#define KERN_DEBUG	KERN_SOH "7"	/* debug-level messages */

通过下列代码打印内核消息和日志级别:

printk(KERN_ERR "This is an error\n");

通过打印错误消息,检查日志级别参数,方便开发人员更快速的调试驱动程序。

/* integer equivalents of KERN_<LEVEL> */
#define LOGLEVEL_SCHED		-2	/* Deferred messages from sched code
					 * are set to this special level */
#define LOGLEVEL_DEFAULT	-1	/* default (or last) loglevel */
#define LOGLEVEL_EMERG		0	/* system is unusable */
#define LOGLEVEL_ALERT		1	/* action must be taken immediately */
#define LOGLEVEL_CRIT		2	/* critical conditions */
#define LOGLEVEL_ERR		3	/* error conditions */
#define LOGLEVEL_WARNING	4	/* warning conditions */
#define LOGLEVEL_NOTICE		5	/* normal but significant condition */
#define LOGLEVEL_INFO		6	/* informational */
#define LOGLEVEL_DEBUG		7	/* debug-level messages */
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值