1、最简单的驱动hello.ko

    

hello.c源文件

  • module.h包含可装载模块需要的大量符号和函数定义
  • init.h指定初始化和清除函数
  • MODULE_LICENSE宏告诉内核,该模块采用的协议
  • printk可能不会将信息打印在终端上,可用dmesg命令查看(dmsg | tail -5只读最后5行)
  • module_init该宏在模块目标代码中增加一个特殊的段,用于说明内核初始化函数所在的位置。
#include <linux/init.h>
#include <linux/module.h>

MODULE_LICENSE("Dual BSD/GPL");
static int hello_init(void)
{
        printk(KERN_ALERT "Hello, world\n");
        return 0;
}


static void hello_exit(void)

{

        printk(KERN_ALERT "Goodbye, world\n");

}


module_init(hello_init);

module_exit(hello_exit);

编译生成hello.ko模块

方法一、Makefile+命令行
Makefile

obj-m := hello.o

命令行

  • make modules 忽略中间参数是编译模块的意思
  • -C $(KDIR)指明跳转到内核源码目录下读取那里的Makefile
  • M=$(PWD) 表明然后返回到当前目录继续读入、执行当前的Makefile
make -C /usr/src/linux-headers-4.13.0-16-generic M=`pwd` modules

方法二、完整的Makefile

    KERNELRELEASE是在内核源码的顶层Makefile中定义的一个变量,在第一次读取执行此Makefile时,KERNELRELEASE没有被定义,所以make将读取执行else之后的内容,当从内核源码目录返回时,KERNELRELEASE已被定义

ifeq ($(KERNELRELEASE),)
    KERNELDIR ?= /usr/src/linux-headers-$(shell uname -r)
    PWD := $(shell pwd)
module:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules

clean:
    rm -rf *.o *.mod.c *.ko *~ core .depend .tmp_version .*.cmd
else
    obj-m := hello.o
endif

安装、卸载模块

  • sudo insmod hello.ko安装之后,调用dmesg可以看到hello,world
  • sudo rmmod hello 卸载后,调用dmesg可以看到goodbye,world
  • lsmod 通过读取/proc/modules虚拟文件获得已安装模块

内核编译的注意事项

  1. 内核使用非常小的栈,可能只有4096B(一页),自己编写的驱动函数必须和整个内核空间共享这个一栈。因此需要大的结构,使用动态分配kmalloc。
  2. “_ ”双下划綫前缀的函数通常是接口的底层组件。 _ init标记表示,模块装载之后将扔掉初始化函数。 _ _exit标记表示该代码用于模块卸载,如果模块直接编译进内核或不允许卸载这该函数被丢弃。
  3. 用户空间的驱动程序可以实现为一个服务器进程,其任务是代替内核作为硬件控制的唯一代理,客户应用程序可连接到该服务器并和设备执行实际通信。

猜你喜欢

转载自blog.csdn.net/qq_23084801/article/details/78938343
今日推荐