注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Tsecer的回音岛

Tsecer的博客

 
 
 

日志

 
 

内核log的去向  

2013-09-01 23:33:46|  分类: Linux系统编程 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
一、内核log
其实所谓的内核log并没有真正的内核syslog之类的接口来供内核来打印,内核的日志只是来自printk的显示,这个printk中一般会定义一个日志的级别,这个级别和我们常见的debug,info,error之类的等级是类似的。和我们通常的print函数类不同,这个printk的内容不是消耗性的,它打印的内容在内核中通过一个环形缓冲区来表示,也就是大家比较熟悉的ring buffer结构。这个buffer的大小在编译的时候确定,
#define __LOG_BUF_LEN    (1 << CONFIG_LOG_BUF_SHIFT)

内核配置选项位于linux-2.6.21\lib\Kconfig.debug文件,可以看到,其中默认的缓冲区大小为16K,
config LOG_BUF_SHIFT
    int "Kernel log buffer size (16 => 64KB, 17 => 128KB)" if DEBUG_KERNEL
    range 12 21
    default 17 if S390 || LOCKDEP
    default 16 if X86_NUMAQ || IA64
    default 15 if SMP
    default 14
内核中日志分级为
#define    KERN_EMERG    "<0>"    /* system is unusable            */
#define    KERN_ALERT    "<1>"    /* action must be taken immediately    */
#define    KERN_CRIT    "<2>"    /* critical conditions            */
#define    KERN_ERR    "<3>"    /* error conditions            */
#define    KERN_WARNING    "<4>"    /* warning conditions            */
#define    KERN_NOTICE    "<5>"    /* normal but significant condition    */
#define    KERN_INFO    "<6>"    /* informational            */
#define    KERN_DEBUG    "<7>"    /* debug-level messages            */
二、如何读取内核日志
1、通过proc/kmsg文件
正如之前看到,内核日志大小有限制,并且通常来说缓冲区的大小并不多,16KB字符串扣除系统启动打印的必须内容,剩余的日志已经较少。对于一个长时间运行的系统,这些内核的信息应该保存到持久设备上,从而可以对系统的异常进行定位,例如一个服务程序被杀死,这个服务程序有时系统的核心业务,例如apache,mysql等,此时这个事件只有内核知道,因为这个OOM杀死是通过kill -9杀死的,所以用户态根本没有机会获得信号处理的机会,或者说收到这个信号之后,进程无法再返回到用户态了,直到从系统中消亡。
对于系统日志的读取,内核提供了两种方法,一种是通过syslog系统调用,一个是通过proc文件/proc/kmsg文件来操作。
[root@Harry tapset]# ll /proc/kmsg
-r--------. 1 root root 0 2013-09-01 03:25 /proc/kmsg
可以看到,这个并不是一个设备文件,而只是一个普通的文件,所以我们就使用随便一个文件打开试一下。遗憾的是,通过vi和cat都没有办法显示这个文件的内容,使用strace简单看一下系统调用,可以发现系统调用在read之后没有返回,我们看下该文件的read操作
static ssize_t kmsg_read(struct file *file, char __user *buf,
             size_t count, loff_t *ppos)
{
    if ((file->f_flags & O_NONBLOCK) && !do_syslog(9, NULL, 0))
        return -EAGAIN;
    return do_syslog(2, buf, count);
}
/*
 * Commands to do_syslog:
 *
 *     0 -- Close the log.  Currently a NOP.
 *     1 -- Open the log. Currently a NOP.
 *     2 -- Read from the log.
 *     3 -- Read all messages remaining in the ring buffer.
 *     4 -- Read and clear all messages remaining in the ring buffer
 *     5 -- Clear ring buffer.
 *     6 -- Disable printk's to console
 *     7 -- Enable printk's to console
 *    8 -- Set level of messages printed to console
 *    9 -- Return number of unread characters in the log buffer
 *     10 -- Return size of the log buffer
 */
int do_syslog(int type, char __user *buf, int len)

case 2:        /* Read from log */
        error = -EINVAL;
        if (!buf || len < 0)
            goto out;
        error = 0;
        if (!len)
            goto out;
        if (!access_ok(VERIFY_WRITE, buf, len)) {
            error = -EFAULT;
            goto out;
        }
        error = wait_event_interruptible(log_wait,
                            (log_start - log_end));
