【Linux】内核打印函数`printk`详解

在这里插入图片描述

在Linux内核开发过程中,printk是一个极其重要的函数,用于将信息输出到内核日志中。通过printk,开发者可以在内核中打印调试信息、错误信息以及其他类型的日志,这对于诊断问题、追踪执行流程以及监控系统状态都非常有帮助。本文将详细介绍printk的基本用法、日志级别管理、性能影响以及如何有效地使用printk来增强内核开发的体验。

1. printk简介

printk是Linux内核提供的用于输出信息到内核日志的标准接口。它可以用来打印调试信息、错误信息等,是内核开发中不可或缺的工具之一。

2. printk的基本用法

2.1 函数原型

printk的函数原型如下:

int printk(const char *fmt, ...);
  • fmt:格式化字符串,类似于printf中的格式化字符串。
  • ...:要打印的数据,按照fmt指定的方式进行格式化。
2.2 基本示例

一个简单的printk使用示例:

printk(KERN_INFO "Hello, World!\n");

这里的KERN_INFO是日志级别标志,用于指定消息的重要性。

3. printk的日志级别

printk支持多种日志级别,每种级别都有其特定的意义,可以用来区分消息的重要性。常见的日志级别如下:

  • KERN_EMERG:紧急(Emergency)
  • KERN_ALERT:警告(Alert)
  • KERN_CRIT:严重错误(Critical)
  • KERN_ERR:错误(Error)
  • KERN_WARNING:警告(Warning)
  • KERN_NOTICE:通知(Notice)
  • KERN_INFO:信息(Informational)
  • KERN_DEBUG:调试(Debug)

日志级别决定了消息是否会被记录下来,以及在记录时的优先级。较低级别的日志可能会在高负载情况下被丢弃。

4. 条件化输出

在编写驱动或其他内核模块时,通常需要根据不同的条件来决定是否输出调试信息。为此,可以使用宏定义来控制是否打印调试信息:

MODULE_PARAM(debug, bool, 0644);
static bool debug;#define MY_DBG(fmt, args...) \do { \if (debug) \printk(KERN_DEBUG fmt, ##args); \} while (0)MY_DBG("Debugging information: %s\n", "details");

这样,在不需要调试信息的情况下,可以禁用调试输出以减少内核日志的大小。

5. 提高性能

虽然printk对于调试非常有用,但是在性能敏感的地方频繁使用printk可能会导致性能下降。以下是一些提高printk性能的方法:

5.1 条件输出

只有在确实需要的时候才调用printk

if (value > threshold) {printk(KERN_INFO "Value exceeded threshold: %d\n", value);
}
5.2 限制频率

如果某个条件频繁满足,可以限制printk的输出频率:

static unsigned long last_print_time;void print_if_needed(const char *fmt, ...)
{unsigned long now = jiffies;va_list args;if (now - last_print_time < HZ) {return;}last_print_time = now;va_start(args, fmt);vprintk(KERN_INFO, fmt, args);va_end(args);
}print_if_needed("Message printed too often\n");
5.3 使用vprintk

对于含有大量参数的情况,使用vprintk可以避免不必要的参数解析:

va_list args;
char buffer[1024];
snprintf(buffer, sizeof(buffer), "%s", "A very long format string");
va_start(args, buffer);
vprintk(KERN_INFO, buffer, args);
va_end(args);

6. printk宏定义

Linux内核提供了一系列宏来简化printk的使用,并且自动包含了日志级别:

  • pr_emerg(fmt, args...):紧急
  • pr_alert(fmt, args...):警告
  • pr_crit(fmt, args...):严重错误
  • pr_err(fmt, args...):错误
  • pr_warn(fmt, args...):警告
  • pr_notice(fmt, args...):通知
  • pr_info(fmt, args...):信息
  • pr_debug(fmt, args...):调试

使用这些宏可以更清晰地表达意图,并且易于维护:

pr_err("Error occurred: %s\n", strerror(errno));

7. 示例代码

下面是一个具体的示例,展示了如何在Linux设备驱动中使用printk来记录调试信息。

