linux驱动程序开发详细介绍

Oo飘雪的夜oO

Oo飘雪的夜oO

2016-02-19 21:58

想不想get新技能酷炫一下,今天图老师小编就跟大家分享个简单的linux驱动程序开发详细介绍教程,一起来看看吧!超容易上手~

第一个内核模块(Hello World模块)

代码如下:

View Code

#includelinux/init.h

#includelinux/module.h

MODULE_LICENSE("Dual BSD/GPL");

static __init int hello_init(void)

{

//printk函数在内核中定义对模块可用,内核需要自已的打印涵数

//因为它靠自已运行,而没有相应的库函数。

//模块能够调用printk是因为insmod加载了之后,模块被链接到内核

//因些可调用内核的公用符号,KERN_ALERT是消息的优先级

printk(KERN_ALERT"HELLO WORLDn");

return 0;

}

static __exit void hello_exit(void)

{

printk(KERN_ALERT"GoodByen");

}

module_init(hello_init);

module_exit(hello_exit);

在这个模块中定义了两个函数,一个在模块加载到内核时调用(hello_init),另一个在从内核将模块移出时调用(hello_exit);在上面的代码中,module_init与module_exit是两个内核宏定义,用于告诉内核从哪里启动,从哪里退出,MODULE_LICENSE宏用于声明模块是遵守某个自由许可证的,否则内核加载时会出现警告。

好了,现在可以对上面这个程序进行相应的测试,在测试之前必须要编写相应的Makefile文件,模块的编译与普通程序的编译是不同的

Makefile文件

代码如下:

View Code

#makefile for hello world

# KERNELRELEASE是在内核源码中定义的第一个变量

ifneq ($(KERNELRELEASE),) #判断变量是否为空(第一次执行时没有定义)

#没定义时执行else语句

obj-m := HelloWorld.o#表明有一个模块要从目录文件HelloWorld.o建立,建立之后将其

#命名为HelloWorld.ko

#如果有一个模块名为module.ko,来自于两个源文件,假设为file1.c与file2.c

#则应该这样 obj-m := module.o

# module-objs:=file1.o file2.o

else

KDIR:=/lib/modules/$(shell uname -r)/build

all:

#当make的目标为all时,-C $(KDIR)跳到内核源码目录下读取Makefile

#M=$(PWD)表示返回当前目录继续读取,执行当前的Makefile,当再次执行时

#$(KERNELRELEASE)已经定义,make将读取else之前的内容

make -C $(KDIR) M=$(PWD) modules

clean:

rm -rf *.ko *.o *.mod.o *.mod.c *.symvers

endif

相应的解释如上

开如编译内核:必须要是超级用户

在当前路径下输入make

编译完成后,输入insmd HelloWorld.ko进行内核的加载,使用dmesg |tail可以查看内核的输出信息。

移除内核采用rmmod HelloWorld 相应的使用dmesg|tail可以看到打印出GoodBye

Printk可能没有输出到屏幕上,这与KERN_ALERT的优先级有关,说明还不够高,内核输出的内容实际在/var/log/kern.log中,可以vim /var/log/kern.log查看。

内核模块与应用程序的不同:

1:应用程序运行后就会处理相应的任务,而内核模块注册后是用来服务于将来请求,并且初始化函数加了__init之后,调用完后,内存空间立即释放。

2:应用程序终止时可以不用负责回收资源,由操作系统来维护,但内核模块在除时必须释放资源。

3:应用程序可以调用相应的库函数,而内核模块能够调用的则只要内核中输入的那些函数。在内核模块的编程中,源文件不应当包括通常的头文件,但也有例外,如stdarg.h等少部分头文件是仅有的例外。

4:错误的处理方式不同,在应用程序中段错误,可以由相应的调试程序进行检查更改,但内核模块中,段错误,如果不终止整个系统的话,就会终止当前进程。

用户空间与内核空间:

应用程序在用户空间运行,而内核模块是在内核空间内运行的。每种模式都有它自已的内存映射,它自已的地址空间。

内核与当前进程的关系:

内核模块做的大部份动作是代表一个特定进程的,内核代码可以引用当前进程,通过存取全局项current,它在asm/cuurent.h定义:

#define current get_current()//通过这个宏定义可以获取指向task_struct的任务指针

内核代码可以通过current来使用进程特定的信息。

内核符号表:

内核模块在加载时通过查找内核符号表来解决未定义的符号,内核符号表包涵了全局内核项的地址,当加载一个模块时,模块中输出的符号也将成为内核符号表的一部分。

模块的输入符号通常采用以下两种形式:

EXPORT_SYMBOL(name)

EXPORT_SYMBOL_GPL(name)

上面的宏定义中的任何一个使得给定的符号在模块外使用,_GPL版本的宏定义只能使符号对_GPL许可的模块可用。

版本依赖:

模块代码一定要为每个它要连接的内核版本重新编译,在模块编译的过程中,其中一步是到当前的内核对读取Makefile文件,在编译的过程中会采用内核树中的文件(vermagic.o)连接你的模块,在这个文件里面有许多有关内核的信息,包括版本...

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/diannaorumen/)

模块参数:

模块参数由insmod与modprobe在加载时指定。

(本文来源于图老师网站,更多请访问http://m.tulaoshi.com/diannaorumen/)

对前面的HelloWorld.c进行修改如下:

在终端上输入

代码如下:

Make

Insmod HelloWorld.ko who=test num=10

Dmesg|tail -3

即可以看到。

声明数组参数时采用module_param_array(name,type,num,perm)

Name是数组的名字,type是数组元素的类型,num是数组无数的个数,perm是权限

附:insmod

Insmod将内核模块加载到内存中,它依赖一个在kernel/module.c中定义的系统调用,函数sys_init_module分配内核内存来存放模块,它接着copy模块的代码段到这块内存区,借助内核符号表来解决模块中的内核引用,并且调用模块的初始经函数来启动所有的东西。

Modprobe工具也用来加载一个内核模块到内存,与insmod不同的是,它会查看要加载的模块,看看是否引用了当前内核没有定义的符号。如要有,它会在当前搜索路径下寻找其他模块,看是否这个符号的定义,如果有,则将这个模块也加载进内核。

Rmmod用来去除内核模块,如果内核认为模块还在使用,或者内核配置了不允许去除模块,则模块的卸载会失败。

Lsmod例举出当前系统中加载的所有模块列表。

内核模块编程中函数通常声明为静态的,是因为它们不会在文件之外可见。

展开更多 50%)
分享

猜你喜欢

linux驱动程序开发详细介绍

电脑入门
linux驱动程序开发详细介绍

如何编写Linux设备驱动程序

Linux Linux命令 Linux安装 Linux编程 Linux桌面 Linux软件 Linux内核 Linux管理
如何编写Linux设备驱动程序

s8lol主宰符文怎么配

英雄联盟 网络游戏
s8lol主宰符文怎么配

Linux网络驱动程序编写一

Linux Linux命令 Linux安装 Linux编程 Linux桌面 Linux软件 Linux内核 Linux管理
Linux网络驱动程序编写一

在Linux下安装显卡驱动程序

PHP
在Linux下安装显卡驱动程序

lol偷钱流符文搭配推荐

英雄联盟 网络游戏
lol偷钱流符文搭配推荐

驱动程序怎么安装

电脑网络
驱动程序怎么安装

驱动程序是什么

电脑网络
驱动程序是什么

lolAD刺客新符文搭配推荐

英雄联盟
lolAD刺客新符文搭配推荐

Jsp&Servelet 学习笔记(4)

Jsp&Servelet 学习笔记(4)

图片显示不出来的解决方法

图片显示不出来的解决方法
下拉加载更多内容 ↓