Printf and Printk

本文详细介绍了Linux内核中的printk函数,对比了printk与printf的不同之处,解释了printk如何根据日志级别向控制台输出信息。文章还探讨了如何调整日志级别的方法。
对于做嵌入式或者熟悉linux 内核 的人来说,对printk这个函数一定不会感到陌生。printk相当于printf的孪生姐妹,她们一个运行在用户态,另一个则在内核态被人们所熟知。
  printk是在内核中运行的向控制台输出显示的函数, Linux内核 首先在内核空间分配一个静态 缓冲区 ,作为显示用的空间,然后调用sprintf,格式化显示字符串,最后调用tty_write向终端进行信息的显示。

  printk与printf的差异,是什么导致一个运行在内核态而另一个运行用户态?其实这两个函数的几乎是相同的,出现这种差异是因为tty_write函数需要使用fs指向的被显示的字符串,而fs是专门用于存放用户态段选择符的,因此,在内核态时,为了配合tty_write函数,printk会把fs修改为内核态数据段选择符ds中的值,这样才能正确指向内核的数据缓冲区,当然这个操作会先对fs进行压栈保存,调用tty_write完毕后再出栈恢复。总结说来,printk与printf的差异是由fs造成的,所以差异也是围绕对fs的处理。

用printk,内核会根据日志级别,可能把消息打印到当前控制台上,这个控制台通常是一个字符模式的终端、一个串口打印机或是一个并口打印机。这些消息正常输出的前提是──日志输出级别小于console_loglevel(在内核中数字越小优先级越高)。

  没有指定日志级别的printk语句默认采用的级别是 DEFAULT_ MESSAGE_LOGLEVEL(这个默认级别一般为<4>,即与KERN_WARNING在一个级别上),其定义在linux26/kernel/printk.c中可以找到

  日志级别一共有8个级别,printk的日志级别定义如下(在include/linux/kernel.h中):

  #define KERN_EMERG 0/*紧急事件消息,系统崩溃之前提示,表示系统不可用*/

  #define KERN_ALERT 1/*报告消息,表示必须立即采取措施*/

  #define KERN_CRIT 2/*临界条件,通常涉及严重的硬件或软件操作失败*/

  #define KERN_ERR 3/*错误条件,驱动程序常用KERN_ERR来报告硬件的错误*/

  #define KERN_WARNING 4/*警告条件,对可能出现问题的情况进行警告*/

  #define KERN_NOTICE 5/*正常但又重要的条件,用于提醒*/

  #define KERN_INFO 6/*提示信息,如驱动程序启动时,打印硬件信息*/

  #define KERN_DEBUG 7/*调试级别的消息*/

      # echo 8 > /proc/sys/kernel/printk

  这样所有级别<8,(0-7)的消息都可以显示在控制台上.

 

应该是loglevel的问题,通过下面命令查看你当前的loglevel设置。
cat /proc/sys/kernel/printk

如果想什么都打印出来,就
echo 8 > /proc/sys/kernel/printk

