printk 与syslog

本文深入剖析了Linux内核中的printk函数,阐述其在内核启动早期如何工作,独立于stdin和stdout。重点讨论了printk的实现原理,包括logbuf_lock、log_buf缓冲区、console_sem信号量的角色,以及如何在数据格式化后存入缓冲区。同时,文章提到初始化stdin和stdout通常涉及打开/dev/null,但并非所有情况都如此,可能在shell中设置为指向串口。最后,分析了console_drivers结构体如何注册和管理多个控制台设备。
摘要由CSDN通过智能技术生成
在头文件 <linux/kernel.h> 中定义了 【8种可用的日志级别字符串】
KERN_EMERG    用于紧急事件消息,它们一般是系统崩溃之前提示的消息。
KERN_ALERT    用于需要立即采取动作的情况。
KERN_CRIT     临界状态,通常涉及严重的硬件或软件操作失败。
KERN_ERR      用于报告错误状态;设备驱动程序会经常使用KERN_ERR来报告来自硬件的问题。
KERN_WARNING  对可能出现问题的情况进行警告,这类情况通常不会对系统造成严重问题。
KERN_NOTICE   有必要进行提示的正常情形。许多与安全相关的状况用这个级别进行汇报。
KERN_INFO     提示性信息。很多驱动程序在启动的时候,以这个级别打印出它们找到的硬件信息。
KERN_DEBUG    用于调试信息。

dmesg是从kernel的ring buffer(环缓冲区)中读取信息的.

那什么是ring buffer呢?
    在LINUX中,所有的系统信息(包内核信息)都会传送到ring buffer中.而内核产生的信息由printk()打印出来。系统启动时所看到的信息都是由该函数打印到屏幕中。 printk()打出的信息往往以 <0>...<2>... 这的数字表明消息的重要级别。高于一定的优先级别会打印到屏幕上,否则只会保留在系统的缓冲区中(ring buffer)。
    至于dmesg具体是如何从ring buffer中读取的,大家可以看dmesg.c源代码.很短,比较容易读懂.

     是syslogd这个守护进程根据/etc/syslog.conf,将不同的服务产生的Log记录到不同的文件中.
    LINUX系统启动后,由/etc/init.d/sysklogd先后启动klogd,syslogd两个守护进程。
    其中klogd会通过syslog()系统调用或者读取proc文件系统来从系统缓冲区(ring buffer)中得到由内核printk()发出的信息.而syslogd是通过klogd来读取系统内核信息.

    1> 所有系统信息是输出到ring buffer中去的.dmesg所显示的内容也是从ring buffer中读取的.
    2> LINUX系统中/etc/init.d/sysklogd会启动2个守护进程:Klogd, Syslogd
    3> klogd是负责读取内核信息的,有2种方式:
       syslog()系统调用(这个函数用法比较全,大家去MAN一下看看)直接的对/proc/kmsg进行读取(再这提一下,/proc/kmsg是专门输出内核信息的地方)
    4> Klogd的输出结果会传送给syslogd进行处理,syslogd会根据/etc/syslog.conf的配置把log信息输出到/var/log/下的不同文件中.

注意将printk与syslog接合使用, 用在内核开发方面很不错.

内核用的打印函数printk完全是和stdinstdout无关的,因为一开始到start_kernel函数刚开始进入内核就可以用printk 函数了,而建立stdinstdout是在init函数中实现的。有个问题,这里的代码中,建立stdinstdout如下

 if (open("/dev/null", O_RDWR, 0) < 0)

  printk("Warning: unable to open an initial console./n");

 (void) dup(0);

 (void) dup(0);

问题在于它打开的是/dev/null,而一般pc机上的linux打开的都是/dev/console,而且我把这几行代码删除也没有问题,所以我猜想这里建立stdinstdout并没什么用,肯定在shell中建立了定位到串口的stdinstdout。所以接下来还需要看看busybox的代码吧。

在这里还是主要分析一下printk实现的原理。

static spinlock_t logbuf_lock = SPIN_LOCK_UNLOCKED; //定义logbuf_lock,并初始化为unlock状态

