深入浅出Linux设备驱动编程之内核模块

2016-01-29 17:41 8 1 收藏

深入浅出Linux设备驱动编程之内核模块,深入浅出Linux设备驱动编程之内核模块

【 tulaoshi.com - Linux 】

  Linux设备驱动属于内核的一部分,Linux内核的一个模块可以以两种方式被编译和加载:

  (1)直接编译进Linux内核,随同Linux启动时加载;

  (2)编译成一个可加载和删除的模块,使用insmod加载(modprobe和insmod命令类似,但依赖于相关的配置文件),rmmod删除。这种方式控制了内核的大小,而模块一旦被插入内核,它就和内核其他部分一样。

  下面我们给出一个内核模块的例子:

#include <linux/module.h //所有模块都需要的头文件
#include <linux/init.h // init&exit相关宏
MODULE_LICENSE("GPL");
static int __init hello_init (void)
{
 printk("Hello module initn");
 return 0;
}

static void __exit hello_exit (void)
{
 printk("Hello module exitn");
}

module_init(hello_init);
module_exit(hello_exit);
  分析上述程序,发现一个Linux内核模块需包含模块初始化和模块卸载函数,前者在insmod的时候运行,后者在rmmod的时候运行。初始化与卸载函数必须在宏module_init和module_exit使用前定义,否则会出现编译错误。

  程序中的MODULE_LICENSE("GPL")用于声明模块的许可证。
    
  如果要把上述程序编译为一个运行时加载和删除的模块,则编译命令为:

gcc -D__KERNEL__ -DMODULE -DLINUX -I /usr/local/src/linux2.4/include -c -o hello.o hello.c
  由此可见,Linux内核模块的编译需要给gcc指示-D__KERNEL__ -DMODULE -DLINUX参数。-I选项跟着Linux内核源代码中Include目录的路径。

  下列命令将可加载hello模块:

insmod ./hello.o
  下列命令完成相反过程:

rmmod hello
  如果要将其直接编译入Linux内核,则需要将源代码文件拷贝入Linux内核源代码的相应路径里,并修改Makefile。
我们有必要补充一下Linux内核编程的一些基本知识:

  内存

  在Linux内核模式下,我们不能使用用户态的malloc()和free()函数申请和释放内存。进行内核编程时,最常用的内存申请和释放函数为在include/linux/kernel.h文件中声明的kmalloc()和kfree(),其原型为:

void *kmalloc(unsigned int len, int priority);
void kfree(void *__ptr);
  kmalloc的priority参数通常设置为GFP_KERNEL,如果在中断服务程序里申请内存则要用GFP_ATOMIC参数,因为使用GFP_KERNEL参数可能会引起睡眠,不能用于非进程上下文中(在中断中是不允许睡眠的)。

  由于内核态和用户态使用不同的内存定义,所以二者之间不能直接访问对方的内存。而应该使用Linux中的用户和内核态内存交互函数(这些函数在include/asm/uaccess.h中被声明):

unsigned long copy_from_user(void *to, const void *from, unsigned long n);
unsigned long copy_to_user (void * to, void * from, unsigned long len);
  copy_from_user、copy_to_user函数返回不能被复制的字节数,因此,如果完全复制成功,返回值为0。

  include/asm/uaccess.h中定义的put_user和get_user用于内核空间和用户空间的单值交互(如char、int、long)。

  这里给出的仅仅是关于内核中内存管理的皮毛,关于Linux内存管理的更多细节知识,我们会在本文第9节《内存与I/O操作》进行更加深入地介绍。

  输出
 
  在内核编程中,我们不能使用用户态C库函数中的printf()函数输出信息,而只能使用printk()。但是,内核中printk()函数的设计目的并不是为了和用户交流,它实际上是内核的一种日志机制,用来记录下日志信息或者给出警告提示。

  每个printk都会有个优先级,内核一共有8个优先级,它们都有对应的宏定义。如果未指定优先级,内核会选择默认的优先级DEFAULT_MESSAGE_LOGLEVEL。如果优先级数字比int console_loglevel变量小的话,消息就会打印到控制台上。如果syslogd和klogd守护进程在运行的话,则不管是否向控制台输出,消息都会被追加进/var/log

来源:https://www.tulaoshi.com/n/20160129/1502507.html

延伸阅读
深入浅出 CPropertySheet 译者:徐景周(原作:Mustafa Demirhan) 为了最大限度的发挥属性页的效用,首先让我们先从 CPropertySheet 继承一个新类,取名为 CMyPropSheet. 接着便可以进行下面的各种操作: 一、隐藏属性页默认按钮 隐藏掉Apply应用按钮: propsheet.m_psh.dw...
上次我们简单介绍了静态成员,今天再延伸一下,看看设计模式中的单件模式(Singleton),我先照搬一些理论 Singleton-对象创建型模式意图:保证一个类仅有一个实例,并提供一个访问它的全局访问点。动机:对于一些类来说,只有一个实例是很重要的。虽然系统中可以有许多打印机,但却只应该有一个打印假脱机(printer spooler),只应该有一个文件...
标签: SQLServer
嵌套SELECT语句也叫子查询,形如: SELECT name FROM bbc WHERE region = (SELECT region FROM bbc WHERE name = 'Brazil') 一个 SELECT 语句的查询结果可以作为另一个语句的输入值。 上面的SQL语句作用为获得和'Brazil'(巴西)同属一个地区的所有国家。 子查询不但可以出现在Where子句中,也可以出现在from子句中,作为一个临时表...
标签: SQLServer
嵌套SELECT语句也叫子查询,形如: SELECT name FROM bbc WHERE region = (SELECT region FROM bbc WHERE name = 'Brazil') 一个 SELECT 语句的查询结果可以作为另一个语句的输入值。 上面的SQL语句作用为获得和'Brazil'(巴西)同属一个地区的所有国家。 子查询不但可以出现在Where子句中,也可以出现在from子句中,作为一个...
标签: SQLServer
在介绍GROUP BY 和 HAVING 子句前,我们必需先讲讲sql语言中一种特殊的函数:聚合函数,例如SUM, COUNT, MAX, AVG等。这些函数和其它函数的根本区别就是它们一般作用在多条记录上。 SELECT SUM(population) FROM bbc 这里的SUM作用在所有返回记录的population字段上,结果就是该查询只返回一个结果,即所有国家的总人口数。 通过使用...

经验教程

194

收藏

71
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部