Linux Driver-Hello World简介
1、简介
我记得在学习VC ++和C语言时,我将首先以HELLO WORLD为例进行演示,并逐步将学者介绍给会议厅。这几乎已经成为计算机编程语言学习的必要入门方式。
当然,学习linux编程时也是如此,下面的示例应该很熟悉:
首先使用VI编写C程序:vi hello.c
#include“ stdio.h”
int main()
{
printf(“ hello world !!! \ n”);
返回0;
}
然后使用GCC进行编译:gcc -o hello hello.c
最后运行程序:./hello

在终端上,您将看到:你好,世界!!!
以上是基于操作系统的用户应用程序的开发。但是,Linux驱动程序的开发完全不同,因为驱动程序的开发在内核空间中运行,而应用程序在用户空间中运行。尽管hello world是一个简单的程序,但是对于嵌入式linux驱动程序的初学者来说,通过此过程的操作,您可以对linux驱动程序开发过程及其一些概念有深刻的了解。因此,我在这里是在学习Linux的基础上进行一些整理,并撰写此博客。一方面,我回顾并巩固了这一知识。另一方面,我希望这里的内容可以为您提供一些有用的信息。弟弟很高兴。当然,我希望有专家能够给予评估和指导,并及时纠正小弟弟的错误。预先谢谢你。
2、概念
作为系统内核的一部分,驱动程序在核心模式下工作,而应用程序在用户模式下工作。换句话说,程序无法通过指针将用户空间的数据地址直接传递给内核(因为MMU映射的地址根本不相同)。要在应用程序和驱动程序之间传输数据(指针),需要进行转换。将在用户模式下“可见”的空间地址转换为在内核模式下可访问的地址。 Linux系统提供了一系列方便的功能来实现此转换,例如get_user,put_user,copy_from_user,copy_to_user等,它们负责自己检查访问权限,使用它们时不需要更多的问题。
Linux内核将驱动程序分为3种类型:字符设备,块设备和网络设备。字符设备和块设备可以像文件一样进行访问。它们之间的主要区别不是他们是否可以寻找,而是系统如何管理这两类设备。字符设备应用程序的每个I / O操作将直接传递给与系统内核相对应的驱动程序;并且用于块设备的应用程序的操作应通过系统缓冲区管理间接传递给驱动程序进行处理。这种块设备的管理方法已针对存储进行了优化。字符设备的管理方法针对操作进行了优化。对于网络设备,它是Linux系统中的一种特殊设备。不能通过字符设备或块设备之类的相应设备文件节点来访问它,并且内核不再通过诸如读写之类的调用来访问网络设备。 Linux网络系统主要基于BSD UNIX套接字机制。在系统和驱动程序之间存在用于数据传输的特殊数据结构。该系统支持数据传输和数据接收缓冲,提供流控制机制,并提供更多协议支持。
在Linux系统中,所有驱动程序都制成模块,即模块。简而言之,模块提供了功能,可以根据需要随时从内核空间加载和卸载这些模块。因此,内核模块旨在动态地向内核添加或减去函数,而不仅限于驱动程序。
关于内核模块的初始化(加载)功能
当用户输入命令“ insmod模块文件名”(或其他方法)以加载内核模块时,系统将检查是否可以加载该模块。如果可以加载,则内核调用模块的初始化函数。在linux2.4中,内核模块的初始化函数名为init_module()。但是,如果需要将驱动程序编译到内核中,则初始化函数的名称不能与内核其他部分(包括其他内核模块)的名称相同。这样,如果将程序编译到内核中就比较麻烦。但是,在此头文件中定义了宏module_init()来屏蔽两者之间的差异。实际上,使用此宏可以使驱动程序与linux2.6向上兼容,并且与linux2.0兼容更加方便。
关于内核模块的删除(卸载)功能
当用户输入命令“ rmmod module file name”(或其他方法)以卸载内核模块时,此时,系统将检测模块是否可以卸载,内核将调用模块清除功能。在linux2.4中,清理函数的函数名称为cleanup_module()。但是,如果需要将驱动程序编译到内核中,则初始化函数的名称不能与内核其他部分(包括其他内核模块)的名称相同。这样,如果将程序编译到内核中就比较麻烦。但是,在此头文件中定义了宏module_exit()来屏蔽两者之间的差异。
3、示例
由于需要将内核模块装入内核空间,因此其程序的编程与一般应用程序不同。没有像main()这样的输入函数。下面是相应功能的相应源代码hello.c,介绍如何编写驱动模块。 (由于不能在博客上显示尖括号,因此可以将其替换为双括号,并且可以在编程和调试期间将其替换。)
#ifndef __KERNEL __
#define __KERNEL __
#endif
#ifndef模块
#define MODULE
#endif
#include“ linux / module.h” //所有模块所需的头文件
#include“ linux / sched.h”
#include“ linux / kernel.h”
#include“ linux / init.h” //初始化和退出相关的宏
MODULE_LICENSE(“ GPL”);
int text_init(void){
printk(“ Hello World!”);
返回0;

}
void text_cleanup(void){
printk(“再见世界!”);
}
module_init(text_init); //注册加载时要执行的功能
module_exit(text_cleanup); //注册卸载时要执行的功能
4、Debug
Linux内核模块需要包括模块初始化和模块卸载功能。前者在insmod期间运行,而后者在rmmod期间运行。必须在使用宏module_init和module_exit之前定义初始化和卸载功能,否则会发生编译错误。
程序中的
MODULE_LICENSE(“ GPL”)用于声明模块的许可证。
编译:
gcc -c -I / usr / src / linux-2.4 / include / hello.c
运行:
insmod hello.o
终端将显示:
localhost内核:Hello World!
同时,您将在/ proc / modules中看到相应的设备信息:
更多/ proc / modules
您将看到(也许以下值是不同的):
你好844 0(未使用)
.......
卸载驱动程序:
rmmod你好
您将在终端上看到:
localhost内核:再见世界!
5、注意:

在gcc编译选项中添加-c
在gcc编译器选项中定义两个宏:-DMODULE -D__KERENL __
或直接在源文件中定义这两个宏:
#define MODULE
#define __KERNEL __
在源文件中包含module.h文件:
#include“ linux / module.h”
假设您正在运行的内核源目录的绝对路径是MyKernelSrcPath,则在编译gcc时添加一个选项:
-I $ MyKernelSrcPath / include(例如-I / usr / src / linux / include)
有时可以使用insmod -f成功加载它,但应谨慎使用。
如果看不到用printk打印的信息,则可以使用dmesg命令进行查看。
打印消息受级别限制,消息级别可以由printk设置,例如:
printk(“” n“ something”); / *其中0
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-345431-1.html
中国这次也不太强硬