当前位置: 首页 > wzjs >正文

广西玉林建设厅官方网站谷歌是如何运营的

广西玉林建设厅官方网站,谷歌是如何运营的,免费网站收录提交,扬中论坛网前言 前面两篇我们介绍了printk的基本用法和pr_xxx的用法: Linux Kernel调试:强大的printk(一) Linux Kernel调试:强大的printk(二) 本篇我们来介绍开发驱动程序时用到的dev_xxx&#xff0c…

前言

前面两篇我们介绍了printk的基本用法和pr_xxx的用法:

Linux Kernel调试:强大的printk(一)

Linux Kernel调试:强大的printk(二)

本篇我们来介绍开发驱动程序时用到的dev_xxx,以及限制输出的频率

dev_xxx

内核在include/linux/dev_printk.h中同样定义了一系列的dev_xxx:

/** #defines for all the dev_<level> macros to prefix with whatever* possible use of #define dev_fmt(fmt) ...*/#define dev_emerg(dev, fmt, ...) \dev_printk_index_wrap(_dev_emerg, KERN_EMERG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_crit(dev, fmt, ...) \dev_printk_index_wrap(_dev_crit, KERN_CRIT, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_alert(dev, fmt, ...) \dev_printk_index_wrap(_dev_alert, KERN_ALERT, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_err(dev, fmt, ...) \dev_printk_index_wrap(_dev_err, KERN_ERR, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_warn(dev, fmt, ...) \dev_printk_index_wrap(_dev_warn, KERN_WARNING, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_notice(dev, fmt, ...) \dev_printk_index_wrap(_dev_notice, KERN_NOTICE, dev, dev_fmt(fmt), ##__VA_ARGS__)
#define dev_info(dev, fmt, ...) \dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)#if defined(CONFIG_DYNAMIC_DEBUG) || \(defined(CONFIG_DYNAMIC_DEBUG_CORE) && defined(DYNAMIC_DEBUG_MODULE))
#define dev_dbg(dev, fmt, ...)                                                \dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
#elif defined(DEBUG)
#define dev_dbg(dev, fmt, ...)                                                \dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#else
#define dev_dbg(dev, fmt, ...)                                                \dev_no_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__)
#endif

(这里不再对这些宏进行展开了,需要你自己去查看内核源码)

这些函数本质上也是对 printk 的封装,但相比 printk,它们具有以下优点:

  • 它们都有一个dev参数,这使得它们支持打印模块信息和设备信息:可以自动将设备名称等信息添加到打印信息的前头,便于定位问题。

  • 支持动态调试:可以动态选择打开或关闭某个内核子系统或模块的输出,更加灵活。

我们还是以代码示例来进行演示,写一个虚拟设备的驱动程序,可到这里获取:https://gitee.com/coolloser/linux-kerenl-debug