更多细节,自己搜一下 loglevel相关介绍。
追问
rhel  6.2
pintk里面四个数 4 4 1 7
手册好像写的
4是console loglevel
4是default message loglevel
1是mini console loglevel
7是default console loglevel
但是即使是 root  都没有权限更改 /proc/sys/kernel/printk 这个文件是怎么回事呢

 

       int %d 或者 %x( 注: %d 是十进制, %x 是十六进制 )

  unsigned int %u 或者 %x

  long %ld 或者 %lx

  unsigned long %lu 或者 %lx

  long long %lld 或者 %llx

  unsigned long long %llu 或者 %llx

  size_t %zu 或者 %zx

  ssize_t %zd 或者 %zx

  原始指针值必须用 %p 输出。

  u64,即(unsigned long logn),必须用 %llu 或者 %llx 输出,如:

  printk("%llu", (unsigned long long)u64_var);

  s64,即(long long),必须用 %lld 或者 %llx 输出,如 :

  printk("%lld", (long long)s64_var);

  如果 ( 变量类型 )<type> 的长度依赖一个配置选项 ( 例如: sector_t, blkcnt_t, phys_addr_t, resource_size_t) 或者 依赖相关的体系结构(例如: tcflag_t ),使用一个可能最大类型的格式说明符,并且显示转换它。如:

  printk("test: sector number/total blocks: %llu/%llu\n",(unsigned long long)sector, (unsigned long long

<think> 我们是在Linux内核环境下讨论如何屏蔽printf打印。注意:内核中通常使用printk而不是printf。因此,用户可能是指printk。 根据用户需求,他想要在内核中屏蔽printk打印。我们可以通过以下几种方式实现: 1. 修改printk的打印级别:将控制台的打印级别设置为一个较高的值,使得低于该级别的printk消息不会打印到控制台。 例如,我们可以通过以下命令设置控制台打印级别为0(只打印紧急消息): echo 0 > /proc/sys/kernel/printk 或者在内核启动参数中添加`loglevel=0`。 2. 在代码中动态修改控制台打印级别:通过`console_loglevel`变量(注意:直接修改变量可能不安全,建议使用接口)。 内核提供了接口函数`console_suspend()`和`resume_console()`,但更直接的是设置`console_loglevel`。 我们可以写一个内核模块来修改这个值。 3. 使用内核启动参数:在启动内核时,通过内核命令行参数设置默认的打印级别。例如: loglevel=0 # 或者 quiet,quiet相当于设置loglevel为4(KERN_WARNING) 4. 屏蔽特定模块的打印:可以通过修改模块中的printk语句的打印级别,或者使用条件编译。 5. 完全禁用打印:重定向printk到空设备,但是内核中并没有直接的“空设备”用于printk,因为printk是直接输出到控制台和日志缓冲区的。我们可以尝试修改控制台驱动,使其不输出,但这比较复杂。 6. 使用动态调试(dynamic debug):对于使用pr_debug()/dev_dbg()等打印(这些打印在默认情况下不输出),但printk不能直接用动态调试控制。不过,我们可以通过sysfs接口动态控制printk的打印。 由于用户没有特别说明是永久屏蔽还是临时屏蔽,以及是否针对整个系统,我们提供以下方案: 方案一:通过proc文件系统临时调整(推荐) 我们可以通过写/proc/sys/kernel/printk文件来改变控制台日志级别。该文件有4个数值,分别是: console_loglevel default_message_loglevel minimum_console_loglevel default_console_loglevel 我们只需改变第一个值(控制台日志级别)即可。例如,将其设置为0(只显示紧急消息): # echo 0 > /proc/sys/kernel/printk 这样,所有低于KERN_EMERG(0)级别的printk都不会输出到控制台。注意,日志缓冲区中仍然会记录,只是不在控制台显示。 方案二:使用内核命令行参数 在启动内核时,可以添加参数: quiet # 设置控制台日志级别为4(KERN_WARNING),即只显示警告及以上的消息 loglevel=0 # 设置控制台日志级别为0 方案三:在代码中修改 在内核代码中,我们可以直接设置控制台日志级别(但需要确保在打印之前设置): console_loglevel = 0; 注意:直接修改这个全局变量需要同步,且可能不安全。建议使用内核提供的函数: int set_console_loglevel(int level); 但是,查看内核源码,我们发现这个函数没有导出(非GPL),所以模块中可能无法使用。因此,在模块中我们可以这样: extern int console_loglevel; console_loglevel = 0; 注意:修改console_loglevel会影响整个系统的控制台输出。 方案四:使用动态调试控制pr_debug等 如果用户使用的是pr_debug(),那么可以通过动态调试来关闭。例如: // 首先在内核配置中打开CONFIG_DYNAMIC_DEBUG // 然后编译内核 // 使用以下命令关闭所有pr_debug: echo 'file * -p' > /sys/kernel/debug/dynamic_debug/control 但是,对于printk,动态调试不支持。 综上所述,对于用户想要屏蔽printf(实际上是printk)的打印,我们推荐使用方案一(临时调整)或方案二(启动时调整)。 注意:在内核中,printk的打印级别是消息的一部分,例如: printk(KERN_DEBUG "Debug message\n"); 其中,KERN_DEBUG的值为7,如果控制台日志级别(console_loglevel)大于7,那么这条消息就不会显示在控制台。 因此,我们可以通过提高控制台日志级别来屏蔽低级别的打印。 如果需要完全屏蔽所有printk(包括紧急消息),那么可以将控制台日志级别设置为比0还小的值(但最小值是0,所以实际上不可能屏蔽紧急消息)。不过,我们可以重定向控制台到空设备(如/dev/null)?但是内核中没有这个机制。 另一种思路:修改控制台驱动,让它不输出。我们可以通过注销控制台驱动或者将控制台驱动绑定到一个不输出的设备上。但这需要深入了解控制台驱动。 因此,如果用户只是不想在控制台上看到打印,那么设置控制台日志级别为0(紧急消息)或1(紧急和警报)等,可以屏蔽大部分打印。 下面给出一个示例,在内核模块中修改控制台日志级别: ```c #include <linux/module.h> #include <linux/kernel.h> extern int console_loglevel; static int __init disable_printk_init(void) { console_loglevel = 0; // 设置控制台日志级别为0(KERN_EMERG),只显示紧急消息 return 0; } static void __exit disable_printk_exit(void) { // 退出时恢复默认级别,默认级别是7(在/proc/sys/kernel/printk中定义) console_loglevel = 7; // 恢复为默认值,注意这里默认值可能不同,可以读取初始值保存 } module_init(disable_printk_init); module_exit(disable_printk_exit); MODULE_LICENSE("GPL"); ``` 但是,请注意,修改console_loglevel会影响整个系统,并且在模块卸载后需要恢复,否则会一直保持。 另外,这种方法需要知道默认值,我们可以先保存默认值,退出时恢复。但是,这个变量是全局的,可能被其他地方修改,所以保存的值可能不是真正的默认值。 因此,更推荐使用/proc接口(即方案一)或者启动参数(方案二)。 如果用户希望永久屏蔽,可以在启动参数中加入`loglevel=0`或`quiet`。 相关问题: 1. 如何屏蔽特定模块的printk打印? 2. 如何将printk的输出重定向到文件? 3. 如何调整printk的打印级别? 4. 如何在内核启动时设置默认的打印级别? </think>在 Linux 内核中屏蔽 `printk`(内核中替代 `printf` 的打印函数)有以下几种方法,按推荐度排序: ### 一、动态调整日志级别(推荐) **原理**:通过修改内核的日志级别阈值,控制哪些级别的消息可输出: ```c // 设置控制台日志级别为0(仅允许KERN_EMERG级别消息) echo 0 > /proc/sys/kernel/printk // 完全禁用所有打印(需root权限) echo 0 > /proc/sys/kernel/printk_delay ``` **效果**: - 所有低于设置级别的 `printk` 消息自动被过滤[^1] - 动态生效,无需重新编译内核 --- ### 二、编译时全局禁用(永久生效) **原理**:修改内核配置,彻底关闭打印功能: 1. 在内核配置中禁用 `CONFIG_PRINTK`: ```bash make menuconfig # 路径:Kernel hacking -> printk and dmesg options -> [ ] Show printk messages ``` 2. 重新编译并安装内核: ```bash make -j$(nproc) && make modules_install && make install ``` **效果**: - 所有 `printk` 调用在编译期被优化移除[^2] - 显著减小内核体积,提升性能 --- ### 三、条件编译控制特定模块 **原理**:在代码中使用宏控制模块级打印: ```c // 在模块头文件中定义宏开关 #define ENABLE_MODULE_DEBUG 0 #if ENABLE_MODULE_DEBUG #define mod_printk(fmt, ...) printk(KERN_DEBUG fmt, ##__VA_ARGS__) #else #define mod_printk(fmt, ...) #endif // 使用自定义宏代替printk mod_printk("Debug message won't appear when disabled\n"); ``` --- ### 四、运行时重定向到空设备 **原理**:通过 `kmsg` 接口丢弃消息: ```c #include <linux/kmsg_dump.h> // 注册消息丢弃处理函数 static struct kmsg_dumper dump; static int null_dump(struct kmsg_dumper *dumper, char *buf, size_t len) { return len; // 直接丢弃消息 } // 初始化 dump.dump = null_dump; kmsg_dump_register(&dump); ``` **适用场景**:需要动态开启/关闭特定时段的日志 --- ### 五、中断上下文特殊处理 在中断处理中需避免直接打印(可能引发死锁): ```c irqreturn_t irq_handler(int irq, void *dev_id) { if (printk_ratelimit()) { // 限速保护 printk(KERN_EMERG "Critical error in IRQ %d\n", irq); } return IRQ_HANDLED; } ``` > **关键区别**:内核使用 `printk` 而非 `printf`,且通过日志级别 (`KERN_DEBUG`..`KERN_EMERG`) 控制输出[^1] ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值