滨海做网站的公司,免费网站模板 下载,工程分包网,杭州 网站建设公司排名一、驱动相关概念
1.操作系统的功能 向下管理硬件#xff0c;向上提供接口 操作系统向上提供的接口类型#xff1a; 内存管理#xff1a;内存申请#xff08;malloc#xff09; 内存释放#xff08;free#xff09;等 文件管理#xff1a; 通过文件系统格式对文件ext2…一、驱动相关概念
1.操作系统的功能 向下管理硬件向上提供接口 操作系统向上提供的接口类型 内存管理内存申请malloc 内存释放free等 文件管理 通过文件系统格式对文件ext2、ext3、 ext4格式进行管理 进程管理 进程的创建/调度/消亡 网络管理 通过网络栈协议完成数据的收发 设备管理 字符设备、块设备、网卡设备 2.驱动的概念 2.1.定义 驱动是能够让硬件实现某个特定功能的软件代码根据驱动代码是否使用了系统内核提供的接口将驱动分为裸机驱动和系统驱动 2.2.裸机驱动和系统驱动 裸机驱动不基于操作系统提供的接口完成驱动的编写这种驱动比较简单由开发者独立即可编写完成但是实现的功能比较单一 系统驱动操作系统设备驱动编写驱动时调用系统内核提供的接口驱动程序会被加载到内核空间。系统驱动开发者无法独立完成需要操作系统内核的辅助但是基于系统驱动完成的硬件控制工作会更加完善和复杂。
3.系统驱动操作系统设备驱动在操作系统中的层次 设备驱动会被加载到系统内核空间设备驱动主要完成对硬件功能的实现相当于拓展了系统内核设备管理的功能 4.设备驱动的类型 设备驱动的类型时根据设备类型的不同进行划分的一般将设备分为字符设备、块设备、和网卡设备 三类 字符设备以字节流的形式进行顺序访问的设备叫做字符设备。 90%的设备为字符设备鼠标、键盘、摄像头... 块设备以块为单位进行随机访问的设备叫做块设备。块设备一般是一些磁盘设备 网卡设备用于进行网络数据传输的设备。网卡设备没有网卡设备文件想要读取网卡设备的数据需要基于socket套接字实现
二、内核模块编程
1.内核模块编程的意义 驱动属于内核的一部分驱动资源要加载到内核中所以要按照内核模块的编程去编写框架 2.内核模块的三要素 入口主要进行资源的申请工作安装内核模块时执行 出口主要进行资源的释放工作卸载内核模块时执行 许可证声明当前内核模块遵循GPL协议 3.内核模块编写实例及代码解释
#inckude linux/init.h
#include linux/module.h//入口函数安装内核模块时执行
static int __init mycdev_init(void)
{//static: 修饰当前函数只可在本文件使用//int函数返回值类型如果函数规定了返回值但是没有加返回值编译会报错//mycdev_init函数名可以自己命名//void表示函数无参数void不可以省略//__init告诉编译器当前函数保存在.init.text段 #define __init sectoin(.init.text)//linux内核中有自己的链接脚本文件 vmlinux.lds这个链接脚本文件规定了不同的内容存放在内存中的哪个位置return 0;
}//出口函数卸载内核模块时执行
static void __exit mycdev_exit(void)
{//__exit告诉编译器当前函数保存在.exit.text段 #define __exit section(.exit.text)
}//声明入口函数
module_init(mycdev_init);
//声明出口函数
module_exit(mycdev_exit);
//声明当前内核模块遵循GPL协议
MODULE_LICENSE(GPL);
4.内核模块的编译 编译内核镜像make uImage 编译设备树make dtbs 模块化编译make modules 内核模块编译有两种方式内部编译和外部编译 4.1内部编译 内部编译又称为静态编译需要依赖于内核的源码树 ① 编写内核模块 ② 将内核模块文件移动到内核指定路径下 ③ 在内核指定路径下的Makefile文件中添加编译文件 ④ 修改Kconfig文件添加当前内核模块文件的菜单选项选配项 ⑤ 在源码顶层目录下make menuconfig将当前文件的选配项选配为M保存退出此时.config被修改 ⑥ make modules 进行模块化编译 4.2.外部编译 外部编译又称为动态编译可以在内核路径外单独编译当前模块文件外部编译需要我们自己手写Makefile 通用版本的Makefile
modname ? demo #内核模块名称询问赋值若无对应的外部变量则为demo有则为外部变量
arch ? arm #架构询问赋值若无对应的外部变量则为arm有则为外部变量
ifed ($(arch),arm) #通过命令行是否有对应的外部变量来判断采用什么架构进行编译#KERNRLDIR保存开发板内核源码路径KERNRLDIR : /home/ubuntu/linux.5.10.61/
else#KERNRLDIR保存ubuntu内核源码路径KERNRIDIR :/lib/kernel/$(shell uname -r)/bulid #uname -r命令是查找当前操作系统使用的内核版本
endif#PWD保存当前内核模块的路径
PWD : $(shell pwd)all:#make modules是模块化编译的命令#-C $(KERNELDIR) 表示执行make前到$(KERNELDIR)所保存路径下读取该目录下的Makefile文件执行make编译#M$(PWD) 指定模块化编译的路径进行模块化编译的路径是PWD保存的路径make -C $(KERNELDIR) M$(PWD) modules
clean:#编译删除make -C $(KERNELDIR) m$(PWD) clean#将obj-m保存的文件单独链接生成内核模块
obj-m $(modname).o #此处写.o不写.c虽然当前目录下没有.o文件但是Makefile有自动推导功 能会自动寻找对应.c5.内核模板操作相关命令 安装内核模块命令 insmod *.ko 卸载内核模块命令 rmmod *.ko 后缀名.ko可省略 查看内核已经安装的内核模块 lsmod 查看某个内核模块的信息 modinfo *.ko
三、内核消息打印函数printk函数的使用
1.printk函数的使用格式 printk(格式控制符输出列表) 这种格式消息按照默认输出级别进行输出 或 printk(消息级别 格式控制符输出列表) 2.printk函数消息队列打印级别 prink函数打印的内容属于内核消息。内核消息根据消息的轻重缓急给他们设置不同的消息级别。终端也会存在一个默认的消息级别只有消息级别高于终端默认消息级别消息才可以在终端进行输出消息级别共分为0-7总共8级数字越小代表消息级别越高一般常用3-7级 #define KERN_EMERG KERN_SOH 0 /* system is unusable */ #define KERN_ALERT KERN_SOH 1 /* action must be taken immediately */ #define KERN_CRIT KERN_SOH 2 /* critical conditions */ #define KERN_ERR KERN_SOH 3 /* error conditions */ #define KERN_WARNING KERN_SOH 4 /* warning conditions */ #define KERN_NOTICE KERN_SOH 5 /* normal but significant condition */ #define KERN_INFO KERN_SOH 6 /* informational */ #define KERN_DEBUG KERN_SOH 7 /* debug-level messages */ 3.查看消息默认级别 消息默认级别保存在/proc/sys/kernel/printk文件 查看cat /proc/sys/kernel/printk 4 4 1 7 4 终端默认消息级别 4 printk默认的消息级别 1 终端支持的消息最高级别 7 终端支持的消息最低级别 代码示例
#include linux/init.h
#include linux/module.h// 入口函数安装内核模块时执行
static int __init mycdev_init(void)
{// static 修饰当前函数只能在本文件使用// int 函数的返回值类型如果函数规定返回值但是没有加返回值编译会报错// mycdev_init函数名可以自己起名字// void表示函数无参数当没有参数时void一定要加不然报错//__init的作用是用来告诉编译器当前代码保存在.init.text段中// #define __init __section(.init.text)// linux内核也会有自己的链接脚本 vmlinux.lds,这个链接脚本里规定了不同的内容在// 内存中的什么位置printk(KERN_ERR hello world\n);int a10;printk(KERN_ERR %d\n,a);return 0;
}
// 出口函数卸载内核模块时执行
static void __exit mycdev_exit(void)
{// #define __exit __section(.exit.text)//__exit指定出口函数保存在.exit.text段中
}
// 用于声明入口函数
module_init(mycdev_init);
// 用于声明出口函数
module_exit(mycdev_exit);
// 声明当前内核模块遵循GPL协议
MODULE_LICENSE(GPL); 5.如何修改消息默认级别 ubuntu: 先切换到管理员sudo su 再输入以下内容echo 4 3 1 7 /proc/sys/kernel/printk 注这种修改方式ubuntn重启后级别恢复默认设置的4 4 1 7 开发板上linux 进入根文件系统下的etc下的init.d文件夹cd ~/nfs/rootfs/etc/init.d 打开rcS脚本文件vi rcS 在rcS中最后一行添加echo 4 3 1 7 /proc/sys/kernel/printk
6.ubuntu虚拟终端的使用 切换到虚拟终端ctrlalt[f2-f6](fn) 退出虚拟终端ctrlaltf1(fn)
7.dmesg命令的使用 dmesg查看内核打印消息 dmesg -c先将保存的内核消息打印在终端再清除 dmesg -C直接清除