可见通过read系统调用,此时是阻塞在了日志事件上,如果没有新的日志打印过来,此时该系统调用将会一直无法返回,所以看到cat或者vi一直被挂起。
2、通过系统调用
在sys_syslog系统调用的前面,作者不辞劳苦的写了这个系统调用参数的意义,所以可以利用这些参数来组合出系统调用。当然不用我们自己写代码来执行,有现成的工具,那就是dmesg这个工具,它为什么没有阻塞呢?因为它充分利用了这个系统调用的参数,而没有通过2参数来读取。
syslog(0xa, 0, 0)                       = 131072
syslog(0x3, 0x8284868, 0x20008)         = 103654
fstat64(1, {st_mode=S_IFIFO|0600, st_size=0, ...}) = 0
三、日志的读取
在当前的系统中,很多系统使用syslog-ng来完成日志操作任务,在此时的syslog-ng系统配置文件中,我们很少看到对于系统内核日志的规则描述,那么这个系统内核的日志被放在了哪里呢?
1、klogd
我们看一下谁在使用/proc/kmsg文件
 fuser -v /proc/kmsg

                     USER        PID ACCESS COMMAND
/proc/kmsg:          root       2721 f.... klogd
可以看到,这个工具并不是我们所熟知的一个工具,但是这个工具毕竟比较简单,它就是从系统读取日志,但是这个工具没有复杂的配置文件,那么它读取的日志放在哪里呢?
这里读取出一个配置文件也有一定的原因,从前一节中可以知道,这个read系统调用默认是阻塞的,所以如果让syslog-ng来直接读取,那么此时很可能阻塞整个进程,所以独立一个进程处理
2、klogd读取日志的去向
/* Open outputs. */
    if ( use_output )
    {
        if ( strcmp(output, "-") == 0 )
            output_file = stdout;
        else if ( (output_file = fopen(output, "w")) == (FILE *) 0 )
        {
            fprintf(stderr, "klogd: Cannot open output file " \
                "%s - %s\n", output, strerror(errno));
            return(1);
        }
    }
    else
        openlog("kernel", 0, LOG_KERN);
extern void Syslog(int priority, char *fmt, ...)
/* Output using syslog. */
    if (!strcmp(fmt, "%s"))
    {
        va_start(ap, fmt);
        argl = va_arg(ap, char *);
        if (argl[0] == '<' && argl[1] && argl[2] == '>')
        {
            switch ( argl[1] )
            {
            case '0':
                priority = LOG_EMERG;
                break;
            case '1':
                priority = LOG_ALERT;
                break;
            case '2':
                priority = LOG_CRIT;
                break;
            case '3':
                priority = LOG_ERR;
                break;
            case '4':
                priority = LOG_WARNING;
                break;
            case '5':
                priority = LOG_NOTICE;
                break;
            case '6':
                priority = LOG_INFO;
                break;
            case '7':
            default:
                priority = LOG_DEBUG;
            }
            argl += 3;
        }
        syslog(priority, fmt, argl);
        va_end(ap);
#ifdef TESTING
        putchar('\n');
#endif
        return;
    }

    va_start(ap, fmt);
    vsyslog(priority, fmt, ap);
    va_end(ap);
也就是这个工具从内核中解析获得系统的输出内容,然后将这个数据调整为LOG_KERN级别的facility,并打印如syslog,而这个syslog打印的内容则是syslog-ng识别的一个内容,甚至可以说是它的主要功能。
        #
        # the following line will be replaced by the
        # socket list generated by SuSEconfig using
        # variables from /etc/sysconfig/syslog:
        #
        unix-dgram("/dev/log");
3、日志的格式汇总
这个其实在glibc中就有规定,并且内核的facility级别就是0,在这个地方,大家终于达成了共识syslog.h

/* facility codes */
#define    LOG_KERN    (0<<3)    /* kernel messages */
#define    LOG_USER    (1<<3)    /* random user-level messages */
#define    LOG_MAIL    (2<<3)    /* mail system */
#define    LOG_DAEMON    (3<<3)    /* system daemons */
#define    LOG_AUTH    (4<<3)    /* security/authorization messages */
#define    LOG_SYSLOG    (5<<3)    /* messages generated internally by syslogd */
#define    LOG_LPR        (6<<3)    /* line printer subsystem */
#define    LOG_NEWS    (7<<3)    /* network news subsystem */
#define    LOG_UUCP    (8<<3)    /* UUCP subsystem */
#define    LOG_CRON    (9<<3)    /* clock daemon */
#define    LOG_AUTHPRIV    (10<<3)    /* security/authorization messages (private) */
#define    LOG_FTP        (11<<3)    /* ftp daemon */

    /* other codes through 15 reserved for system use */
