Linux驱动交叉编译把驱动文件放入开发板,以及printk函数打印级别
上一篇介绍了一个最简单的驱动程序和驱动程序大体结构,但那还是用本地编译只能在Ubuntu上运行,我们该怎么编译一个能加载到开发板上呢,就需要交叉编译,交叉编译通常都是在嵌入式开发中使用到的。
交叉编译
理解交叉编译前先了解下本地编译:是指编译源代码的平台和执行源代码编译后程序的平台是同一个平台。例如在x86平台下编译的程序,就只能在x86平台下运行。
而我们现在是在Ubuntu下(x86)编译,到ARM开发板(arm)上去运行自然不行,所以交叉编译:是指编译源代码的平台和执行源代码编译后程序的平台是两个不同的平台,其中运行编译程序称为宿主机,运行编译程序所产生目标代码的称为目标机。
那为什么不在ARM开发板上编译程序呢,这样就不用转来转去了。之所以要有交叉编译,主要原因是:
1、目标机的运行速度往往比宿主机慢得多,许多专用的嵌入式硬件被设计为低成本和低功耗,没有太高的性能。
2、整个编译过程是非常消耗资源的,嵌入式系统往往没有足够的内存或磁盘空间。
3、 一个完整的Linux编译环境需要很多支持包,交叉编译使我们不需要花时间将各种支持包移植到目标机上。
交叉编译说完了,那怎么宿主机怎么给程序进行交叉编译呢,我们是站在巨人的肩膀上,自然是有现成的工具叫交叉编译器。
根据每个人使用的开发板不同需要下载不同的交叉编译器,大家可自行百度。
其实我们只需要在Makefile中指明交叉编译器的路径就行了,我们来看一个例子。
ifeq ($(KERNELRELEASE),)#内核源代码路径
KERNELDIR ?= /home/xin/6818GEC/kernel
#交叉编译器路径
CROSS_PATH := /home/xin/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
#模块源代码路径
PWD := $(shell pwd)default:$(MAKE) CROSS_COMPILE=$(CROSS_PATH) -C $(KERNELDIR) M=$(PWD) modules
clean:rm -rf *.o *.ko *.mod .*.cmd *.mod.* modules.order Module.symvers .tmp_versionselse
#obj-m表示编译生成可加载模块,obj-y表示直接将模块编译进内核。
obj-m := hello.oendif
这里面的参数上一篇详细解释过了,看不懂可以去看看(初学者的第一个Linux驱动)。其中开发板内核源代码路径和交叉编译器路径需要根据自己的存放位置去改变。
这是Ubuntu上开发板内核源代码的路径和内容。

这是Ubuntu上交叉编译器的路径和内容。 路径中只需要交叉编译器的前缀arm-eabi-

内核打印函数 printk
正常当我们在写应用程序时,都会使用printf函数或相关的打印函数来输出信息,帮助我们调试代码或者打印日志。那内核的驱动程序又没有应用层的库函数,这时候就需要使用我们的printk函数了。先来看一段代码和现象。
#include <linux/init.h>
#include <linux/module.h>//加载函数
int printktest_init(void)
{//内核打印语句printk("<0>""printk level 0!\n");printk("<1>""printk level 1!\n");printk("<2>""printk level 2!\n");printk("<3>""printk level 3!\n");printk("<4>""printk level 4!\n");printk("<5>""printk level 5!\n");printk("<6>""printk level 6!\n");printk("<7>""printk level 7!\n");printk("printk no level!\n");return 0;
}//卸载函数
void printktest_exit(void)
{printk("<0>""printk level 0!\n");printk("<1>""printk level 1!\n");printk("<2>""printk level 2!\n");printk("<3>""printk level 3!\n");printk("<4>""printk level 4!\n");printk("<5>""printk level 5!\n");printk("<6>""printk level 6!\n");printk("<7>""printk level 7!\n");printk("printk no level!\n");
}//声明为模块的入口和出口
module_init(printktest_init);
module_exit(printktest_exit);MODULE_LICENSE("GPL");//GPL模块许可证
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("1.0");//版本
MODULE_DESCRIPTION("printk module!");//描述信息

