android N dmesg源码分析

dmesg源码位置:
external/toybox/toys/lsb/dmesg.c
system/core/toolbox/dmesg.c(应该不是这个)


在external/toybox/Android.mk的ALL_TOOLS变量中添加了dmesg.c,编译出的/system/bin/dmesg其实是一个指向
/system/bin/toybox的符号链接,
toybox是android m上集成的项目,目的在于用一个单一bin文件统一linux下的常用命令
Toybox combines many common Linux command line utilities together into a single BSD-licensed executable. It's simple, small, fast, and reasonably standards-compliant (POSIX-2008 and LSB 4.1).

Toybox's main goal is to make Android self-hosting by improving Android's command line utilities so it can build an installable Android Open Source Project image entirely from source under a stock Android system. After a talk at the 2013 Embedded Linux Conference explaining this plan (outlinevideo), Google merged toybox into AOSP and began shipping toybox in Android Mashmallow.

adb shell后执行的ls cat等命令也其实都指向toybox

linux内核自3.5以后提供了/dev/kmsg,用于向用户空间提供访问内核log的借口,
What: /dev/kmsgDate: Mai 2012KernelVersion: 3.5Contact: Kay Sievers <kay@vrfy.org>Description: The /dev/kmsg character device node provides userspace accessto the kernel's printk buffer.

external/toybox/toys/lsb/dmesg.c:
/* dmesg.c - display/control kernel ring buffer.
  *
  * Copyright 2006, 2007 Rob Landley <rob@landley.net>
  *
 
// We care that FLAG_c is 1, so keep c at the end.
USE_DMESG(NEWTOY(dmesg, "w(follow)Ctrs#<1n#c[!tr]", TOYFLAG_BIN))
 
config DMESG
   bool "dmesg"
   default y
   help
     usage: dmesg [-Cc] [-r|-t] [-n LEVEL] [-s SIZE] [-w]
 
     Print or control the kernel ring buffer.
 
     -C  Clear ring buffer without printing
     -c  Clear ring buffer after printing
     -n  Set kernel logging LEVEL (1-9)
     -r  Raw output (with <level markers>)
     -s  Show the last SIZE many bytes
     -t  Don't print kernel's timestamps
     -w  Keep waiting for more output (aka --follow)
*/
 
#define FOR_dmesg
#include "toys.h"
#include <sys/klog.h>
 
GLOBALS(
   long   level;
   long   size;
 
   int   color;
)
 
static   int   xklogctl( int   type,  char   *buf,  int   len)
{
   int   rc = klogctl(type, buf, len);
 
   if   (rc<0) perror_exit( "klogctl" );
 
   return   rc;
}
 