#define    LOG_LOCAL0    (16<<3)    /* reserved for local use */
#define    LOG_LOCAL1    (17<<3)    /* reserved for local use */
#define    LOG_LOCAL2    (18<<3)    /* reserved for local use */
#define    LOG_LOCAL3    (19<<3)    /* reserved for local use */
#define    LOG_LOCAL4    (20<<3)    /* reserved for local use */
#define    LOG_LOCAL5    (21<<3)    /* reserved for local use */
#define    LOG_LOCAL6    (22<<3)    /* reserved for local use */
#define    LOG_LOCAL7    (23<<3)    /* reserved for local use */

#define    LOG_NFACILITIES    24    /* current number of facilities */
#define    LOG_FACMASK    0x03f8    /* mask to extract facility part */
                /* facility of pri */
五、syslog-ng对内核日志的处理
事实上在配置文件中找不到关于内核的日志的配置说明,不过这个没有关系,因为对于syslog-ng读取的日志,每个规则并没有说一定是匹配之后就终止,只要不加final,这个日志会被继续处理,所以内核的日志最终将会流向系统的缺省日志
filter f_messages   { not facility(news, mail) and not filter(f_iptables); };
filter f_warn       { level(warn, err, crit) and not filter(f_iptables); };
filter f_alert      { level(alert); };

#
# All messages except iptables and the facilities news and mail:
#
destination messages { file("/var/log/messages"); };
log { source(src); filter(f_messages); destination(messages); };


#
# Firewall (iptables) messages in one file:
#
destination firewall { file("/var/log/firewall"); };
log { source(src); filter(f_iptables); destination(firewall); };


#
# Warnings (except iptables) in one file:
#
destination warn { file("/var/log/warn" fsync(yes)); };
log { source(src); filter(f_warn); destination(warn); };

#
 所以,他们大部分还都在/var/log文件夹下,并且对于警告级别高的,可能会存在多分。    
六、内核日志原始格式
操作步骤
杀掉系统所有日志工具,
pkill log
 echo 1> /proc/sys/vm/block_dump 
cat /proc/kmsg

<7>kjournald2(220): WRITE block 1351456 on dm-0
<7>kjournald2(220): WRITE block 1507408 on dm-0
<7>kjournald2(220): WRITE block 25436672 on dm-0
<7>kjournald2(220): WRITE block 25436680 on dm-0
<7>kjournald2(220): WRITE block 25436688 on dm-0
<7>kjournald2(220): WRITE block 25436696 on dm-0
<7>kjournald2(220): WRITE block 25436704 on dm-0
<7>pdflush(23): WRITE block 1351448 on dm-0
<7>pdflush(23): WRITE block 1351456 on dm-0
<7>pdflush(23): WRITE block 1351216 on dm-0
<7>gnome-terminal(11333): dirtied inode 528459 (?) on dm-0
<7>gnome-terminal(11333): dirtied inode 528459 (?) on dm-0
<7>gnome-terminal(11333): dirtied inode 528459 (?) on dm-0
<7>gnome-terminal(11333): dirtied inode 529093 (?) on dm-0
<7>gnome-terminal(11333): dirtied inode 529093 (?) on dm-0
<7>gnome-terminal(11333): dirtied inode 529093 (?) on dm-0
<7>kjournald2(220): WRITE block 1507408 on dm-0
<7>kjournald2(220): WRITE block 1351216 on dm-0
<7>kjournald2(220): WRITE block 1351456 on dm-0
<7>kjournald2(220): WRITE block 25436712 on dm-0
<7>kjournald2(220): WRITE block 25436720 on dm-0
<7>kjournald2(220): WRITE block 25436728 on dm-0
<7>kjournald2(220): WRITE block 25436736 on dm-0
<7>kjournald2(220): WRITE block 25436744 on dm-0
<7>kjournald2(220): WRITE block 25436752 on
  评论这张
 
阅读(1044)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017