我们发现代码printk中有0~7,8个数字,而我们加载模块却只打印前5条语句,这是为什么呢?
之前我们说过printk和printf等打印函数用法十分相似,但printk多了打印级别的设置。内核打印通过printk函数,printk打印的内容能否显示取决于打印级别。
printk函数有8个级别,0-7(数字越小优先级越高)
#define KERN_EMERG "<0>" /*系统不可用信息*/
#define KERN_ALERT "<1>" /* 必须立即处理的错误*/
#define KERN_CRIT "<2>" /*严重的错误信息*/
#define KERN_ERR "<3>" /*错误信息*/
#define KERN_WARNING "<4>" /*警告信息*/
#define KERN_NOTICE "<5>" /*需要注意的情况信息*/
#define KERN_INFO "<6>" /*普通信息*/
那什么级别的prink函数中的内容才能显示呢。在Linux中有一个文件用来存放内核默认的打印级别。/proc/sys/kernel/printk,其内容为:

解释一下其中数字含义
5 表示内核打印级别(只有printk打印级别高于5才能显示)
7 表示printk函数默认打印级别(使用printk函数不设置打印级别默认为7)
1 内核打印级别最小值
7 默认内核打印级别
通常只需要改变内核打印级别比printk低就行了。
我们可以直接修改里面的内容。

但这种方法在系统关机或重启后就会失效。我们可以写一个脚本在每次启动时去修改里面的值。有两个方法实现永久修改。
方法1:
写一个shell脚本,内容很简单

然后再放到环境变量(/etc/profile)中去。内容为source 路径/printk.sh。

这样每次启动开发板就都会重新把内容写入/proc/sys/kernel/printk文件里了。
方法2:
在uboot的bootargs中加入“loglevel=X”的语句。首先进入uboot界面。这个可能每个开发板的操作都不太一样,这里演示一下GEC6818。

设置完了要记得保存一下,不然不生效。
通过以上两种方法都能成功设置打印级别。
![]()
我们再从新编译加载一下模块,看看效果吧。
Makefile
ifeq ($(KERNELRELEASE),)#内核源代码路径
KERNELDIR ?= /home/xin/6818GEC/kernel
#交叉编译器路径
CROSS_PATH := /home/xin/6818GEC/prebuilts/gcc/linux-x86/arm/arm-eabi-4.8/bin/arm-eabi-
#模块源代码路径
PWD := $(shell pwd)default:$(MAKE) CROSS_COMPILE=$(CROSS_PATH) -C $(KERNELDIR) M=$(PWD) modules
iclean:rm -rf *.o *.ko *.mod .*.cmd *.mod.* modules.order Module.symvers .tmp_versionselse
#obj-m表示编译生成可加载模块,obj-y表示直接将模块编译进内核。
obj-m := print.oendif
print.c
#include <linux/init.h>
#include <linux/module.h>//加载函数
int printktest_init(void)
{//内核打印语句printk("<0>""printk level 0!\n");printk("<1>""printk level 1!\n");printk("<2>""printk level 2!\n");printk("<3>""printk level 3!\n");printk("<4>""printk level 4!\n");printk("<5>""printk level 5!\n");printk("<6>""printk level 6!\n");printk("<7>""printk level 7!\n");printk("printk no level!\n");return 0;
}//卸载函数
void printktest_exit(void)
{printk("<0>""printk level 0!\n");printk("<1>""printk level 1!\n");printk("<2>""printk level 2!\n");printk("<3>""printk level 3!\n");printk("<4>""printk level 4!\n");printk("<5>""printk level 5!\n");printk("<6>""printk level 6!\n");printk("<7>""printk level 7!\n");printk("printk no level!\n");
}//声明为模块的入口和出口
module_init(printktest_init);
module_exit(printktest_exit);MODULE_LICENSE("GPL");//GPL模块许可证
MODULE_AUTHOR("xin");//作者
MODULE_VERSION("1.0");//版本
MODULE_DESCRIPTION("printk module!");//描述信息

