现在的位置: 首页 > 综合 > 正文

printk 与syslog

2012年07月27日 ⁄ 综合 ⁄ 共 6828字 ⁄ 字号 评论关闭
在头文件 <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

   log_level_unknown = 0;

  }

  emit_log_char(*p);

  if (*p == '/n') //每一行前面都需要加<4>之类的日志级别

   log_level_unknown = 1;

 }

 

 if (!arch_consoles_callable()) // unexecute

 {

  /* 控制台是否可调用,一般下面的不会被执行

   * On some architectures, the consoles are not usable

   * on secondary CPUs early in the boot process.

   */

  spin_unlock_irqrestore(&logbuf_lock, flags);

  goto out;

 }

 if (!down_trylock(&console_sem)) //lock ok

 {

  /* down_trylock获取信号量,lock则返回0,否则立即返回非0

   * We own the drivers.  We can drop the spinlock and let

   * release_console_sem() print the text

   */

  spin_unlock_irqrestore(&logbuf_lock, flags);

  console_may_schedule = 0;

  release_console_sem(); //在这个函数中把数据发送到串口并释放console_sem

 } else {

  /*

   * Someone else owns the drivers.  We drop the spinlock, which

   * allows the semaphore holder to proceed and to call the

   * console drivers with the output which we just produced.

   */

  spin_unlock_irqrestore(&logbuf_lock, flags);

 }

out:

 return printed_len;

}

 

static unsigned long log_start;  /* Index into log_buf: next char to be read by syslog() */

static unsigned long con_start;  /* Index into log_buf: next char to be sent to consoles */

static unsigned long log_end;  /* Index into log_buf: most-recently-written-char + 1 */

static unsigned long logged_chars; /* Number of chars produced since last read+clear operation */

static void emit_log_char(char c)

{

 LOG_BUF(log_end) = c;  //把字符c存到log_buf缓冲区中,缓冲区满了就会覆盖开始的数据

 log_end++;   //

 if (log_end - log_start > LOG_BUF_LEN) //log_start指示syslog读取的开始

  log_start = log_end - LOG_BUF_LEN;//缓冲区满了会把开始的指针向前推

 if (log_end - con_start > LOG_BUF_LEN) //con_start指示控制台读取的开始

  con_start = log_end - LOG_BUF_LEN;

 if (logged_chars < LOG_BUF_LEN)

  logged_chars++;

}

 

void release_console_sem(void)

{

 unsigned long flags;

 unsigned long _con_start, _log_end;

 unsigned long must_wake_klogd = 0;

 

 for ( ; ; ) {

  spin_lock_irqsave(&logbuf_lock, flags);//关中断和保存flag

  must_wake_klogd |= log_start - log_end; //唤醒klogd标志

  if (con_start == log_end)

   break;   /* Nothing to print */

  _con_start = con_start;

  _log_end = log_end;

  con_start = log_end;  /* Flush , con_start向前移用了,可见缓冲区是循环使用的 */

  spin_unlock_irqrestore(&logbuf_lock, flags);

  call_console_drivers(_con_start, _log_end);//在这个函数中发送数据,见下面的分析

 }

 console_may_schedule = 0; //指示数据发送时是否能进行任务调度,在使用串口控制台时没用

 up(&console_sem); //释放信号量

 spin_unlock_irqrestore(&logbuf_lock, flags);

 if (must_wake_klogd && !oops_in_progress)

  wake_up_interruptible(&log_wait);

}

 

 

static void call_console_drivers(unsigned long start, unsigned long end)

{

 unsigned long cur_index, start_print;

 static int msg_level = -1;

 

 if (((long)(start - end)) > 0)

  BUG();

 

 cur_index = start;

 start_print = start;

 while (cur_index != end) {

  if ( msg_level < 0 &&

   ((end - cur_index) > 2) &&

   LOG_BUF(cur_index + 0) == '<' &&

   LOG_BUF(cur_index + 1) >= '0' &&

   LOG_BUF(cur_index + 1) <= '7' &&

   LOG_BUF(cur_index + 2) == '>')

  {

   msg_level = LOG_BUF(cur_index + 1) - '0';

   cur_index += 3;

   start_print = cur_index;

  } //去除每行开头的类似<4>的日志级别,把它赋给msg_level

  while (cur_index != end) {

   char c = LOG_BUF(cur_index);

   cur_index++;

   if (c == '/n') {

    if (msg_level < 0) {

     /*

      * printk() has already given us loglevel tags in

      * the buffer.  This code is here in case the

      * log buffer has wrapped right round and scribbled

      * on those tags

      */

     msg_level = default_message_loglevel;

    }

    _call_console_drivers(start_print, cur_index, msg_level);

    //发送一行数据

    msg_level = -1;

    start_print = cur_index;

    break;

   }

  }

 }

 _call_console_drivers(start_print, end, msg_level); //发送剩余的数据

}

 

 

struct console *console_drivers; //全局的console类型的结构体

 

static void _call_console_drivers(unsigned long start, unsigned long end, int msg_log_level)

{

 //如果msg_log_level < console_loglevel 并且 console_drivers存在 并且 start != end

 if (msg_log_level < console_loglevel && console_drivers && start != end) {

  if ((start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)) {

   /* wrapped write */

   //缓冲区循环使用就会出现(start & LOG_BUF_MASK) > (end & LOG_BUF_MASK)

   //的清况,于是就分两部分来发送.

   //__call_console_drivers才是真正的发送函数

   __call_console_drivers(start & LOG_BUF_MASK, LOG_BUF_LEN);

   __call_console_drivers(0, end & LOG_BUF_MASK);

  } else {

   __call_console_drivers(start, end);

  }

 }

}

 

static void __call_console_drivers(unsigned long start, unsigned long end)

{

 struct console *con;

抱歉!评论已关闭.