【迅为iTop4412学习笔记】1.简单的linux模块


声明

以下都是我刚开始看驱动视频的个人强行解读,如果有误请指出,共同进步。

本节目标

  1. 了解什么是模块
  2. 加载、卸载模块的时候打印一段话
  3. 简单的Makefile编译

简述

  对于剪裁linux内核,其实就是选择是否把某一部分代码编译进内核,编译进去了,我们在启动linux的时候,就可以直接用。那么如果为了减小编译出来的linux内核,不编译进去,想用的时候怎么办?

  linux的做法是:模块。

  我们要用,就把模块插入linux内核,不用了,就把他卸载掉。linux把必要的编译进去,没必要的可以编译成模块,然后我们要用就插入模块。方便。

下面开始说编译一个最简单的模块。

  对于编写模块的C程序,不是像学C语言一样,上来先int main()。我们从头文件开始一步一步看。

一、头文件

  所有的头文件都在迅为的linux源码下,相对路径:Android4.0/iTop4412_Kernel_3.0/include

  直接先上必要的头文件

// linux要加载、卸载模块,需要包含此头文件
#include <linux/init.h>
// 加墨、卸载模块相关的宏定义、库函数需要包含此头文件
#include <linux/module.h>

  在此我试图查看源码看看是如何实现的,打开看了一下我决定关掉先学怎么用…

  linux加载、卸载模块要分别调用两个来自module.h的宏定义(是什么后面讲),才能完成加载和卸载的过程。

二、GPL协议

   Linux是开源的,所以我们要遵循开源协议,在模块的开头附上这些信息,如果不写会无法编译(写了才表示你统一遵循协议)

// 声明GPL许可证,总之免费使用,但是要开源
MODULE_LICENSE("Dual BSD/GPL");
// 这里是作者
MODULE_AUTHOR("MrYang");

以上写法表示我们遵循BSD和GPL协议,至于代表什么意义,可以自己去查…作者就填自己。

三、模块的init和exit

  在讲头文件的时候我们有说,当我们把模块插入内核的时候,会调用一个宏定义,卸载也是一样,我们先上这两个宏定义。

// 函数名很清楚了...
module_init(加载函数);
module_exit(卸载函数);

  此处我没有上源码,而是直接写的函数,一方面是打开源码看确实看不懂,二是刚学也没必要深究,先学会咋用把。

  名字其实已经很清楚了,加载模块的时候调用module_init(),这是一个宏定义,参数是一个自定义的int类型的函数,linux调用了module_init(),然后就找到了我们定义的函数进行执行。卸载的参数则是一个void类型的函数。

四、定义init和exit需要调用的函数

// 用于init时调用的函数
static int mryang_init(void)
{
	printk(KERN_EMERG "HELLO WORLD enter!\n");
	return 0;
}
// 用于exit时调用的函数
static void mryang_exit(void)
{
	printk(KERN_EMERG "HELLO WORLD exit!\n");
}

  首先注意到输出函数并非C语言常用的printf,而是printk,k应该是指kernel,输出内容前的KERN_EMERG是指控制内核log输出级别,高级别的输出到屏幕,低级别的保存为日志文件,我们需要打印到屏幕,就给了最高的权限。

  我们自定义了两个函数,用了放入module_init()和module_exit()里当作参数。这样,linux加载就会调用module_init()宏定义,然后根据参数调用我们自定义的mryang_init()函数,卸载同理

五、贴出模块内的所有程序(mini_linux_module.c)

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

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("MrYang");

static int mryang_init(void)
{
	printk(KERN_EMERG "Hello MrYang!\n");
	return 0;
}

static void mryang_exit(void)
{
	printk(KERN_EMERG "Bye MrYang!\n");	
}

module_init(mryang_init);
module_exit(mryang_exit);

  编写完了程序,一个问题来了,我们如何编译他?对于模块,才用的是利用Makefile自动化编译来编译他

  为什么要用Makefile?假如有很多很多程序的时候,我们难不成一个一个编译吗?所以我们编写一个Makefile,来进行编译。

六、写一个Makefile对程序进行自动化编译

  先不管Makefile怎么一步一步写,我们直接上代码

#!/bin/bash
#通知编译器我们要编译模块的哪些源码
#这里是编译itop4412_hello.c这个文件编译成中间文件itop4412_hello.o
obj-m += mini_linux_module.o 

#源码目录变量,这里用户需要根据实际情况选择路径
#作者是将Linux的源码拷贝到目录/home/topeet/android4.0下并解压的
KDIR := /home/topeet/android4.0/iTop4412_Kernel_3.0

#当前目录变量
PWD ?= $(shell pwd)

#make命名默认寻找第一个目标
#make -C就是指调用执行的路径
#$(KDIR)Linux源码目录,作者这里指的是/home/topeet/android4.0/iTop4412_Kernel_3.0
#$(PWD)当前目录变量
#modules要执行的操作
all:
	make -C $(KDIR) M=$(PWD) modules
		
#make clean执行的操作是删除后缀为o的文件
clean:
	rm -rf *.o

  Makefile显然不是我写的!

  迅为的说法是,我们没必要抱着Makefile去啃,有些东西大致看看就懂了,真要用了再去研究。

  所以这其实可以当作一个模板,因为东西很少,所以看起来比较直观。

  obj-m表示编译成模块,后面跟着的是后缀为.o的文件名,你把.c放这跟他同级,Makefile自动帮你编译成.o文件。KDIR就是源码目录,PWD是当前目录,所以你的.c和Makefile放在同一级的文件夹里。

大致看看就行,我们要改的就两个地方,一个是文件叫啥名字,一个是源码的路径

七、编译,加载、查看、卸载等等…

  最后俩文件 mini_linux_module.c 和 Makefile 放在同一个文件夹下,然后控制台输入

make

报错会有ERROR,不报错,生成mini_linux_module.ko文件就是最后驱动了。

我们把生成的驱动放到U盘然后插入板子,输入命令挂载U盘到/mnt/disk目录

mount /dev/sda1 /mnt/disk

然后我们进入/mnt/disk目录就可以用命令加载、卸载驱动了!

加载驱动:

insmod mini_linux_module.ko

查看驱动:

lsmod

卸载驱动:

rmmod mini_linux_module

注意 卸载不带.ko

卸载的时候我出现了点小问题,发现卸载失败,后来百度搜的方法是:

  1. 控制台uname -r 查看内核版本,返回:3.0.15
  2. 新建文件夹 mkdir /lib/modules/3.0.15,再卸载 就OK了
    补充1:原来迅为的pdf里有写,所以说还是要认真看迅为给的资料…

=======================================


不用板子用虚拟机如何完成?

Makefile文件里源码路径要修改为虚拟机的linux内核源码路径

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

但运行起来发现printk无输出,可以先安装sudo apt install busybox-syslogd,再用命令sudo cat /proc/kmsg,就可以完整显示内核打印的消息了,若要后台命令后面就加个&。

你可以插入模块之后,再用命令查看内核输出的信息,网上的说法是只有串口才能输出printk信息,用SSH无法输出,只能手动查看,网上的解决方法试了几个没啥用,懒得试了…

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值