以上就是Linux驱动交叉编译把驱动文件放入开发板,以及printk函数打印级别的全部内容,有什么说的不对或者觉得不清楚地方欢迎在评论区提出来。
相关文章:
Linux驱动交叉编译把驱动文件放入开发板,以及printk函数打印级别
上一篇介绍了一个最简单的驱动程序和驱动程序大体结构,但那还是用本地编译只能在Ubuntu上运行,我们该怎么编译一个能加载到开发板上呢,就需要交叉编译,交叉编译通常都是在嵌入式开发中使用到的。 交叉编译 理解交叉编译前先了解…...
力扣(LeetCode)433. 最小基因变化(2023.03.07)
基因序列可以表示为一条由 8 个字符组成的字符串,其中每个字符都是 ‘A’、‘C’、‘G’ 和 ‘T’ 之一。 假设我们需要调查从基因序列 start 变为 end 所发生的基因变化。一次基因变化就意味着这个基因序列中的一个字符发生了变化。 例如,“AACCGGTT”…...
网络基础(2)
目录1. 端口号2. 套接字socket3. 网络通信3.1 sockaddr与sockaddr_in3.2 接口服务端3.2.1 创建套接字,打开网络文件3.2.2 给该服务器绑定端口和ip(特殊处理)3.2.3 初始化相关服务器3.2.4 提供服务客户端3.2.5 绑定3.2.6 使用服务4. makefile实…...
掌握Spring Cloud Gateway:构建高性能API网关的原理和实践
Spring Cloud Gateway 是一个基于 Spring Boot 的 API 网关,用于构建微服务架构中的网关服务。它提供了统一的路由、请求转发、过滤器、负载均衡、熔断等功能,帮助开发者更好地管理和控制微服务系统的请求流量。 本文将介绍 Spring Cloud Gateway 的原理…...
NAST概述
一、NATS介绍 NATS是由CloudFoundry的架构师Derek开发的一个开源的、轻量级、高性能的,支持发布、订阅机制的分布式消息队列系统。它的核心基于EventMachine开发,代码量不多,可以下载下来慢慢研究。 不同于Java社区的kafka,nats…...
【JS知识点】——原型和原型链
文章目录原型和原型链构造函数原型显式原型(prototype)隐式原型(\_\_proto\_\_)原型链总结原型和原型链 在js中,原型和原型链是一个非常重要的知识点,只有理解原型和原型链,才能深刻理解JS。在…...
c盘怎么清理到最干净?有什么好的清理方法
c盘怎么清理到最干净?有什么好的清理方法?清理C盘空间是电脑维护的重要步骤之一。C盘是Windows操作系统的核心部分,保存了许多重要的系统文件,因此空间不足会影响计算机的性能和稳定性。下面是一些清理C盘空间的方法 一.清理临时文件 在使用…...
day26_HTML
今日内容 上课同步视频:CuteN饕餮的个人空间_哔哩哔哩_bilibili 同步笔记沐沐霸的博客_CSDN博客-Java2301 零、 复习昨日 一、二阶段介绍 二、HTML 零、 复习昨日 见代码 一、二阶段介绍 第一阶段: 基础入门 java基本语法编程基础(方法,数组)面向对象编程常用类高级(IO,线程,新…...
深度剖析C语言预处理
致前行的人: 人生像攀登一座山,而找寻出路,却是一种学习的过程,我们应当在这过程中,学习稳定冷静,学习如何从慌乱中找到生机。 目录 1.程序翻译过程: 2.字符串宏常量 3.用宏定义充当注释符号 4…...
【WPF 值转换器】ValueConverter 进阶用法
【WPF 值转换器】ValueConverter 进阶用法介绍基类实现子类实现效果介绍 值转换器在WPF开发中是非常常见的,当然不仅仅是在WPF开发中。值转换器可以帮助我们很轻松地实现,界面数据展示的问题,如:模块隐藏显示、编码数据展示为可读…...
Vue2的基本使用
一、vue的基本使用 第一步 引入vue.js文件 <script src"https://cdn.staticfile.org/vue/2.7.0/vue.min.js"></script> 或者<script src"./js/vue.js"></script> 第二步 在body中设置一个挂载点 {{msg}} <div id"app…...
【云原生kubernetes】k8s数据存储之Volume使用详解
目录 一、什么是Volume 二、k8s中的Volume 三、k8s中常见的Volume类型 四、Volume 之 EmptyDir 4.1 EmptyDir 特点 4.2 EmptyDir 实现文件共享 4.2.1 关于busybox 4.3 操作步骤 4.3.1 创建配置模板文件yaml 4.3.2 创建Pod 4.3.3 访问nginx使其产生访问日志 4.3.4 …...
SerDes---CDR技术
1、为什么需要CDR 时钟数据恢复主要完成两个工作,一个是时钟恢复,一个是数据重定时,也就是数据的恢复。时钟恢复主要是从接收到的 NRZ(非归零码)码中将嵌入在数据中的时钟信息提取出来。 2、CDR种类 PLL-Based CDROve…...
如何实现在on ethernetPacket中自动回复NDP response消息
对于IPv4协议来说,如果主机想通过目标ipv4地址发送以太网数据帧给目的主机,需要在数据链路层填充目的mac地址。根据目标ipv4地址查找目标mac地址,这是ARP协议的工作原理 对于IPv6协议来说,根据目标ipv6地址查找目标mac地址,它使用的不是ARP协议,而是邻居发现NDP(Neighb…...
CSS清楚浮动
先看看关于浮动的一些性质 浮动使元素脱离文档流 浮动元素可以设置宽高,在CSS中,任何元素都可以浮动,浮动元素会生成一个块级框,而不论其本身是何种元素。 如果没有给浮动元素指定高度,,那么它会以内容的…...
HTTPS详解(原理、中间人攻击、CA流程)
摘要我们访问浏览器也经常可以看到https开头的网址,那么什么是https,什么是ca证书,认证流程怎样?这里一一介绍。原理https就是httpssl,即用http协议传输数据,数据用ssl/tls协议加密解密。具体流程如下图&am…...
EventLoop机制
JavaScript 是单线程的语言 JavaScript 是一门单线程执行的编程语言。也就是说,同一时间只能做一件事情。 单线程执行任务队列的问题: 如果前一个任务非常耗时,则后续的任务就不得不一直等待,从而导致程序假死的问题。 同步任…...
倒立摆建模
前言 系统由一辆具有动力的小车和安装在小车上的倒立摆组成,系统是不稳定,我们需要通过控制移动小车使得倒立摆保持平衡。 具体地,考虑二维情形如下图,控制力为水平力FFF,输出为角度θ\thetaθ以及小车的位置xxx。 力…...
SpringSecurity支持WebAuthn认证
WebAuthn是无密码身份验证技术,解决了密码泄露的风险,主流的浏览器都支持。有很多开源的类库实现了WebAuthn规范,Java下流行的类库有:webauthn4jjava-webauthn-serververtx-authSpring Security官方暂时未支持WebAuthn,…...
深度学习技巧应用3-神经网络中的超参数搜索
大家好,我是微学AI,今天给大家带来深度学习技巧应用3-神经网络中的超参数搜索。 在深度学习任务中,一个算法模型的性能往往受到很多超参数的影响。超参数是指在模型训练之前需要我们手动设定的参数,例如:学习率、正则…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决
Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中,新增了一个本地验证码接口 /code,使用函数式路由(RouterFunction)和 Hutool 的 Circle…...
Vite中定义@软链接
在webpack中可以直接通过符号表示src路径,但是vite中默认不可以。 如何实现: vite中提供了resolve.alias:通过别名在指向一个具体的路径 在vite.config.js中 import { join } from pathexport default defineConfig({plugins: [vue()],//…...
raid存储技术
1. 存储技术概念 数据存储架构是对数据存储方式、存储设备及相关组件的组织和规划,涵盖存储系统的布局、数据存储策略等,它明确数据如何存储、管理与访问,为数据的安全、高效使用提供支撑。 由计算机中一组存储设备、控制部件和管理信息调度的…...
分布式计算框架学习笔记
一、🌐 为什么需要分布式计算框架? 资源受限:单台机器 CPU/GPU 内存有限。 任务复杂:模型训练、数据处理、仿真并发等任务耗时严重。 并行优化:通过任务拆分和并行执行提升效率。 可扩展部署:适配从本地…...
【设计模式】1.简单工厂、工厂、抽象工厂模式
every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 以下是 简单工厂模式、工厂方法模式 和 抽象工厂模式 的 Python 实现与对比,结合代码示例和实际应用场景说明: 1. 简单工厂模式&a…...
Flask+LayUI开发手记(八):通用封面缩略图上传实现
前一节做了头像上传的程序,应该说,这个程序编写和操作都相当繁琐,实际上,头像这种缩略图在很多功能中都会用到,屏幕界面有限,绝不会给那么大空间摆开那么大一个界面,更可能的处理,就…...
【Zephyr 系列 14】使用 MCUboot 实现 BLE OTA 升级机制:构建安全可靠的固件分发系统
🧠关键词:Zephyr、MCUboot、OTA 升级、BLE DFU、双分区、Bootloader、安全固件管理 📌面向读者:希望基于 Zephyr 为 BLE 设备加入安全 OTA 升级功能的开发者 📊预计字数:5200+ 字 🧭 前言:为什么你需要 OTA? 随着设备部署数量增多与产品生命周期延长,远程升级(…...
豆瓣图书评论数据分析与可视化
【题目描述】豆瓣图书评论数据爬取。以《平凡的世界》、《都挺好》等为分析对象,编写程序爬取豆瓣读书上针对该图书的短评信息,要求: (1)对前3页短评信息进行跨页连续爬取; (2)爬取…...