#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uaccess.h>#define DEVICE_NAME "vdev"
#define CLASS_NAME "vclass"static int major;
static struct class *vdev_class;
static struct cdev vdev_cdev;
static struct device *vdev_device; // 新增设备指针// 设备数据结构
struct vdev_data {char *buffer;size_t size;struct device *dev; // 每个设备实例保存设备指针
};// 打开设备
static int vdev_open(struct inode *inode, struct file *file)
{struct vdev_data *data;data = kmalloc(sizeof(struct vdev_data), GFP_KERNEL);if (!data) {pr_err("Failed to allocate memory\n");return -ENOMEM;}data->buffer = kmalloc(1024, GFP_KERNEL);if (!data->buffer) {pr_err("Failed to allocate buffer\n");kfree(data);return -ENOMEM;}data->size = 0;data->dev = vdev_device; // 保存设备指针file->private_data = data;dev_info(data->dev, "Device opened\n");return 0;
}// 释放设备
static int vdev_release(struct inode *inode, struct file *file)
{struct vdev_data *data = file->private_data;if (data) {kfree(data->buffer);kfree(data);}dev_info(vdev_device, "Device closed\n");return 0;
}// 读取设备
static ssize_t vdev_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
{struct vdev_data *data = file->private_data;if (*pos >= data->size)return 0;if (copy_to_user(buf, data->buffer + *pos, data->size - *pos))return -EFAULT;*pos += data->size - *pos;dev_dbg(data->dev, "Read %zu bytes\n", data->size);return data->size;
}// 写入设备
static ssize_t vdev_write(struct file *file, const char __user *buf, size_t count, loff_t *pos)
{struct vdev_data *data = file->private_data;if (count > 1024) {dev_warn(data->dev, "Write size too big: %zu\n", count);return -EFBIG;}if (copy_from_user(data->buffer, buf, count))return -EFAULT;data->size = count;*pos = count;dev_info(data->dev, "Received %zu bytes\n", count);return count;
}// 文件操作结构体
static const struct file_operations vdev_fops = {.owner = THIS_MODULE,.open = vdev_open,.release = vdev_release,.read = vdev_read,.write = vdev_write,
};// 模块初始化
static int __init vdev_init(void)
{dev_t dev;int ret;// 动态分配主设备号ret = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME);if (ret < 0) {pr_err("Failed to allocate device number\n");return ret;}major = MAJOR(dev);// 创建设备类(修正参数)vdev_class = class_create(CLASS_NAME);if (IS_ERR(vdev_class)) {unregister_chrdev_region(dev, 1);return PTR_ERR(vdev_class);}// 初始化字符设备cdev_init(&vdev_cdev, &vdev_fops);vdev_cdev.owner = THIS_MODULE;// 添加字符设备ret = cdev_add(&vdev_cdev, dev, 1);if (ret < 0) {class_destroy(vdev_class);unregister_chrdev_region(dev, 1);return ret;}// 创建设备节点并保存设备指针vdev_device = device_create(vdev_class, NULL, dev, NULL, DEVICE_NAME);if (IS_ERR(vdev_device)) {cdev_del(&vdev_cdev);class_destroy(vdev_class);unregister_chrdev_region(dev, 1);return PTR_ERR(vdev_device);}dev_info(vdev_device, "Device initialized\n");return 0;
}// 模块退出
static void __exit vdev_exit(void)
{dev_t dev = MKDEV(major, 0);device_destroy(vdev_class, dev);class_destroy(vdev_class);cdev_del(&vdev_cdev);unregister_chrdev_region(dev, 1);pr_info("Device unloaded\n");
}module_init(vdev_init);
module_exit(vdev_exit);MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Simple virtual device driver demo");

(看不懂不要紧,我们只是关注其中的dev_xxx调用)

# 定义模块名称
MODULE_NAME := dev_xxx# 定义内核构建目录,替换成你自己的路径
KERNEL_BUILD_DIR := /home/leo/debug_kernel/linux-6.12.28# 定义目标文件
obj-m += $(MODULE_NAME).occflags-y += -DDEBUG# 默认目标
all:@echo "Building the $(MODULE_NAME) kernel module..."$(MAKE) -C $(KERNEL_BUILD_DIR) M=$(PWD) modules# 清理目标
clean:@echo "Cleaning up the build environment..."$(MAKE) -C $(KERNEL_BUILD_DIR) M=$(PWD) clean

执行make进行编译,然后加载模块,通过dmesg查看log:

能看到log前面加入了vclass_vdev

然后我们进行如下操作:

sudo mknod /dev/vdev c 240 0
sudo chmod 666 /dev/vdev
sudo echo "test" > /dev/vdev
sudo cat /dev/vdev

再执行dmesg查看log:

同样也插入了设备信息

限制输出的频率

有时候在一个函数中进行打印以方便查看log,但是这个函数被无限次调用,就会输出一堆信息,不但影响我们定位问题,还会对内核的运行产生影响,所以内核定义了如下接口:

#ifdef CONFIG_PRINTK
#define printk_ratelimited(fmt, ...)                                        \
({                                                                        \static DEFINE_RATELIMIT_STATE(_rs,                                \DEFAULT_RATELIMIT_INTERVAL,        \DEFAULT_RATELIMIT_BURST);                \\if (__ratelimit(&_rs))                                                \printk(fmt, ##__VA_ARGS__);                                \
})
#else
#define printk_ratelimited(fmt, ...)                                        \no_printk(fmt, ##__VA_ARGS__)
#endif#define pr_emerg_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_notice_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
/* no pr_cont_ratelimited, don't do that... */// 精简后的
#define pr_devel_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)// 精简后的
#define pr_debug_ratelimited(fmt, ...)                                        \printk_ratelimited(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)

以及

#define dev_emerg_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_emerg, dev, fmt, ##__VA_ARGS__)
#define dev_alert_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_alert, dev, fmt, ##__VA_ARGS__)
#define dev_crit_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_crit, dev, fmt, ##__VA_ARGS__)
#define dev_err_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_err, dev, fmt, ##__VA_ARGS__)
#define dev_warn_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_warn, dev, fmt, ##__VA_ARGS__)
#define dev_notice_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_notice, dev, fmt, ##__VA_ARGS__)
#define dev_info_ratelimited(dev, fmt, ...)                                \dev_level_ratelimited(dev_info, dev, fmt, ##__VA_ARGS__)// 精简后的
#define dev_dbg_ratelimited(dev, fmt, ...)                                \
do {                                                                        \static DEFINE_RATELIMIT_STATE(_rs,                                \DEFAULT_RATELIMIT_INTERVAL,        \DEFAULT_RATELIMIT_BURST);                \if (__ratelimit(&_rs))                                                \dev_printk(KERN_DEBUG, dev, dev_fmt(fmt), ##__VA_ARGS__); \
} while (0)

同样的,在内核编程中推荐使用pr_xxx_ratelimited,驱动程序中使用dev_xxx_ratelimited

这些函数会记录上次打印的时间,并与当前时间进行比较。如果两次打印的时间间隔小于预设的阈值则不会打印当前的消息,从而避免在短时间内重复打印大量相同或相似的消息。同时还能控制在每个时间间隔内允许打印的最大次数。

这里就牵涉到两个参数:

  • 时间间隔,单位是秒

  • 时间间隔内允许打印的最大次数

可以有三种方式控制这两个参数:

  1. 在启动内核时,通过参数进行控制:

printk_ratelimit=2 printk_ratelimit_burst=5
  1. 直接修改内核代码

#define DEFAULT_RATELIMIT_INTERVAL 2 /* 2 seconds */
#define DEFAULT_RATELIMIT_BURST 5 /* 5 messages */
  1. 通过/proc/sys/kernel目录下的printk_ratelimit和printk_ratelimit_burst文件进行控制

$ cat /proc/sys/kernel/printk_ratelimit
5$ cat /proc/sys/kernel/printk_ratelimit_burst 
10

下面我们通过代码演示pr_xxx_ratelimited的使用:

// 定义模块的格式化字符串
#define pr_fmt(fmt) "%s:%s():%d: " fmt, KBUILD_MODNAME, __func__, __LINE__#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>static int print_num = 12; 
module_param(print_num, int, 0644);
MODULE_PARM_DESC(print_num, "Number of print");// 定义模块加载函数
static int __init pr_fmt_init(void)
{int i;pr_info("====printk模块加载成功!====\n");for (i = 0; i < print_num; i++) {pr_info_ratelimited("print count = %d\n", i); mdelay(100);}   return 0; // 返回0表示模块加载成功
}// 定义模块卸载函数
static void __exit pr_fmt_exit(void)
{pr_info("====printk模块卸载成功!====\n");
}// 注册模块加载和卸载函数
module_init(pr_fmt_init);
module_exit(pr_fmt_exit);// 模块信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("your_name");
MODULE_DESCRIPTION("pr_fmt内核模块示例");
MODULE_VERSION("0.1");

# 定义模块名称
MODULE_NAME := pr_ratelimited# 定义内核构建目录,替换成你自己的路径
KERNEL_BUILD_DIR := /home/leo/debug_kernel/linux-6.12.28# 定义目标文件
obj-m += $(MODULE_NAME).occflags-y += -DDEBUG# 默认目标
all:@echo "Building the $(MODULE_NAME) kernel module..."$(MAKE) -C $(KERNEL_BUILD_DIR) M=$(PWD) modules# 清理目标
clean:@echo "Cleaning up the build environment..."$(MAKE) -C $(KERNEL_BUILD_DIR) M=$(PWD) clean

执行make进行编译,然后sudo insmod pr_ratelimited.ko加载模块,使用dmesg查看信息:

可以看到pr_info_ratelimited就打印了10次,而我们print_num是12,这是因为/proc/sys/kernel/printk_ratelimit_burst 文件中的值是10

我们先卸载该模块sudo rmmod pr_ratelimited,然后加载模块时将print_num的值修改为100,再看一下现象:

sudo insmod pr_ratelimited.ko print_num=100

执行dmesg,查看log:

可以看到每次输出10条,中间被抑制了40条,而我们程序中每次延时100ms,(40+10)* 100ms = 5s,这正好就是

/proc/sys/kernel/printk_ratelimit中的值

只打印一次

此外内核还定义了如下一些函数:

#ifdef CONFIG_PRINTK
#define printk_once(fmt, ...)                                        \DO_ONCE_LITE(printk, fmt, ##__VA_ARGS__)
#define printk_deferred_once(fmt, ...)                                \DO_ONCE_LITE(printk_deferred, fmt, ##__VA_ARGS__)
#else
#define printk_once(fmt, ...)                                        \no_printk(fmt, ##__VA_ARGS__)
#define printk_deferred_once(fmt, ...)                                \no_printk(fmt, ##__VA_ARGS__)
#endif#define pr_emerg_once(fmt, ...)                                        \printk_once(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__)
#define pr_alert_once(fmt, ...)                                        \printk_once(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_crit_once(fmt, ...)                                        \printk_once(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__)
#define pr_err_once(fmt, ...)                                        \printk_once(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__)
#define pr_warn_once(fmt, ...)                                        \printk_once(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__)
#define pr_notice_once(fmt, ...)                                \printk_once(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__)
#define pr_info_once(fmt, ...)                                        \printk_once(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__)
/* no pr_cont_once, don't do that... */#if defined(DEBUG)
#define pr_devel_once(fmt, ...)                                        \printk_once(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_devel_once(fmt, ...)                                        \no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif/* If you are writing a driver, please use dev_dbg instead */
#if defined(DEBUG)
#define pr_debug_once(fmt, ...)                                        \printk_once(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#else
#define pr_debug_once(fmt, ...)                                        \no_printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__)
#endif

#define dev_emerg_once(dev, fmt, ...)                                        \dev_level_once(dev_emerg, dev, fmt, ##__VA_ARGS__)
#define dev_alert_once(dev, fmt, ...)                                        \dev_level_once(dev_alert, dev, fmt, ##__VA_ARGS__)
#define dev_crit_once(dev, fmt, ...)                                        \dev_level_once(dev_crit, dev, fmt, ##__VA_ARGS__)
#define dev_err_once(dev, fmt, ...)                                        \dev_level_once(dev_err, dev, fmt, ##__VA_ARGS__)
#define dev_warn_once(dev, fmt, ...)                                        \dev_level_once(dev_warn, dev, fmt, ##__VA_ARGS__)
#define dev_notice_once(dev, fmt, ...)                                        \dev_level_once(dev_notice, dev, fmt, ##__VA_ARGS__)
#define dev_info_once(dev, fmt, ...)                                        \dev_level_once(dev_info, dev, fmt, ##__VA_ARGS__)
#define dev_dbg_once(dev, fmt, ...)                                        \dev_level_once(dev_dbg, dev, fmt, ##__VA_ARGS__)

这些函数不管调用多少次,都只会打印一次消息。即便在多线程环境中也只会打印一次。

通过使用这些函数,你可以更有效地控制内核日志的输出,避免不必要的重复信息,使日志更加清晰和易于管理。

总结

printk相关的用法还有很多,这里不可能面面俱到,只讲一些常用的用法,后面还有讲到动态打印,在实际debug过程中有很大用处。

http://www.dtcms.com/wzjs/14337.html

相关文章:

  • 专门做校招的网站网站关键词快速排名优化
  • 设计公司装修seo技术网
  • 访问网站提示输入用户名密码网络营销专业培训学校
  • 电子商务网站建设实训过程推广策划方案范文
  • 企业做网站需要多少钱郑州疫情最新情况
  • 园洲做网站公司软文广告经典案例300字
  • 郑州专业做网站公司网页制作html代码
  • 古董做推广哪个网站好经典的软文广告
  • 网站建设与制作报价舆情信息在哪里找
  • 手机网站 微网站做了5天游戏推广被抓了
  • 小程序异性拼房上海排名优化seo
  • 域名指向国外服务器做网站昆明网络推广方式有哪些
  • wordpress 子菜单网站seo排名
  • 网站备案的时间怎么发布信息到百度
  • 乐山住房和城乡建设厅网站关键词推广操作
  • 营销型网站建设费用怎么这么大广告软文
  • 表白网址链接制作百度seo排名主要看啥
  • 注册网站除了域名重庆网站seo建设哪家好
  • 南昌模板建站定制网站百度seo服务方案
  • 找工厂货源上什么平台seo排名培训
  • 好发信息网网站建设惠州seo关键词推广
  • 清丰网站建设手机优化管家
  • 一流的菏泽网站建设制作一个网站的基本步骤
  • 新疆网站建设品牌seo关键词排名优化工具
  • 工程建筑模板价格关键词优化话术
  • 企石做网站在线看crm系统
  • 做轮播海报的网站网络营销网站
  • 组建做网站的团队营销技巧培训ppt
  • 租网站服务器广告优化师怎么学
  • 医疗设计网站百度关键词排行榜