内核模块
字符设备驱动
中断、内核定时器
裸机开发和驱动开发的区别?
|-----|----------------|------------------|
| | 裸机开发 | 驱动开发(基于内核) |
| 相同点 | 都能够控制硬件(本质:操作寄存器) ||
| 不同点 | 用C语言给对应的地址里面写值 | 按照一定的框架格式往地址里面写值 |
| 不同点 | 单独编译单独执行 | 依赖内核编译、执行 |
| 不同点 | 同时只能执行一份代码 | 同时执行多份代码 |
| 不同点 | 只需要一个main函数 | 依赖内核的框架来操作硬件 |
驱动里面操作LED灯的寄存器
驱动模块是依赖内核框架执行代码
ARM裸机直接操作的是物理内存,直接拿到寄存器地址写值。
驱动是基于内核,操作的是虚拟内存,使用之前先映射实际物理内存。
Linux系统组成
硬件层:led 鼠标 键盘 lcd 触摸屏 摄像头 u盘 emmc 光猫 路由器 dm9000
0-3G的用户空间 是每个进程单独拥有0-3G的空间
系统调用(软中断swi) 应用层通过系统调用与底层交互,swi将应用层切换到内核层。
驱动代码就在内核空间(在3-4G内核空间, 共用空间)
注:1G的物理内存映射成0~4G的虚拟内存,每个进程都可以访问内核,0~3G是每个进程单独拥有的,3G~4G是所有的共有的。代码运行在物理内存上,向虚拟内存上面写值,其实是写在物理内存上面的
宏内核和微内核(了解)
宏内核:集中管理的架构,负责多种系统资源的调度和管理;
将进程、文件、网络、内存、设备等功能集成到一个内核中,具备直接操作硬件的能力。
优点:执行效率高,操作系统资源可以直接在内核中实现,减少了进程间通信带来的性能损失,提高了系统的执行速度。
缺点:内核的代码量非常大,不易拓展,且单个模块的崩溃可能会影响整个系统的运行。
eg:ubuntu、Android
微内核:更侧重于模块化和移植性,通过传递请求的方式实现不同模块之间的交互
将最基本的操作系统功能放到内核中,其他服务和程序在微内核之上构建,并在用户态下运行。(设计分界线不同)
优点:系统灵活性和可扩展性好,系统可靠性高
缺点:代码运行效率低->通过API接口让整个系统运行起来
eg:Window 、鸿蒙
驱动移植
步骤:
需要有对应的驱动代码(.c)
修改对应目录的Makefile - drivers-》char)-》obj-$(CONFIG_LED) +=led.o
修改对应目录里的Kconfig(tristate " ...... " )
make menuconfig-》配置自己的驱动为M(模块的)
make modules(在最顶层目录)
把编译产生的.ko结尾的驱动文件,放到nfs(/opt/6818/rootfs/rootfs)目录下使用
安装insmod led.ko
卸载rmmod led
静态编译:生成的执行文件不依赖于外部库文件,可以直接运行,且程序运行效率较高。
动态编译:可执行文件和库分开写,生成的可执行程序需要依赖于外部的库文件。
内部编译:在内核源码树中编译
外部编译:在内核源码树外编译
驱动模块
驱动的框架
入口(安装):资源申请
出口(卸载):资源释放
许可证:GPL(开源的---》Linux开源-----》GPL许可证)
Linux内核许可规则
Linux内核许可规则 --- The Linux Kernel documentation
驱动程序编写
#include <linux/init.h>`
`#include <linux/module.h>`
`//申请资源`
`//static - 限定作用域,延长生命周期`
`//存储类型 数据类型 指定存放内存区域 函数名(形参)`
`static int __init hello_init(void)`
`{`
` return 0;`
`}`
`//释放资源`
`static void __exit hello_exit(void)`
`{`
`}`
`module_init(hello_init); //入口:申请资源 本质-回调一个自己写的函数`
`module_exit(hello_exit); //出口:释放资源`
`MODULE_LICENSE("GPL"); //许可证:公共许可协议(开源协议)`
`
ctags的安装和使用
方法1
1.ctags
ctags工具是将指定目录以及子目录添加索引文件,可以实现快速搜索文件内容(宏定义、取别名以及结构体)
2.ctags创建索引文件
安装ctags:
sudo apt-get install ctags
在/usr/include目录下执行命令:
sudo ctags -R
执行完毕之后,会生成一个tags的索引文件
3、默认只能在/usr/include使用,所以需要设置为全局
打开家目录下的.vimrc文件,然后添加一句话:
set tags+=/usr/include/tags
5.3 ctags工具的使用
追代码:
vim -t 结构体名/宏定义名/取别名
或
vi -t 结构体名/宏定义名/取别名
如果要查看所有定义的位置:在底行模式下输入
tselect 结构体名/宏定义名/取别名
继续追指定的内容:
将光标放在这个内容的任意位置,然后点击快捷键ctrl ]
返回上一次的文件:
ctrl t
例如:追一下sockaddr_in结构体,查看结构体成员
继续查看这个结构体中成员的类型
查看完毕后返回到最开始的结构体
方法2
把ctags_set文件夹拷贝到Ubuntu里
cd ctags_set
sudo chmod 774 ctags_set.sh
sudo ./ctags_set.sh
验证:在~目录下
vi -t sockaddr_in
生成tags 文件 :在相应目录下 ctags -R
在代码中追变量: ctrl + ]
返回代码中 : ctrl + t
mv tags /home/hq/kernel/kernel-3.4.39
可以不执行:
==================================
在内核源码目录下 执行
make tags
========================
cd ~
sudo vim .vimrc
在.vimrc 最后添加
set set tags+=内核源码绝对路径/tags
例如set tags+=/home/hq/kernel/kernel-3.4.39/tags
驱动Makefile编写
KERNELDIR = /home/hq/kernel/kernel-3.4.39 #开发板路径`
`#KERNELDIR = /lib/modules/$(shell uname -r)/build #PC机`
`PWD = $(shell pwd) #驱动文件的路径`
`all:`
` make -C $(KERNELDIR) M=$(PWD) modules`
` #基于内核框架将驱动代码编译生成驱动模块`
` #需要在内核的顶层目录下执行 make modules`
` #-C:指定到哪个路径下执行这个命令`
` #M:赋值,要将哪个路径下的驱动文件编译生成驱动模块`
` #注:进入内核目录下执行make modules这条命令,如果不指定 M=$(PWD) 会把内核目录下的.c文件编译生成.ko`
`.PHONY:clean`
`clean:`
` make -C $(KERNELDIR) M=$(PWD) clean`
`obj-m += hello.o`
`
Source Insight工具
软件安装
激活码:SI3US-719473-71478
使用(配置工程)
配置信息:*.c;*.h;*.S;*.lds;*defconfig;*Makefile;*.mak;*.dts;*.dtsi
软件使用
ctrl+/ 可以搜索变量及函数
按住ctrl左击鼠标可以追变量或者函数
打印函数
在Linux中可以使用
grep "printk" * -nR
搜索函数,搜到以后,在里面任意找到一个,看函数原形
` `printk` `(打印级别 "内容");`
`printk(KERN_ERR "Fail%d",a);`
`printk(KERN_ERR "%s:%s:%d\n",__FILE__,__func__,__LINE__);//(驱动在哪一个文件,哪一个函数,哪一行)`
`
内核打印级别
vi -t KERN_ERR(查看内核打印级别)//也可直接在SI中按住ctrl左击跳转进行查看
0 ------------------ 7
最高的 最低的
终端打印级别
#define console_loglevel (console_printk[0]) //终端的级别`
`#define default_message_loglevel (console_printk[1])//消息的默认级别`
`#define minimum_console_loglevel (console_printk[2])//终端的最大级别`
`#define default_console_loglevel (console_printk[3])//终端的最小级别`
`
查看虚拟机终端级别
cat /proc/sys/kernel/printk
只有当消息的级别大于终端级别,消息才会被显示
打印函数代码
#include <linux/init.h>`
`#include <linux/module.h>`
`#include <linux/printk.h>`
`static int __init hello_init(void)`
`{`
` printk("hello world\n");`
` printk(KERN_CRIT "%s %s %d\n",__FILE__,__func__,__LINE__);`
` printk(KERN_INFO "%s %s %d\n",__FILE__,__func__,__LINE__);`
` return 0;`
`}`
`static void __exit hello_exit(void)`
`{`
` printk("bai bai le nin lei\n");`
` printk(KERN_CRIT "%s %s %d\n",__FILE__,__func__,__LINE__);`
` printk(KERN_INFO "%s %s %d\n",__FILE__,__func__,__LINE__);`
`}`
`module_init(hello_init); //入口:申请资源`
`module_exit(hello_exit); //出口:释放资源`
`MODULE_LICENSE("GPL"); //许可证:公共许可协议(开源协议)`
`
但对与咱们的这个Ubuntu被开发者修改过,所有消息不会主动回显。
命令 的 使用
sudo insmod hello.ko 安装驱动模块
sudo rmmod hello 卸载驱动模块
lsmod 查看模块
dmesg 查看消息
sudo dmesg -C 直接清空消息不回显
sudo dmesg -c 回显后清空
修改系统的默认级别
su root
echo 4 3 1 7 > /proc/sys/kernel/printk
虚拟机的默认情况:
修改开发板的默认级别
查看开发板的默认级别
cat /proc/sys/kernel/printk
如果想修改开发板对应的打印级别
vi etc/init.d/rcS
echo 5 5 1 7 > /proc/sys/kernel/printk
//当系统重新启动,rcS中的命令会全部重新执行一遍
在rootfs/etc/init.d/rcS里面添加上以后再起板子,板子的级别就为如下:
etc/init.d/rcS:一些启动虚拟机需要启动的东西都可以放在这个文件中,启动系统时同时启动。
echo 5 5 1 7 > /proc/sys/kernel/printk
放到板子跟文件系统对应这个文件中。
安装驱动和卸载驱动时,消息会打印。