Linux设备驱动:一,简介

来源:互联网 发布:php 设置脚本执行时间 编辑:程序博客网 时间:2024/06/04 19:45

内核 角色划分:

  • 进程管理:内核创建和销毁进程;调度器控制进程如何共享CPU。
  • 内存管理:内核为每一个进程都在有限的可用资源上建立一个虚拟地址空间。
  • 文件系统:结构化的文件系统。
  • 设备控制:设备驱动。
  • 网络:所有的路由和地址解析问题都在内核中实现。
每个模块可以动态链接到运行中的内核中,通过 insmod 程序,以及 rmmod程序。



3 类 驱动
  • 字符设备
  • 块设备
  • 网络接口

链接一个模块到内核:


用户空间 内核空间

模块在内核空间中运行,应用程序在用户空间运行。

应用程序存在于虚拟内存中, 有一个非常大的堆栈区.

内核, 相反, 有一个非常小的堆栈;

常常, 当你查看内核 API 时, 你会遇到以双下划线(__)开始的函数名. 这样标志的函数名通常是一个低层的接口组件, 应当小心使用. 本质上讲, 双下划线告诉程序员:" 如果你调用这个函数, 确信你知道你在做什么."

内核代码不能做浮点算术.

modprobe工具:
如同insmod,但它会查看要加载的模块,看它是否引用了当前内核没有定义的符号。

使用 堆叠 来划分模块成不同层,这有助于通过简化每一层来缩短开发时间。


输出符号给其他模块使用:
EXPORT_SYMBOL(name);EXPORT_SYMBOL_GPL(name);

用户空间驱动的好处
  • 完整的 C 库可以连接. 驱动可以进行许多奇怪的任务, 不用依靠外面的程序(实现使用策略的工具程序, 常常随着驱动自身发布).
  • 程序员可以在驱动代码上运行常用的调试器, 而不必走调试一个运行中的内核的弯路.
  • 如果一个用户空间驱动挂起了, 你可简单地杀掉它. 驱动的问题不可能挂起整个系统, 除非被控制的硬件真的疯掉了.
  • 用户内存是可交换的, 不象内核内存. 一个不常使用的却有很大一个驱动的设备不会占据别的程序可以用到的 RAM, 除了在它实际在用时.
  • 一个精心设计的驱动程序仍然可以, 如同内核空间驱动, 允许对设备的并行存取.
  • 如果你必须编写一个封闭源码的驱动, 用户空间的选项使你容易避免不明朗的许可的情况和改变的内核接口带来的问题.
用户空间驱动的缺点
  • 中断在用户空间无法用. 在某些平台上有对这个限制的解决方法, 例如在 IA32 体系上的 vm86 系统调用.
  • 只可能通过内存映射 /dev/mem 来使用 DMA, 而且只有特权用户可以这样做.
  • 存取 I/O 端口只能在调用 ioperm 或者 iopl 之后. 此外, 不是所有的平台支持这些系统调用, 而存取/dev/port可能太慢而无效率. 这些系统调用和设备文件都要求特权用户.
  • 响应时间慢, 因为需要上下文切换在客户和硬件之间传递信息或动作.
  • 更不好的是, 如果驱动已被交换到硬盘, 响应时间会长到不可接受. 使用 mlock 系统调用可能会有帮助, 但是常常的你将需要锁住许多内存页, 因为一个用户空间程序依赖大量的库代码. mlock, 也, 限制在授权用户上.
  • 最重要的设备不能在用户空间处理, 包括但不限于, 网络接口和块设备.

insmodmodprobermmod

用户空间工具, 加载模块到运行中的内核以及去除它们.


#include <linux/init.h>module_init(init_function);module_exit(cleanup_function);
指定模块的初始化和清理函数的宏定义.


__init__initdata__exit__exitdata
函数( __init 和 __exit )和数据 (__initdata 和 __exitdata)的标记, 只用在模块初始化或者清理时间. 为初始化所标识的项可能会在初始化完成后丢弃; 退出的项可能被丢弃如果内核没有配置模块卸载. 这些标记通过使相关的目标在可执行文件的特定的 ELF 节里被替换来工作.

#include <linux/sched.h>
最重要的头文件中的一个. 这个文件包含很多驱动使用的内核 API 的定义, 包括睡眠函数和许多变量声明.

struct task_struct *current;
当前进程.
current->pid
current->comm
进程 ID 和 当前进程的命令名.

obj-m
一个 makefile 符号, 内核建立系统用来决定当前目录下的哪个模块应当被建立.


/sys/module/proc/modules
/sys/module 是一个 sysfs 目录层次, 包含当前加载模块的信息. /proc/moudles是旧式的, 那种信息的单个文件版本. 其中的条目包含了模块名, 每个模块占用的内存数量, 以及使用计数. 另外的字串追加到每行的末尾来指定标志, 对这个模块当前是活动的.

vermagic.o
来自内核源码目录的目标文件, 描述一个模块为之建立的环境.


#include <linux/module.h>
必需的头文件. 它必须在一个模块源码中包含.

#include <linux/version.h>
头文件, 包含在建立的内核版本信息.

LINUX_VERSION_CODE
整型宏定义, 对 #ifdef 版本依赖有用.


EXPORT_SYMBOL (symbol);EXPORT_SYMBOL_GPL (symbol);
宏定义, 用来输出一个符号给内核. 第 2 种形式输出没有版本信息, 第 3 种限制输出给 GPL 许可的模块.


MODULE_AUTHOR(author);MODULE_DESCRIPTION(description);MODULE_VERSION(version_string);MODULE_DEVICE_TABLE(table_info);MODULE_ALIAS(alternate_name);
放置文档在目标文件的模块中.


module_init(init_function);module_exit(exit_function);
宏定义, 声明一个模块的初始化和清理函数.


#include <linux/moduleparam.h>module_param(variable, type, perm);
宏定义, 创建模块参数, 可以被用户在模块加载时调整( 或者在启动时间, 对于内嵌代码). 类型可以是 bool, charp, int, invbool, short, ushort, uint, ulong,或者 intarray.


#include <linux/kernel.h>int printk(const char * fmt, ...);
内核代码的 printf 类似物.