static char log_buf[LOG_BUF_LEN];  //保存日志数据的缓冲区

#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])

static DECLARE_MUTEX(console_sem); //定义全局互斥信号量console_sem并初始化为1

 

asmlinkage int printk(const char *fmt, ...)

{

 va_list args;

 unsigned long flags;

 int printed_len;

 char *p;

 static char printk_buf[1024];

 static int log_level_unknown = 1;

 

 if (oops_in_progress)  // default : oops_in_progress = 0

 { //oops_in_progress指示进程发生错误,只有在panic()函数中才等于1

  //所以一般情况下下两句都不运行

  /* If a crash is occurring, make sure we can't deadlock */

  spin_lock_init(&logbuf_lock); //初始化logbuf_lock

  /* And make sure that we print immediately */

  init_MUTEX(&console_sem); //初始化console_sem为互斥的信号量,初值为1

 }

 

 /* This stops the holder of console_sem just where we want him */

 spin_lock_irqsave(&logbuf_lock, flags);

 //一般spin_lock在单cpu中无效的,所以spin_lock_irqsave真正的作用是关中断 和保存状态寄存器。

 /* Emit the output into the temporary buffer */

 va_start(args, fmt);

 printed_len = vsnprintf(printk_buf, sizeof(printk_buf), fmt, args);

 //先把数据格式化到printk_buf中去

 va_end(args);

 

 /*

  * Copy the output into log_buf.  If the caller didn't provide

  * appropriate log level tags, we insert them here

  */

 //emit_log_char 把字符存入log_buf中等待被发送,具体的参见下面的分析

 for (p = printk_buf; *p; p++) {

  if (log_level_unknown) {

   if (p[0] != '<' || p[1] < '0' || p[1] > '7' || p[2] != '>') {

    emit_log_char('<');

    emit_log_char(default_message_loglevel + '0');

    emit_log_char('>');

   }

 //如果没有提供<1>类似的日志级别,则在此加上<4>

 //我这里的default_message_loglevel=4

"797 printk messages dropped" 这种信息通常出现在 Linux 系统的日志文件中,特别是在内核日志中。这种消息表示系统已经丢弃了 797 条通过 printk 函数产生的打印消息。 printk 函数是 Linux 内核用于发送调试信息到用户空间的函数。它实际上是宏定义,基于不同的输出级别(如 DEBUG、INFO、ERR 等),选择将信息发送至不同的地方,如 console、dmesg 或 syslog。当系统过载或者需要限制特定级别的消息量时,内核可能会调整 printk 的行为,并开始丢弃某些级别较高的打印消息。 丢弃打印消息的原因有很多,包括但不限于: 1. **内存不足**:当系统内存紧张时,内核可能无法处理所有消息而开始丢弃它们。 2. **日志缓冲区满**:如果内核日志缓冲区满了,新的日志消息将会被丢弃,以防止溢出。 3. **特定级别的消息过多**:例如,如果系统的错误或严重警告消息过多,内核可以配置为减少或停止输出 DEBUG 和 INFO 消息,以避免用户界面过于拥挤。 为了更深入地理解这个问题,你可以检查系统当前的内核配置以及具体的日志文件(通常是 /var/log/kern.log)。这可以帮助确定是否有关于内存使用情况、日志缓冲区设置或其他可能导致消息丢失的问题的详细信息。同时,查看 `/proc/sys/kernel/printk` 文件也可以提供关于内核如何管理 printk 输出的信息,包括是否设置了特定级别的消息阈值。 对于如何解决这类问题,可能的步骤包括: - **增加内存**:如果是因为资源限制导致消息被丢弃,增加系统的可用内存可能是最直接的解决方案。 - **优化应用程序**:如果消息是由特定的应用程序生成的,检查并优化这些应用的行为,降低不必要的消息输出。 - **修改内核配置**:更改内核配置,例如调整 `printk_ratelimit` 参数,以改变消息被丢弃的阈值。 - **清理日志**:定期清理系统日志,特别是那些不再重要的历史记录,释放内存占用。 针对这个问题,还有一些相关的疑问可能需要考虑: -
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值