7.1 定义设备结构
#define DEVICE_NAME_LEN 32
struct my_device {struct cdev cdev;struct class *class;struct device *device;dev_t devno;int state; // 设备状态
};
7.2 初始化模块
static int __init my_device_init(void)
{struct my_device *dev;int ret;dev = kzalloc(sizeof(struct my_device), GFP_KERNEL);if (!dev)return -ENOMEM;// 分配设备号alloc_chrdev_region(&dev->devno, 0, 1, "my_device");// 初始化字符设备dev->cdev.owner = THIS_MODULE;dev->cdev.ops = &my_device_fops;cdev_init(&dev->cdev, &my_device_fops);ret = cdev_add(&dev->cdev, dev->devno, 1);if (ret)goto err_free_dev;// 创建设备类dev->class = class_create(THIS_MODULE, "my_device_class");if (IS_ERR(dev->class)) {ret = PTR_ERR(dev->class);goto err_free_cdev;}// 创建设备实例dev->device = device_create(dev->class, NULL, dev->devno, NULL, "my_device");if (IS_ERR(dev->device)) {ret = PTR_ERR(dev->device);goto err_free_class;}// 记录初始化信息pr_info(KERN_INFO "Driver initialization completed.\n");return 0;err_free_class:class_destroy(dev->class);
err_free_cdev:cdev_del(&dev->cdev);
err_free_dev:kfree(dev);return ret;
}module_init(my_device_init);
7.3 文件操作结构体
static const struct file_operations my_device_fops = {.owner       = THIS_MODULE,.open        = my_device_open,.release     = my_device_release,.unlocked_ioctl = my_device_ioctl,.compat_ioctl = my_device_compat_ioctl,
};static int my_device_open(struct inode *inode, struct file *file)
{struct my_device *dev = container_of(inode->i_cdev, struct my_device, cdev);// 记录设备打开信息pr_info(KERN_INFO "Device opened.\n");return 0;
}static int my_device_release(struct inode *inode, struct file *file)
{struct my_device *dev = container_of(inode->i_cdev, struct my_device, cdev);// 记录设备关闭信息pr_info(KERN_INFO "Device closed.\n");return 0;
}
7.4 实现ioctl处理函数
static long my_device_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = container_of(filp->f_path.dentry->d_inode->i_cdev, struct my_device, cdev);int ret = -EINVAL;switch (cmd) {case MY_IOCTL_OPEN:dev->state = 1; // 设备开启ret = 0;break;case MY_IOCTL_CLOSE:dev->state = 0; // 设备关闭ret = 0;break;case MY_IOCTL_GET_STATE:if (copy_to_user((int *)arg, &dev->state, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (copy_from_user(&dev->state, (int *)arg, sizeof(int))) {ret = -EFAULT;} else {ret = 0;}break;default:ret = -ENOTTY;break;}// 记录ioctl命令处理信息pr_info(KERN_INFO "ioctl command processed: %x\n", cmd);return ret;
}static long my_device_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{struct my_device *dev = container_of(filp->f_path.dentry->d_inode->i_cdev, struct my_device, cdev);int ret = -EINVAL;union {int int_val;long long_val;} uval;switch (cmd) {case MY_IOCTL_GET_STATE:uval.int_val = dev->state;if (put_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {ret = 0;}break;case MY_IOCTL_SET_STATE:if (get_user(uval.long_val, (long *)arg)) {ret = -EFAULT;} else {dev->state = uval.int_val;ret = 0;}break;default:ret = my_device_ioctl(filp, cmd, arg);break;}// 记录compat ioctl命令处理信息pr_info(KERN_INFO "compat ioctl command processed: %x\n", cmd);return ret;
}
7.5 清理模块
static void __exit my_device_exit(void)
{struct my_device *dev = container_of(cdev, struct my_device, cdev);// 记录清理信息pr_info(KERN_INFO "Driver cleanup started.\n");// 删除设备实例device_destroy(dev->class, dev->devno);// 销毁设备类class_destroy(dev->class);// 注销字符设备unregister_chrdev_region(dev->devno, 1);// 释放设备结构kfree(dev);// 记录清理完成信息pr_info(KERN_INFO "Driver cleanup completed.\n");
}module_exit(my_device_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("A simple device driver with printk support.");

8. 用户空间示例

下面是一个简单的用户空间应用程序示例,展示了如何通过ioctl来控制设备,并通过dmesg查看内核日志。

8.1 用户空间程序
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>#define MY_IOCTL_MAGIC 'M'#define MY_IOCTL_OPEN _IO(MY_IOCTL_MAGIC, 0)
#define MY_IOCTL_CLOSE _IO(MY_IOCTL_MAGIC, 1)
#define MY_IOCTL_GET_STATE _IOR(MY_IOCTL_MAGIC, 2, int)
#define MY_IOCTL_SET_STATE _IOW(MY_IOCTL_MAGIC, 3, int)int main()
{int fd;int state = 0;// 打开设备文件fd = open("/dev/my_device", O_RDWR);if (fd == -1) {perror("Failed to open device");return 1;}// 开启设备if (ioctl(fd, MY_IOCTL_OPEN) == -1) {perror("Failed to open device");close(fd);return 1;}// 设置设备状态state = 1;if (ioctl(fd, MY_IOCTL_SET_STATE, &state) == -1) {perror("Failed to set device state");close(fd);return 1;}// 获取设备状态if (ioctl(fd, MY_IOCTL_GET_STATE, &state) == -1) {perror("Failed to get device state");close(fd);return 1;}printf("Device state: %d\n", state);// 关闭设备if (ioctl(fd, MY_IOCTL_CLOSE) == -1) {perror("Failed to close device");close(fd);return 1;}// 关闭文件描述符close(fd);return 0;
}
8.2 查看内核日志

运行用户空间程序后,可以通过dmesg命令查看内核日志,确认是否有调试信息输出。

./test_ioctl
dmesg | grep "ioctl"

9. 总结

printk作为Linux内核开发中的一个重要工具,对于记录调试信息、错误信息以及监控系统状态具有不可替代的作用。通过合理选择日志级别、使用宏定义简化输出、优化性能以及结合其他调试工具和技术,开发者可以有效地利用printk来提高内核模块的质量和性能。希望上述内容能帮助读者更好地理解和掌握Linux内核中的printk使用技巧,提升内核开发的效率和可靠性。在实际开发中,可以根据具体需求灵活运用这些技巧,确保内核模块的稳定性和高效性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/34178.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

校园失物招领借助 SpringBoot:塑造校园互助寻物新风尚

3系统分析 3.1可行性分析 开发者在进行开发系统之前&#xff0c;都需要进行可行性分析&#xff0c;保证该系统能够被成功开发出来。 3.1.1技术可行性 开发该校园失物招领系统所采用的技术是vue和MYSQL数据库。计算机专业的学生在学校期间已经比较系统的学习了很多编程方面的知识…

SpringCloud微服务学习笔记(二)_Docker

文章目录 docker拆封原则docker在linux上的安装部署mysql部署mysql命令解读 常见命令数据卷挂载以宿主目录自定义挂载Dockerfile语法自定义镜像容器网络互联部署java应用,后端部署前端DockerCompose(快速部署) docker 简化环境配置流程 单体架构 架构简单部署成本低团队协作成…

02.ABAP工作台介绍

总学习目录请点击下面连接 SAP ABAP开发从0到入职&#xff0c;冷冬备战-CSDN博客 目录 1.知识库和对象浏览器 目的&#xff1a; 知识库有那些对象 知识库对象结构 如何去知识库查找对象 第一个是知识库信息系统 2.知识库和对象浏览器操作 3.开发abap程序 创建新的AB…

【Axure视频教程】中继器表格——筛选后的条件判断

今天教大家在Axure制作中继器表格筛选以及筛选后条件交互的原型模板&#xff0c;我们可以在输入框里输入员工姓名&#xff0c;点击查询按钮后可以筛选出对应的数据&#xff0c;筛选后会进行条件判断&#xff0c;如果筛选不到任何数据&#xff0c;就会显示提示弹窗。这个原型模板…

华为ensp中nat server 公网访问内网服务器

作者主页&#xff1a;点击&#xff01; ENSP专栏&#xff1a;点击&#xff01; 创作时间&#xff1a;2024年4月15日17点30分 &#x1f4af;趣站推荐&#x1f4af; 前些天发现了一个巨牛的&#x1f916;人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;…

Redis 可以代替 MySQL 作为数据库吗?

当使用Redis作为数据库时&#xff0c;以下是一些基本的代码示例 展示了如何使用Redis进行数据存储、读取和更新&#xff1a; 1.连接到Redis服务器&#xff1a; import redis# 创建Redis连接 r redis.Redis(hostlocalhost, port6379, db0)2.存储和获取数据&#xff1a; # 存…

C语言(一维数组练习)

键盘录入一组数列&#xff0c;利用冒泡排序将数据由大到小排序 #include <stdio.h>int main(int argc,char *argv[]) {int i,j,tmep;int arr[10];printf("请输入10个测试整数&#xff1a;\n");int lensizeof(arr)/sizeof(arr[0]);for(i0;i<len;i){scanf(&q…

【C++】动态内存管理

目录 一、C/C的内存分布 &#x1f354;内存的划分 &#x1f35f;C语言动态内存管理方式 &#x1f32e;C动态内存管理方式 二、new 和 delete &#x1f354;new 和 delete 操作自定义类型 &#x1f35f;operator new 与 operator delete 函数 &#x1f32e;面试题&#x…

开发者如何使用GCC提升开发效率Opencv人脸检测

阅读此篇前请务必阅读以下两篇&#xff0c;不然你可能都不会安装环境 文章目录 简单人脸识别第三方模型集成人脸检测 简单人脸识别 使用Opencv自带的haarcascade_frontalface_alt2模型&#xff0c;始终差一个人没标出来&#xff0c;很尴尬 报错找不到模型 EveryThing搜索一遍…

梯度提升树(GBDT)与房价预测案例

文章目录 什么是梯度提升树&#xff08;GBDT&#xff09;&#xff1f;核心思想GBDT 的特点 梯度提升树的应用案例&#xff1a;房价预测场景描述步骤详解代码详情 详细代码讲解1. 导入必要的库2. 设置中文字体支持3. 可视化真实值与预测值4. 可视化预测误差分布5. 代码的运行效果…

VMware详细安装教程

1.获取安装包 官网下载地址&#xff1a; https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html 2.安装过程 双击安装包进行安装 接受许可协议 选择安装路径&#xff08;c 盘空间大的话&#xff0c;建议保持默认&#xff09; 用户体验设置&…

STM32 软件模拟I2C 操作时序图

m24c02芯片手册可以查看时序图 起始和停止信号 写一个字节&#xff1a; 主发送起始-》 CPU 芯片内部的I2C 控制器&#xff08;片上外设主设备&#xff09;-》发送起始信号-》发送设备地址-》EEPROM&#xff08;从设备&#xff09;返回回应信号-》主发字节起始地址-》从回应-》…

2024年软件测试面试题(800道)【附带答案】持续更新...

新的一年又到了立flag的时候&#xff0c;今年你的目标是拿下大厂offer&#xff1f;还是多少万年薪&#xff1f;其实这些都离不开日积月累的过程。 为此我特意整理出一份&#xff08;超详细笔记/面试题&#xff09;它几乎涵盖了所有的测试开发技术栈&#xff0c;非常珍贵&#…

jdk1.7 发送post请求

所需依赖为 引入依赖为 import org.apache.commons.httpclient.HttpClient; import org.apache.commons.httpclient.methods.PostMethod;特别说明 tmpString为json对象 System.out.println("tmpString " tmpString);// 处理发送https 错误问题SSLContext context …

鸿蒙音乐播放器(超详细)

基于API9的音乐播放器&#xff0c;可播放&#xff0c;暂停&#xff0c;上&#xff0c;下一首歌曲切换等功能 文章目录 1.效果展示 2.首页 3.播放页 注&#xff1a;需要使用模拟器或真机调试运行 一、效果展示 1.首页样式&#xff1a; 2.播放样式&#xff1a; 二、首页功能代码…

基于SQL数据库的酒店管理系统

一、数据库设计 1&#xff0e;需求分析 客房的预定&#xff1a;可以通过网络进行预定&#xff0c;预定修改&#xff0c;取消预订。 客房管理&#xff1a;预定管理、客房查询、设置房态、开房、换房、续住、退房等管理。 员工管理: 员工修改信息、人员调配。 账务管理&…

绿色智能:AI机器学习在环境保护中的深度应用与实践案例

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

2.vue常用指令

2.vue常用指令 目录 1. vue的基础语法结构2. 插值语法3. vue常用指令 3.1 v-html语法结构3.2 v-text3.3 v-bind 绑定动态属性3.4 v-on 添加事件 语法格式3.4.1事件绑定3.4.2 获取event对象3.4.3 动态事件缩写3.4.5 事件绑定的修饰符 3.5 v-if v-else-if v-else5.6 v-show5.7 …

《Solana 中文开发教程》学习笔记(Part 1~4)

Part 1 &#xff1a;Solana介绍 2017年11月&#xff0c;Anatoly Yakovenko发表了一篇白皮书&#xff0c;介绍了“Proof of History”这一技术 Part 2 &#xff1a;Solana介绍 Account 在Solana中&#xff0c;"Everythin is an Account" 类似Linux世界里面把所有的…

基于触觉感知的目标识别技术在智能机器人抓取中的应用综述

引言在智能机器人抓取任务中&#xff0c;目标识别是一个核心问题。机器人不仅需要识别物体&#xff0c;还需根据物体的形状、纹理、硬度等信息来规划抓取策略。传统的目标识别方法通常依赖于视觉信息&#xff0c;但在许多复杂环境中&#xff0c;单一视觉信息往往不能提供足够的…