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-神经网络中的超参数搜索。 在深度学习任务中,一个算法模型的性能往往受到很多超参数的影响。超参数是指在模型训练之前需要我们手动设定的参数,例如:学习率、正则…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...
【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...
【Java学习笔记】Arrays类
Arrays 类 1. 导入包:import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序(自然排序和定制排序)Arrays.binarySearch()通过二分搜索法进行查找(前提:数组是…...
mongodb源码分析session执行handleRequest命令find过程
mongo/transport/service_state_machine.cpp已经分析startSession创建ASIOSession过程,并且验证connection是否超过限制ASIOSession和connection是循环接受客户端命令,把数据流转换成Message,状态转变流程是:State::Created 》 St…...
SCAU期末笔记 - 数据分析与数据挖掘题库解析
这门怎么题库答案不全啊日 来简单学一下子来 一、选择题(可多选) 将原始数据进行集成、变换、维度规约、数值规约是在以下哪个步骤的任务?(C) A. 频繁模式挖掘 B.分类和预测 C.数据预处理 D.数据流挖掘 A. 频繁模式挖掘:专注于发现数据中…...
Qt Widget类解析与代码注释
#include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this); }Widget::~Widget() {delete ui; }//解释这串代码,写上注释 当然可以!这段代码是 Qt …...
c++ 面试题(1)-----深度优先搜索(DFS)实现
操作系统:ubuntu22.04 IDE:Visual Studio Code 编程语言:C11 题目描述 地上有一个 m 行 n 列的方格,从坐标 [0,0] 起始。一个机器人可以从某一格移动到上下左右四个格子,但不能进入行坐标和列坐标的数位之和大于 k 的格子。 例…...
【Java_EE】Spring MVC
目录 Spring Web MVC 编辑注解 RestController RequestMapping RequestParam RequestParam RequestBody PathVariable RequestPart 参数传递 注意事项 编辑参数重命名 RequestParam 编辑编辑传递集合 RequestParam 传递JSON数据 编辑RequestBody …...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
全志A40i android7.1 调试信息打印串口由uart0改为uart3
一,概述 1. 目的 将调试信息打印串口由uart0改为uart3。 2. 版本信息 Uboot版本:2014.07; Kernel版本:Linux-3.10; 二,Uboot 1. sys_config.fex改动 使能uart3(TX:PH00 RX:PH01),并让boo…...