// Use klogctl for reading if we're on a pre-3.5 kernel.
//3.5之前内核还是在 legacy_mode中调用klogct去读kmsg
static   void   legacy_mode() {
   char   *data, *to, *from;
   int   size;
 
   // Figure out how much data we need, and fetch it.
   if   (!(size = TT.size)) size = xklogctl(10, 0, 0);
   data = to = from = xmalloc(size+1);
   data[size = xklogctl(3 + (toys.optflags & FLAG_c), data, size)] = 0;
 
   // Filter out level markers and optionally time markers
   if   (!(toys.optflags & FLAG_r))  while   ((from - data) < size) {
     if   (from == data || from[-1] ==  '\n' ) {
       char   *to;
 
       if   (*from ==  '<'   && (to =  strchr (from,  '>' ))) from = ++to;
       if   ((toys.optflags&FLAG_t) && *from ==  '['   && (to =  strchr (from,  ']' )))
         from = to+1+(to[1]== ' ' );
     }
     *(to++) = *(from++);
   else   to = data+size;
 
   // Write result. The odds of somebody requesting a buffer of size 3 and
   // getting "<1>" are remote, but don't segfault if they do.
   if   (to != data) {
//将/proc/kmsg中的内容写到标准输出中,这句完成将kmsg输出的任务
      xwrite(1, data, to-data);
     if   (to[-1] !=  '\n' ) xputc( '\n' );
   }
   if   (CFG_TOYBOX_FREE)  free (data);
}
 
static   void   color( int   c) {
   if   (TT.color)  printf ( "\033[%dm" , c);
}
 
void   dmesg_main( void )
{
   // For -n just tell kernel which messages to keep.
   if   (toys.optflags & FLAG_n) {
     xklogctl(8, 0, TT.level);
 
     return ;
   }
 
   // For -C just tell kernel to throw everything out.
   if   (toys.optflags & FLAG_C) {
     xklogctl(5, 0, 0);
 
     return ;
   }
 
   TT.color = isatty(1);
 
 
   // Each read returns one message. By default, we block when there are no
   // more messages (--follow); O_NONBLOCK is needed for for usual behavior.
// dmesg命令在3.5内核之后读取的是 /dev/kmsg
   int   fd = xopen( "/dev/kmsg" , O_RDONLY | ((toys.optflags&FLAG_w)?0:O_NONBLOCK));
   while   (1) {
     char   msg[8192];  // CONSOLE_EXT_LOG_MAX.
     unsigned  long   long   time_us;
     int   facpri, subsystem, pos;
     char   *p, *text;
     ssize_t len;
 
     // kmsg fails with EPIPE if we try to read while the buffer moves under
     // us; the next read will succeed and return the next available entry.
     do   {
       len = read(fd, msg,  sizeof (msg));
     while   (len == -1 &&  errno   == EPIPE);
     // All reads from kmsg fail if you're on a pre-3.5 kernel.
     if   (len == -1 &&  errno   == EINVAL) {
       close(fd);
       return   legacy_mode ();//3.5之前内核还是在 legacy_mode中调用klogct去读kmsg
     }
     if   (len <= 0)  break ;
  //msg中保存的是从/dev/kmsg中读到的原始字符串,eg
//4,222,12150,-;ACPI: 4 ACPI AML tables successfully acquired and loaded
     msg[len] = 0;

 // 用sscanf解析msg
// char *msg = "4,222,12150,-;ACPI: 4 ACPI AML tables successfully acquired and loaded";
// int retval = sscanf(msg, "%u,%*u,%llu,%*[^;];%n", &facpri, &time_us, &pos);
// %u, 对应优先级"4,",因此输出facpri--4
// %*u, 跳过行数"222,"
// %llu,对应时间"12150," 下来剩下的就到了"-;ACPI: 4 ACPI AML tables successfully acquired and loaded";
//%*[^;]; 这个意义是跳过接下来从不是字符;开始的到字符; 也就是会跳过"-;",剩下就到了"ACPI: 4 ACP。。。。",这些//就是真正的log
//对于%n,gnu c 实现了 C 标准的 format specify 的 %n,它的含义是返回从该次 XXscanf 调用开始到此读了多少个字节,//目前已经读取到了"ACPI...",
//从4到A前面的;一共14个字符,A是第15个,因此%n将使pos获取14
//整个sscanf匹配到的输入项只有facpri和time_us,pos不算在内,因此返回值才与2做比较
//获取到了pos后,text = msg + pos的意义就很明确了,从原始的msg中提取出";"后面真正的文本,作为text
//这里text就是ACPI: 4 ACPI AML tables successfully acquired and loaded
这个文档上也对/dev/kmsg的标准格式有所说明:

     if   ( sscanf (msg,  "%u,%*u,%llu,%*[^;];%n" , &facpri, &time_us, &pos) != 2)
       continue ;
 
     // Drop extras after end of message text.
     text = msg + pos;
     if   ((p =  strchr (text,  '\n' ))) *p = 0;
 
// subsystem 是指kkernel log中带有 子系统名:详细信息的log,比如
// NET: Registered protocol family 10
//usb usb2: New USB device found, idVendor=1d6b, idProduct=0002
     // Is there a subsystem? (The ": " is just a convention.)
     p =  strstr (text,  ": " );
     subsystem = p ? (p - text) : 0;
 
     // "Raw" is a lie for /dev/kmsg. In practice, it just means we show the
     // syslog facility/priority at the start of each line.
// 打印优先级
     if   (toys.optflags&FLAG_r)  printf ( "<%d>" , facpri);
 
     if   (!(toys.optflags&FLAG_t)) {
       color(32); //调用printf("\033[%dm", 32); 用绿色打印
// 如果不带-t参数就打印时间戳
       printf ( "[%5lld.%06lld] " , time_us/1000000, time_us%1000000);
       color(0);
     }
 
     // Errors (or worse) are shown in red, subsystems are shown in yellow.
// 1 时间戳之后如果有subsystem先打印subsystem
     if   (subsystem) {
       color(33); // 调用printf("\033[%dm", 32); 用黄色打印
       printf ( "%.*s" , subsystem, text);
       text += subsystem;
       color(0);
     }
// 2 下来按优先级,4级及以上调用xputs输出,1-3级用printf红色输出
     if   (!((facpri&7) <= 3)) xputs(text);
     else   {
       color(31);
       printf ( "%s" , text);
       color(0);
       xputc( '\n' );
     }
   }

  close(fd);

}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值