DAY3-Linux内核的编译,烧写和内核模块

255 阅读4分钟

十一.编译uboot和Linux内核

1.将6818GEC.tar.gz和kernel20190724.tar.gz拷贝到Ubuntu的非共享目录

解压源代码:
    tar -xzvf 6818GEC.tar.gz

使用kernel20190724.tar.gz替换6818GEC目录中的内核(黑色LCD底板才替换)

image-20220801234015843

目录介绍:

GEC6818uboot ---------------- uboot源码

kernel ----------------------------- 内核源码

mk --------------------------------- 编译uboot和内核的脚本

out --------------------------------- 编译uboot和内核镜像文件的输出目录

prebuilts -------------------------- 编译工具链

linux -------------------------------- 三星提供的针对s5p6818的参考源码

prototype -------------------------- s5p6818平台硬件驱动的参考代码

image-20220801234020569

2.编译uboot和内核(在6818GEC目录下执行)

编译uboot --------------- ./mk -u
编译内核 ---------------- ./mk -k

编译成功后镜像文件在out/release目录下

注意:如果编译内核最后报make_ext4fs不存在,需要安装安装

sudo apt-get update
sudo apt-get install lib32c-dev lib32stdc++6

3.Linux内核的配置

内核配置用来选择将哪些代码编译进内核

通过make menuconfig进入内核配置界面

可能出现的错误:

(1)出现缺少ncurses库的错误,需要安装ncurses

image-20220801234027325

sudo apt-get update
sudo apt-get install ncurses-dev

(2)如果窗口太小,字体太大也会报错

image-20220801234033539

最大化窗口,调小字体

image-20220801234037959

内核的配置界面通过上下方向键移动光标定位的条目,底下三个按键通过左右方向键移动,如果要执行选中的按钮,直接按回车键

回车选中带箭头的条目可以进入子菜单,双击Esc返回上一级

带括号的条目可以选中,定位到该条目通过空格键选中/取消选中

(3)配置内核支持虚拟终端

Device Drivers  --->
    Character devices  --->
        [*] Virtual terminal
        [ ]   Enable character translations in console  
        [*]   Support for console on virtual terminal (NEW) 
        [*]   Support for binding and unbinding console drivers 

保存退出

完成配置之后覆盖配置文件

cp .config arch/arm/configs/GEC6818_defconfig

重新编译内核

练习:

完成uboot和内核的编译,以及内核的配置

4.烧写uboot和内核

将编译出来的uboot镜像和内核镜像去替换刷机工具包中的uboot和内核镜像,再用前面介绍的刷机方法刷机。

image-20220801234046972

image-20220801234054404

只烧写内核

image-20220801234101395

一.Linux驱动

Linux驱动属于内核的一部分,学习驱动开发时将驱动设计为内核模块,内核模块是一种可以在系统运行时加载和卸载的机制。

1.内核编程的注意事项

1.不能使用C标准库和C标准头文件
2.使用GNU C
3.没有内存保护机制
4.不能处理浮点运算
5.注意并发互斥和可移植性问题

2.内核模块的编写

需要包含头文件

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

必须实现加载函数和卸载函数

//加载函数 ---------- 模块加载自动调用
int xxx(void)
{
    //......
    
    return 0;//返回0表示加载成功
}

//卸载函数 ----------- 模块卸载时自动调用
void yyy(void)
{
    //......
}

使用内核提供的宏来修饰加载函数和卸载函数

module_init(xxx);//修饰加载函数
module_exit(yyy);//修饰卸载函数

注:内核中打印函数叫printk,用法和printf几乎一样。

模块许可证:

在编写模块时必须加上模块许可证,防止内核被污染,造成某些功能无法使用。

MODULE_LICENSE("GPL");//GPL模块许可证

模块源代码:

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

//加载函数
int hello_init(void)
{
	printk(KERN_WARNING"hello GEC!\n");

	return 0;
}

//卸载函数
void hello_exit(void)
{
	printk(KERN_WARNING"byebye GEC!\n");
}

module_init(hello_init);
module_exit(hello_exit);

//模块许可证
MODULE_LICENSE("GPL");

3.内核模块的编译

内核模块的编译使用对应内核的编译方法来编译(内核源码必须编译过),使用类似于以下格式的Makefile来编译模块

#内核源代码目录
KERNEL_DIR=/home/gec/GZFX2108/driver/6818GEC/kernel
#定义编译工具链路径
CROSS_PATH=/home/gec/GZFX2108/driver/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-


#编译成模块
obj-m		+= first.o

#编译命令
default:
	$(MAKE) CROSS_COMPILE=$(CROSS_PATH) -C $(KERNEL_DIR) M=$(PWD) modules
clean:
	rm -rf *.o *.ko *.mod* *.ord* *.sy* .*cmd .tmp* 

使用make命令进行编译,生成的模块文件的后缀为.ko

image-20220801234116661

4.模块的使用

Linux命令:

insmod ------------- 加载模块
    insmod 模块文件路径
rmmod -------------- 卸载模块
    rmmod 模块名
lsmod -------------- 查看已加载的模块
    lsmod        

image-20220801234121388

练习:

实现一个内核模块的编写,编译和使用,修改printk中的数字(0~7),测试哪些数字可以打印出来