驱动程序学习(一)从“hello world”开始

来源:互联网 发布:断电后ubuntu崩溃了 编辑:程序博客网 时间:2024/05/21 22:40

终于开始写驱动程序,首先说些题外话吧。

我们学习嵌入式,学习了bootloader  kernel  filesystem

进行过内核的移植,驱动的移植,根文件系统的制作,可是这些都与编程能力无关。

而我们学习的造诣,我们的能力,更多的是体现在编程能力上,所以以后的学习还要多敲代码,多看书。

首先来贴一段代码:hello.c

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


int insmod_module(void)
{
printk("Hello,World\n");
return 0;
}


void rmmod_module(void)
{
printk("Goodbye\n]");
}


module_init(insmod_module);
module_exit(rmmod_module);

这是一个最简单的字符型驱动模型

可以分为四部分

//驱动程序所必需要的包含文件

//设备的功能接口函数与数据结构体

主要是:open()

reda()

write()

ioctrl()  还有file_operation

//加载驱动的入口点 module_init()

//卸载设备驱动的入口点module_exit()

Makefile

      KERNELDIR ?= /home/linux/linux-2.6.22.6

        PWD := $(shell pwd)
        modules:
                $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
        modules_install:
                $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install
        clean:
                rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
        .PHONY: modules modules_install clean

接下来我们在说一下,makefile

将前面声明的路径替换到后面的命令(注意后面的命令前面的不是空格,是TAB)

引用自:http://www.embedu.org/Column/Column310.htm

KERNELDIR ?= /home/linux/linux-2.6.22.6,这句是对KERNELDIR进行赋值,这个变量是后面我们用到的指代内核源码目录用的。

PWD := $(shell pwd),这句是对PWD变量进行赋值,作用是将$(shell pwd)的返回结果既求得当前目录的路径赋值给PWD,这个变量我们在后面指代我们要编译的驱动程序所在的位置。

modules:
        $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

这句是Makefile的规则:这里的$(MAKE)就相当于make-C 选项的作用是指将当前工作目录转移到你所指定的位置。“M=”选项的作用是,当用户需要以某个内核为基础编译一个外部模块的话,需要在make modules 命令中加入“M=dir”,程序会自动到你所指定的dir目录中查找模块源码,将其编译,生成KO文件。

modules_install:
                $(MAKE) -C $(KERNELDIR) M=$(PWD) modules_install

这个命令是模块的安装,在Makefile中搜索“lib\/modules”可以看到下面的语句,通过阅读你不难找到这个“MODLIB”的用处,它是用来指定安装路径的,而变量“INSTALL_MOD_PATH”往往为空。

MODLIB = $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
        Export MODLIB

.PHONY: modules modules_install clean

这句话是的作用是保证modules,modules_install,clean这三个命令能正常完成。.PHONY 这是一个特殊目标名称,还有其它的,如:

.SUFFIXES,.DEFAULT,.PRECIOUS,.INTERMEDIATE,.SECONDARY,.SECONDEXPANSION,.DELETE_ON_ERROR,.IGNORE .LOW_RESOLUTION_TIME .SILENT .EXPORT_ALL_VARIABLES .NOTPARALLEL

          

它们的具体用法可以参考GNU手册中的Special Built-in Target Names章节。

.PHONY目标的具体意思是如果在Makefile的工作目录中有名如:modules,modules_install,clean等文件时命令会出错。它是防止这出错的方式。

编译成功后会得到很多个文件,其中有我们所要的hello.ko

通过 insmod  可以加载模块到我们的系统上。

insmod  hello.ko

按道理我们应该的得到一行打印信息:Hello,World;可是我的系统上并没有这个信息

接着执行lsmod

确实找到了我们编译出来的模块hello.ko

然后卸载 rmmod hello.ko 可是也没有打印信息。这个不大明白是怎么回事。

有待解决。

解决方法:将所有的printk改成: printk("Hello,World\n");

  printk(KERN_EMERG "Hello,World\n");

今天在网上google了一下找到了相关的资料。

printk与printf类似只是一个运行在内核态,一个运行在用户态。

二者的作用相同,不同点也是很明显的。

 用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。

没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到
  日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):
  #define KERN_EMERG    0
  #define KERN_ALERT     1
  #define KERN_CRIT       2
  #define KERN_ERR        3
  #define KERN_WARNING  4
  #define KERN_NOTICE    5
  #define KERN_INFO       6
  #define KERN_DEBUG     7

通过读写/proc/sys/kernel/printk文件可读取和修改控制台的日志级别。查看这个文件的方法如下:
  #cat /proc/sys/kernel/printk 

      # 8      4        1          7
  上面显示的4个数据分别对应控制台日志级别、默认的消息日志级别、最低的控制台日志级别和默认的控制台日志级别

可用下面的命令设置当前日志级别:
  # echo 8 > /proc/sys/kernel/printk
  这样所有级别<8,(0-7)的消息都可以显示在控制台上. 

原创粉丝点击