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-神经网络中的超参数搜索。 在深度学习任务中,一个算法模型的性能往往受到很多超参数的影响。超参数是指在模型训练之前需要我们手动设定的参数,例如:学习率、正则…...
三、FastAPI实战:从POST接口设计到自动化测试脚本的完整闭环
1. 从零开始:为什么需要一个完整的“开发-测试”闭环? 你好,我是老张,一个在前后端领域摸爬滚打了十多年的老码农。不知道你有没有过这样的经历:吭哧吭哧写好了后端接口,信心满满地交给前端或者测试同学&am…...
Hana Studio vs SAP GUI:ABAP开发工具选择指南与实战对比
Hana Studio vs SAP GUI:ABAP开发者的十字路口与实战抉择 在SAP ABAP开发的世界里,工具的选择从来不是一件小事。它关乎你每天敲击键盘的流畅度,关乎调试时能否快速定位到那个恼人的逻辑错误,更关乎在复杂项目压力下,你…...
HS2-HF Patch实战指南:解锁游戏增强功能的5个关键步骤
HS2-HF Patch实战指南:解锁游戏增强功能的5个关键步骤 【免费下载链接】HS2-HF_Patch Automatically translate, uncensor and update HoneySelect2! 项目地址: https://gitcode.com/gh_mirrors/hs/HS2-HF_Patch 副标题:面向中级玩家的功能扩展与…...
2.7 庐山派K230芯片I2C模块API手册:主从模式配置与Python编程实战
庐山派K230芯片I2C模块API手册:主从模式配置与Python编程实战 最近在玩庐山派K230开发板,发现它内置了5个硬件I2C模块,用起来挺方便的。I2C这个通信协议在嵌入式里太常用了,各种传感器、EEPROM存储器、触摸芯片都靠它。但很多朋友…...
3D Face HRN快速部署指南:小白也能一键搭建的人脸重建系统
3D Face HRN快速部署指南:小白也能一键搭建的人脸重建系统 1. 从零开始:为什么选择3D Face HRN? 如果你对3D建模感兴趣,或者工作中需要处理人脸相关的数字内容,那么“从一张照片生成3D人脸”这个需求,你一…...
Docker环境部署Qwen3-VL-WEBUI:图文AI快速启动,小白友好教程
Docker环境部署Qwen3-VL-WEBUI:图文AI快速启动,小白友好教程 1. 为什么你需要试试Qwen3-VL? 如果你对AI的印象还停留在“只会聊天”,那Qwen3-VL可能会颠覆你的认知。想象一下,你拍一张产品设计图,AI不仅能…...
USB PD快充功率监测仪:高精度嵌入式USB电压电流测量终端
第七届立创电赛 USB表:一款面向PD快充场景的高精度USB功率监测终端1. 项目概述USB供电能力的持续增强正深刻改变着电子设备的能源架构。从USB 2.0时代的5V/500mA,到USB PD 3.1规范支持的28V/5A(140W),USB接口已不再仅是…...
[特殊字符] Nano-Banana参数详解:CFG=7.5时提示词敏感度与部件完整性实测
Nano-Banana参数详解:CFG7.5时提示词敏感度与部件完整性实测 1. 项目核心:专为拆解而生的AI引擎 如果你经常需要制作产品说明书、维修指南,或者想用一种酷炫的方式展示产品的内部结构,那你可能听说过“爆炸图”或“平铺拆解图”…...
俄罗斯RT-2PM2“白杨-M“(Topol-M)洲际弹道导弹系统完整技术报告
Comprehensive Technical Report on Russias RT-2PM2 Topol-M Intercontinental Ballistic Missile System 报告日期:2026年3月11日 保密级别:公开来源情报(OSINT) 摘要(Executive Summary) RT-2PM2"…...
深求·墨鉴案例分享:看AI如何精准识别手写笔记,完整保留原始结构
深求墨鉴案例分享:看AI如何精准识别手写笔记,完整保留原始结构 1. 引言:从一堆凌乱笔记到清晰电子文档的烦恼 你有没有过这样的经历?开会时在笔记本上奋笔疾书,密密麻麻记了好几页,会后想整理成电子版&am…...
