Android的logger机制分析

  • 时间:
  • 浏览:0
  • 来源:大发彩神计划版网址—大发彩神稳赢计划

       ret += nr;

 * lapped by the writer; also do the same for the default "start head".

size_t         r_off; /* current read head offset */

     logger.h文件中还定义了其它宏,这里就不一一分析了。

   struct logger_log* log;   /* associated log */

#ifndef LOGV

 * not need additional reference counting. The structure is protected by the

      USER-NAME@MACHINE-NAME:~/Android$ emulator &

{

       if(!reader)

   publicstaticfinalint ERROR=6;

#define LOGGER_ENTRY_MAX_PAYLOAD   \

static unsigned char _buf_ ## VAR[SIZE]; \

readers成员变量用来保存当前正在读取日志的多多程序运行 ,正在读取日志的多多程序运行 由底部形态体logger_reader来描述。

   }

最后,把有效负载的长度val换成struct logger_entry的长度就得到了要读取的日志记录的总长度了。

       if(unlikely(nr<0)){

 *     the minor number requested is used.

                       if((misc_minors[i>>3]&(1<<(i&7)))==0)

       kernel/common/drivers/staging/android/logger.h

 * from a to b cross c?

}

#define LOG(priority, tag, ...) \

           log->w_off= orig;

       schedule();

   mutex_lock(&log->mutex);

 */

        */

logger.h文件中,有这有一八个 宏的定义:

 * Caller must hold log->mutex.

       return ret;

       同类使用LOGV

        */

static size_t get_next_entry(struct logger_log *log, size_t off, size_t len)

   }while(count< len);

#if LOG_NDEBUG

      Log.e(LOG_TAG, "This is the logprinted by Log.e in android user space.");

       memcpy(log->buffer, buf + len, count - len);

   if(ret)

   while(1){

    * Priority constant for the println method; use Log.e.

        LOGV("This is the log printed by LOGV in androiduser space.");

        */

................................................

#define LOGGER_LOG_EVENTS  "log_events"    /* system/hardware events */

{

       否则 调用do_write_log首先把logger_entry底部形态体写入到日志缓冲区中:

     查看/proc/kmsg文件:

   .unlocked_ioctl= logger_ioctl,

size_t         size;  /* size of the log */

..................................................................

 * Fix up any readers, pulling them forward to the first readable

logger.c文件中定义了有一八个 日志设备:

 * fix_up_readers - walk the list of all readers and "fix up" any who were

   struct logger_reader*reader= file->private_data;

 *

               err = PTR_ERR(misc->this_device);

   publicstaticint e(String tag, String msg){

                               break;

 *

#define LOGGER_ENTRY_MAX_PAYLOAD   \

   }

#define LOGGER_GET_LOG_BUF_SIZE    _IO(__LOGGERIO, 1) /* size of log */

       }

                       mutex_unlock(&misc_mtx);

misc成员变量可不时需看出,logger驱动多多程序运行 使用的设备属于misc类型的设备,通过在Android模拟器上执行cat /proc/devices命令,可不时需看出,misc类型设备的主设备号是10

   }

   publicstaticint w(String tag, String msg){

 * - Atomically reads exactly one log entry

 * Example:

    android_printLog(priority, tag, __VA_ARGS__)

       return println_native(LOG_ID_MAIN, INFO, tag, msg +'\n'+ getStackTraceString(tr));

#define logger_offset(n)   ((n) & (log->size - 1))

 * struct logger_log - represents a specific log, such as 'main' or 'radio'

           mutex_unlock(&log->mutex);

       reader->r_off= log->head;

           log->w_off= orig;

 * do_write_log - writes 'len' bytes from 'buf' to 'log'

       }

   }

       }

           mutex_unlock(&log->mutex);

{

   .wq = __WAIT_QUEUE_HEAD_INITIALIZER(VAR .wq), \

 * Behavior:

/*

   }

 */

printk(KERN_ALERT"This is the log printed by printk in linux kernel space.");

       mutex_unlock(&log->mutex);

       return-ENODEV;

   }

       private static final String LOG_TAG ="MY_LOG_TAG";

};

       int priority, String tag, String msg);

#define __LOGGERIO 0xAE

       INIT_LIST_HEAD(&reader->list);

       return println_native(LOG_ID_MAIN, DEBUG, tag, msg);

       nr = do_write_log_from_user(log, iov->iov_base, len);

               }

/*

    * the current read head offset up to 'count' bytes or to the end of

       if(signal_pending(current)){

/*

   publicstaticint i(String tag, String msg, Throwable tr){

   size_t len;

}

   ANDROID_LOG_UNKNOWN =0,

C/C++日志接口一般是在编写硬件抽象层模块原困编写JNI最好的法子时使用,而Java接口一般是在应用层编写APP时使用。

       /* write out this segment's payload */

}

 */

}

      logger.c文件中,注册的读取日志设备文件的最好的法子为logger_read

       if(clock_interval(old, new, reader->r_off))

#ifndef LOG

#include <linux/types.h>

};

/*

 * The second argument may be NULL or "" to indicate the "global" tag.

   struct logger_reader*reader;

#else

   }else

       if(file->f_flags& O_NONBLOCK){

#define LOGGER_ENTRY_MAX_LEN       (4*1024)

       return println_native(LOG_ID_MAIN, DEBUG, tag, msg +'\n'+ getStackTraceString(tr));

   publicstaticint w(String tag, String msg, Throwable tr){

 */

start:

       goto start;

       mutex_unlock(&log->mutex);

        #include <cutils/log.h>

}

       至此,Logger驱动多多程序运行 的主要逻辑就分析完成了,还有其它的一点接口,如logger_poll logger_ioctllogger_release函数,比较简单,就不再分析了。

       int err=0;

 * logging macros. You can change this preprocessor definition

static __u32 get_entry_len(struct logger_log *log, size_t off)

       ret += nr;

       那我就可不时需看一遍输出的日志了。

   publicstaticfinalint WARN=5;

static ssize_t do_write_log_from_user(struct logger_log *log,

           ret =-EAGAIN;

#define android_printLog(prio, tag, fmt...) \

       return println_native(LOG_ID_MAIN, INFO, tag, msg);

   publicstaticfinalint DEBUG=3;

DEFINE_LOGGER_DEVICE(log_main, LOGGER_LOG_MAIN,64*1024)

       off = logger_offset(off+ nr);

   .compat_ioctl= logger_ioctl,

   /* get exactly one entry from the log */

       }

   .open= logger_open,

       count += nr;

 * entry after (what will be) the new write offset. We do this now

       mutex_lock(&misc_mtx);

                     constvoid __user*buf, size_t count)

       return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);

 *

       goto out;

   /**

#define LOGGER_LOG_MAIN    "log_main"  /* everything else */

   header.sec= now.tv_sec;

   mutex_unlock(&log->mutex);

 * writev(), and aio_write(). Writes are our fast path, and we try to optimize

           return1;

 * The stuff in the rest of this file should not be used directly.

   (LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

//KERN_ALERT表示日志级别,上方紧跟着格式化字符串。

   struct mutex       mutex; /* mutex protecting buffer */

 *

out:

   return ret;

原困logger_entry是内核堆栈空间分配的,直接用memcpy拷贝就可不时需了。

   .owner= THIS_MODULE,

   if(count!= len)

struct logger_reader {

   printk(KERN_INFO"logger: created %luK log '%s'\n",

 * entry after the new write head.

   wait_queue_head_t   wq;/* wait queue for readers */

   struct list_head   readers;/* this log's readers */

下面我准备从应用开发和源码分析两偏离 来分析安卓的Logger机制。

      注释说明了这有一八个 日志设备的用途。

msg成员变量记录有效负载的内容,它的大小由len成员变量来取舍。

   __s32       sec;   /* seconds since Epoch */

system/core/include/cutils/log.h中,定义了对应的宏。

   struct logger_entry header;

               int i= DYNAMIC_MINORS;

{

{

   size_t          w_off; /* current write head offset */

           return nr;

       return println_native(LOG_ID_MAIN, WARN, tag, msg);

fix_up_readers(log,sizeof(struct logger_entry)+ header.len);

 * logger_read - our log's read() method

       }

#define LOGGER_GET_NEXT_ENTRY_LEN  _IO(__LOGGERIO, 3) /* next entry len */

       ssize_t nr;

   default:

 *

}

 */

       Log.i(LOG_TAG, "This is the log printed by Log.i inandroid user space.");

   publicstaticint v(String tag, String msg){

   if(len&& copy_from_user(log->buffer+ log->w_off, buf, len))

注册完成后,通过device_create创建设备文件节点。这里,将创建/dev/log/main/dev/log/events/dev/log/radio有一八个 设备文件,那我,用户空间就可不时需通过读写这有一八个 文件和驱动多多程序运行 进行交互。

#define LOGV(...)  ((void)0)

      USER-NAME@MACHINE-NAME:~/Android$ adb shell

       return ret;

       mutex_lock(&log->mutex);

 *     A zero is returned on success and a negative errno code for

 * - O_NONBLOCK works

out:

 *

           return-ENOMEM;

你这俩 偏离 将简要地介绍一下在Android应用多多程序运行 开发中Log的使用最好的法子

   if(!log)

   wake_up_interruptible(&log->wq);

   char       msg[0];/* the entry's payload */

   if(unlikely(ret))

       /*

       structlogger_entry | priority | tag | msg

.....................................................

Android系统中的C/C++日志接口是通过宏来使用的。

}

     前一天的代码表示,原困有新的日志可读,那末就首先通过get_entry_len来获取下四根可读的日志记录的长度,从这里可不时需看出,日志读取多多程序运行 是以日志记录为单位进行读取的,一次只读取四根记录。get_entry_len的函数实现如下:

 */

       其中, prioritytagmsg这有一八个 段的内容是由iov参数从用户空间传递下来的,分别对应iov上方的有一八个 元素。而logger_entry是由内核空间来构造的:

              size_t count, loff_t*pos)

   /* wake up any blocked readers */

 * Android log priority values, in ascending priority order.

 * This object lives from open to release, so we don't need additional

    __android_log_print(prio, tag, fmt)

        */

   header.nsec= now.tv_nsec;

                  struct logger_reader*reader,

   /**

 * struct logger_reader - a logging device open for reading

#define DEFINE_LOGGER_DEVICE(VAR, NAME, SIZE) \

 *

 * Optimal read size is LOGGER_ENTRY_MAX_LEN. Will set errno to EINVAL if read

while(nr_segs-->0){

    LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__)

 * - If there are no log entries to read, blocks until log is written to

以上是Logger在应用开发中的使用的简单分析,除了Logger在应用开发中的使用,我将更进一步地分析Logger驱动多多程序运行 的源代码,更加深刻的认识Android日志系统。

      新打开日志设备文件时,是从log->head位置后来结束了了读取日志的,保处在struct logger_reader的成员变量r_off中。

       len = min_t(size_t, iov->iov_len, header.len- ret);

 *

 */

    * Priority constant for the println method; use Log.d.

/*

 */

   ANDROID_LOG_WARN,

struct logger_log是真正用来保存日志的底部形态体。

 * get_entry_len - Grabs the length of the payload of the next entry starting

如对应于ANDROID_LOG_VERBOSE的宏LOGV

   size_t count =0;

   return0;

            unsignedlong nr_segs, loff_t ppos)

#define LOGGER_LOG_RADIO   "log_radio" /* radio-related messages */

 out:

   if(file->f_mode& FMODE_READ){

   ret = init_log(&log_radio);

   }

#define KERN_EMERG "<0>"   /* system is unusable          */

        #define LOG_TAG "MY LOG TAG"

   publicstaticfinalint INFO=4;

#define LOGGER_FLUSH_LOG       _IO(__LOGGERIO, 4) /* flush log */

}

   if(count!= len)

        */

       size_t len;

 *     misc_register   -       register a miscellaneous device

   do_write_log(log,&header,sizeof(struct logger_entry));

};

      接着,通过有一八个 while循环把iov的内容写入到日志缓冲区中,也而是 日志的优先级别priority、日志Tag和日志主体Msg

       prepare_to_wait(&log->wq,&wait, TASK_INTERRUPTIBLE);

   if(copy_to_user(buf, log->buffer+ reader->r_off, len))

       printk(KERN_ERR"logger: failed to register misc "

 * This structure lives from module insertion until module removal, so it does

 为那些要调用fix_up_reader你这俩 函数呢?你这俩 函数又是作那些用的呢?这原困日志缓冲区是循环使用的,即旧的日志记录原困那末及时读取,而缓冲区的内容又原困用完时,就时需覆盖旧的记录来容纳新的记录。而这偏离 将要被覆盖的内容,有原困是一点reader的下一偏离 读取的日志所在的位置,以及为新的reader准备的日志后来结束了了读取位置head所在的位置。否则 ,时需调整那些位置,使它们都能不能指向有一八个 新的有效的位置。大伙来看一下fix_up_reader函数的实现:

时需注意的是,在函数后来结束了了的地方,表示读取日志上下文的structlogger_reader是保处在文件指针的private_data成员变量上方的,这是在打开设备文件时设置的,设备文件打开最好的法子为logger_open

       dev = MKDEV(MISC_MAJOR, misc->minor);

       iov++;

        要查看那些LOG的输出,可不时需配合logcat工具。原困是在Eclipse环境(ADT)下运行,直接在Eclipse就可不时需查看一遍:

   }

       memcpy(&val, log->buffer+ off,1);

这你这俩 状况是通过判断日志缓冲区的大小和要读取的日志记录在缓冲区中的位置的差值来区别的,原困相差1,而是 明是前你这俩 状况了。

#define LOGGER_GET_LOG_LEN     _IO(__LOGGERIO, 2) /* used log len */

   }

      kernel/common/drivers/staging/android/logger.c

 * The caller needs to hold log->mutex.

         输入的参数iocb表示io上下文,iov表示要写入的内容,长度为nr_segs,表示有nr_segs个段的内容要写入。大伙知道,每个要写入的日志的底部形态形式为:

   int ret;

/*

       struct miscdevice*c;

   publicstaticfinalint VERBOSE=2;

   list_for_each_entry(reader,&log->readers, list)

        */

publicfinalclass Log{

   publicstaticfinalint ASSERT=7;

   return ret;

       memcpy(&val, log->buffer+ off,2);

       misc->this_device= device_create(misc_class, misc->parent, dev,NULL,

   unsignedchar*    buffer;/* the ring buffer itself */

       INIT_LIST_HEAD(&misc->list);

};

 *     and placed in the minor field of the structure. For other cases

   header.tid= current->pid;

#ifndef LOG_PRI

/**

      否则 ,原困要使用C/C++日志接口,否则我定义此人 的LOG_TAG宏和含晒 头文件system/core/include/cutils/log.h就可不时需了:

 */

 * them above all else.

      接下来,得到了要读取的记录的长度,就调用do_read_log_to_user函数来执行真正的读取动作:

           return-EFAULT;

                                         "%s", misc->name);

}

   returnsizeof(struct logger_entry)+ val;

   ssize_t ret;

 *

       reader = kmalloc(sizeof(struct logger_reader), GFP_KERNEL);

        原困Android日志系统是以驱动多多程序运行 的形式实现在内核空间的,所以时需获取Android内核源代码来分析。在下载好Android源代码工程中,Logger驱动多多程序运行 主要由有一八个 文件构成,分别是:

 */

   }

   return count;

   /*

   while(nr_segs-->0){

 * Note how near a no-op this is in the write-only case. Keep it that way!

        * earlier defaults

/*

   if(count< ret){

       struct logger_reader*reader;

   ANDROID_LOG_INFO,

       if(!ret)

   do{

      logger_init函数通过调用init_log函数来初始化了上述提到的有一八个 日志设备:

   __u16       len;   /* length of the payload */

staticint __init init_log(struct logger_log *log)

   if(unlikely(log->w_off== reader->r_off)){

       struct logger_entry | priority | tag | msg

 * We do this by "pulling forward" the readers and start head to the first

           break;

}

       dev_t dev;

       goto out;

   ret = init_log(&log_main);

                       return-EBUSY;

   ANDROID_LOG_DEBUG,

 * because if we partially fail, we can end up with clobbered log

       mutex_lock(&log->mutex);

    * Second, we read any remaining bytes, starting back at the head of

       return println_native(LOG_ID_MAIN, VERBOSE, tag, msg +'\n'+ getStackTraceString(tr));

/*

       return println_native(LOG_ID_MAIN, WARN, tag, msg +'\n'+ getStackTraceString(tr));

   mutex_unlock(&log->mutex);

   /*

       return println_native(LOG_ID_MAIN, ERROR, tag, msg);

           reader->r_off= get_next_entry(log, reader->r_off, len);

 * from 'off'.

   publicstaticint w(String tag, Throwable tr){

buffer成员变量是用于保存日志信息的内存缓冲区,它的大小由size成员变量取舍。

   /**

       list_add(&misc->list,&misc_list);

#endif

   struct miscdevice  misc;  /* misc device representing the log */

       .parent = NULL, \

 * clock_interval - is a < c < b in mod-space? Put another way, does the line

device_initcall(logger_init);

       list_add_tail(&reader->list,&log->readers);

   return count;

printk的使用最好的法子:

       USER-NAME@MACHINE-NAME:~/Android$ adb shell

};

/*

   /* is there still something to read or did we race? */

   return0;

   size_t new = logger_offset(old+ len);

   publicstaticint i(String tag, String msg){

   }, \

 *     @misc: device structure

       mutex_unlock(&misc_mtx);

   }

                       mutex_unlock(&misc_mtx);

   }

        * Add it to the front, so that later devices can "override"

 * buffer is insufficient to hold next entry.

{

   ret = do_read_log_to_user(log, reader, buf, ret);

(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))

staticvoid fix_up_readers(struct logger_log *log, size_t len)

原困iov的内容是由用户空间传下来的,时需调用do_write_log_from_user来写入:

    * entry after (what will be) the new write offset. We do this now

}

   struct logger_log*log= file_get_log(iocb->ki_filp);

  原困是在此人 编译的Android源代码工程中使用,则在后台中运行模拟器:

staticint __init logger_init(void)

   .size = SIZE, \

      Android系统中,printk输出的日志信息保处在/proc/kmsg中,要查看/proc/kmsg的内容,时需在后台中运行模拟器:

   }

 * reference counting. The structure is protected by log->mutex.

#define LOG_PRI(priority, tag, ...) \

       file->private_data= log;

              "device for log '%s'!\n", log->misc.name);

      原困是内存缓冲区buffer是有一八个 循环使用的环形缓冲区,给定有一八个 偏移值,它在buffer中的位置由下logger_offset来取舍:

#endif

    * Priority constant for the println method.

   /* get the size of the next entry */

   if(unlikely(ret))

static inline int clock_interval(size_t a, size_t b, size_t c)

   ret = init_log(&log_events);

    * because if we partially fail, we can end up with clobbered log

 */

   struct logger_log*log= reader->log;

      日志驱动多多程序运行 模块的初始化函数为logger_init

   struct logger_log*log;

{

       reader->log= log;

   finish_wait(&log->wq,&wait);

#deinfe KERN_WARNING   "<4>"   /* warning conditions          */

 *

#define KERN_ALERT "<1>"   /* action must be taken immediately */

                  char __user*buf,

 *

   if(unlikely(!header.len))

       ret =(log->w_off== reader->r_off);

   size_t len;

static ssize_t logger_read(struct file *file,char __user*buf,

 * before using the other macros to change the tag.

               if(i<0){

    * entries that encroach on readable buffer.

 *     The structure passed is linked into the kernel and may not be

   size_t orig = log->w_off;

 * Basic log message macro.

 */

 */

  否则 ,原困要使用Java日志接口,否则我在类中定义的LOG_TAG常量和引用android.util.Log就可不时需了:

/*

#define _LINUX_LOGGER_H

                       return-EBUSY;

   now = current_kernel_time();

 *     number is set to %MISC_DYNAMIC_MINOR a minor number is assigned

 * Caller needs to hold log->mutex.

{

Android内核是基于Linux Kernel 2.36的,否则 ,Linux KernelLOG机制同样适合于Android内核,这而是 与C语言的printf齐名的printk。与printf同类,printk提供格式化输入功能,同去,它也具有所有LOG机制的特点——提供日志级别过虑功能。

system/core/include/android/log.h定义了日志的级别:

       file->private_data= reader;

      上方提到,每四根日志记录是由两大偏离 组成的,有一八个 用于描述这条日志记录的底部形态体struct logger_entry,那我是记录体你这俩 ,即有效负载。底部形态体structlogger_entry的长度是固定的,否则我知道有效负载的长度,就可不时需知道整条日志记录的长度了。而有效负载的长度是记录在底部形态体struct logger_entry的成员变量len中,而len成员变量的地址与struct logger_entry的地址相同,否则 ,只时需读取记录的后来结束了了位置的有一八个 字节就可不时需了。

    */

} android_LogPriority;

   if(unlikely(ret)){

       }

   ANDROID_LOG_SILENT,/* only for SetMinPriority(); must be last */

       return println_native(LOG_ID_MAIN, ERROR, tag, msg +'\n'+ getStackTraceString(tr));

      init_log函数主要调用了misc_register函数来注册misc设备,misc_register函数定义在kernel/common/drivers/char/misc.c文件中:

/*

       /* figure out how much of this vector we can keep */

 */

      否则 在这里,还有有一八个 重要的步骤:

       return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr));

   publicstaticint d(String tag, String msg, Throwable tr){

struct logger_entry {

 * logger_aio_write - our write method, implementing support for write(),

   ANDROID_LOG_ERROR,

               while(--i>=0)

       root@android:/ # logcat

   ANDROID_LOG_VERBOSE,

 * Defines a log structure with name 'NAME' and a size of 'SIZE' bytes, which

               goto out;

 */

    * Fix up any readers, pulling them forward to the first readable

 * This is the local tag used for the following simplified

       memcpy(((char*)&val)+1, log->buffer,1);

   }else{

       if(misc->minor< DYNAMIC_MINORS)

typedefenum android_LogPriority{

pidtid成员变量分别用来记录是哪条多多程序运行 写入了这条记录。

      接下来,我将首先分析Logger驱动多多程序运行 的相关数据底部形态,否则 对Logger驱动多多程序运行 源代码的使用情景进行分析,比如日志系统初始化情景、日志读取情景和日志写入情景。

    */

 *

{

#include <linux/ioctl.h>

/*

#ifndef LOG_TAG

/*

   len = min(count, log->size- reader->r_off);

分别是log_mainlog_eventslog_radio,名称分别LOGGER_LOG_MAINLOGGER_LOG_EVENTSLOGGER_LOG_RADIO,它们的次设备号为MISC_DYNAMIC_MINOR,即为在注册时动态分配。

 * LONG_MAX minus LOGGER_ENTRY_MAX_LEN.

   /**

{

     启动adb shell工具:

       size_t nr = get_entry_len(log, off);

   memcpy(log->buffer+ log->w_off, buf, len);

       if(a< c|| b>= c)

Android系统中,提供了简单、便利的LOG机制,开发人员可不时需方便地使用。下面我将介绍在Android内核空间和用户空间中LOG的使用和查看最好的法子。

 * get_next_entry - return the offset of the first valid entry at least 'len'

   .w_off = 0, \

     再来看logger.c文件中,其它相关数据底部形态的定义:

原困那末新的日志可读,否则 设备文件是是否是以非阻塞O_NONBLOCK的最好的法子打开原困这时有信号要外理(signal_pending(current)),那末就直接返回,不再等待的图片 新的日志写入。判断当前是是否是有新的日志可读的最好的法子是:

   if(b< a){

    * the log.

           return1;

}

 *     failure.

   reader->r_off= logger_offset(reader->r_off+ count);

     接着还定义了有一八个 宏:

{

               misc_minors[misc->minor>>3]|=1<<(misc->minor&7);

       return-EFAULT;

   ret = get_entry_len(log, reader->r_off);

       return0;

   if(ret)

   int ret;

static ssize_t do_read_log_to_user(struct logger_log *log,

static struct logger_log VAR = { \

      即判断当前缓冲区的写入位置和当前读多多程序运行 的读取位置是是否是相等,原困不相等,则说明有新的日志可读。

}

   .release= logger_release,

       #definelogger_offset(n)          ((n) & (log->size -1))

#deinfe KERN_DEBUG "<7>"   /* debug-level messages        */

   size_t old = log->w_off;

       if(a< c&& b>= c)

   /**

       ssize_t nr;

   __s32       nsec;  /* nanoseconds */

   if(unlikely(ret))

   }

   return ret;

   header.len= min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);

staticstruct file_operations logger_fops={

#define LOGGER_ENTRY_MAX_LEN       (4*1024)

   return off;

               misc->minor= i;

      start标号处的while循环是等待的图片 的图片 日志可读,原困原困那末新的日志可读了,那末就要读多多程序运行 就要进入休眠状况,等待的图片 新的日志写入后再唤醒,这是通过prepare_waitschedule有一八个 调用来实现的。

staticint logger_open(struct inode *inode,struct file*file)

 你这俩 函数简单地调用copy_to_user函数来把处在内核空间的日志缓冲区指定的内容拷贝到用户空间的内存缓冲区就可不时需了,同去,把当前读取日志多多程序运行 的上下文信息中的读偏移r_off前进到下四根日志记录的后来结束了了的位置上。

Android系统在用户空间中提供了轻量级的logger日志系统,它是在内核中实现的你这俩 设备驱动,与用户空间的logcat工具配合使用都能不能方便地跟踪调试多多程序运行 。在Android系统中,分别为C/C++Java语言提供你这俩 不同的logger访问接口。

       return-EFAULT;

   publicstaticint d(String tag, String msg){

     struct logger_reader是用来表示有一八个 读取日志的多多程序运行 的底部形态体,log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者多多程序运行 。r_off成员变量表示当时需读取的日志在缓冲区中的位置。

    */

       .minor = MISC_DYNAMIC_MINOR, \

secnsec成员变量记录日志写的时间。

    * We read from the log in two disjoint operations. First, we read from

#define KERN_CRIT  "<2>"   /* critical conditions         */

                  size_t count)

/* logger_offset - returns index 'n' into the log via (optimized) modulus */

    * the log, whichever comes first.

   /**

struct logger_entry是有一八个 用于描述四根Log记录的底部形态体。

   ret = nonseekable_open(inode, file);

 在多多程序运行 开发过程中,LOG是广泛使用的用来记录多多程序运行 执行过程的机制,它既可不时需用于多多程序运行 调试,也可不时需用于产品运营中的事件记录。

   }

 *     destroyed until it has been unregistered.

 * Log macro that allows you to specify a number for priority.

       log->head= get_next_entry(log, log->head, len);

      root@android:/# cat  /proc/kmsg

 *

head成员变量用来表示打开日志文件中,应该从哪有一八个 位置后来结束了了读取日志。

   __u16       __pad; /* no matter what, we get 2 bytes of padding */

   return0;

   .mutex = __MUTEX_INITIALIZER(VAR .mutex), \

 * The caller needs to hold log->mutex.

   len = min(count, log->size- log->w_off);

      ret = (log->w_off == reader->r_off);

   __u16 val;

       return ret;

          (unsignedlong) log->size>>10, log->misc.name);

   if(clock_interval(old, new, log->head))

           ret =-EINTR;

#define LOG_TAG NULL

printk提供了8种日志级别(<linux/kernel.h>):

   .readers = LIST_HEAD_INIT(VAR .readers), \

w_off成员变量用来记录下四根日志应该从哪里后来结束了了写。

staticvoid do_write_log(struct logger_log *log,constvoid*buf, size_t count)

   ANDROID_LOG_FATAL,

       ret =-EINVAL;

       if(copy_from_user(log->buffer, buf + len, count - len))

 *

        */

int misc_register(struct miscdevice * misc)

       /* write out this segment's payload */

又原困日志记录缓冲区是循环使用的,这有一八个 节字有原困是第有一八个 字节存插进缓冲区最后有一八个 字节,而第八个字节存插进缓冲区的第有一八个 节,除此之外,这有一八个 字节是是否是连在同去的。否则 ,分你这俩 状况来考虑,对于前者,分别通过读取缓冲区最后有一八个 字节和第有一八个 字节来得到日志记录的有效负载长度到本地变量val中,对于后者,直接读取连续有一八个 字节的值到本地变量val中。

   publicstaticint e(String tag, String msg, Throwable tr){

 *     Register a miscellaneous device with the kernel. If the minor

mutex成员变量是有一八个 互斥量,用来保护log的并发访问。原困可不时需看出,这里的日志系统的读写那些的间题,觉得是有一八个 生产者-消费者的那些的间题,否则 ,时需互斥量来保护log的并发访问。

}

           break;

 * entries that encroach on readable buffer.

 *

       len = min_t(size_t, iov->iov_len, header.len- ret);

................................................

       if(IS_ERR(misc->this_device)){

       #defineLOGGER_LOG_RADIO       "log_radio"    /* radio-related messages */

       #define LOGGER_LOG_EVENTS      "log_events" /* system/hardware events */

       #define LOGGER_LOG_MAIN         "log_main"    /* everything else */

   /* null writes succeed, return zero */

       启动adb shell工具:

   size_t len;

#deinfe KERN_ERR   "<3>"   /* error conditions         */

   .aio_write= logger_aio_write,

Logger机制是在Android系统中提供的有一八个 轻量级的日志系统,你这俩 日志系统是以驱动多多程序运行 的形式在内核空间实现的,在用户空间分别提供了Java接口和C/C++接口来使用你这俩 日志系统,使用的接口取决于编写的是Android应用多多程序运行 还是系统组件。

   /**@hide */publicstaticnativeint println_native(int bufID,

/*

       iov++;

wq成员变量是有一八个 等待的图片 队列,用于保存正等待的图片 的图片 读取日志的多多程序运行 。

   DEFINE_WAIT(wait);

 * must be a power of two, greater than LOGGER_ENTRY_MAX_LEN, and less than

           return nr;

       if(misc->minor== MISC_DYNAMIC_MINOR){

   .poll= logger_poll,

   ssize_t ret =0;

   __s32       pid;   /* generating process's pid */

__pad成员变量是用来对齐底部形态体的。

 *

       .fops = &logger_fops, \

   publicstaticint v(String tag, String msg, Throwable tr){

   len = min(count, log->size- log->w_off);

       list_for_each_entry(c,&misc_list, list){

#define LOGV(...)  ((void)LOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))

   log = get_log_from_minor(MINOR(inode->i_rdev));

   mutex_lock(&log->mutex);

len成员变量记录了这条记录的有效负载的长度,有效负载指定的日志记录你这俩 的长度,否则 不包括用于描述你这俩 记录的struct logger_entry底部形态体。大伙调用android.util.Log接口来使用日志系统时,会指定日志的优先级别PriorityTag字符串以及Msg字符串,Priority + Tag + Msg三者内容的长度加起来而是 记录的有效负载长度。

   int ret;

   }

 * mutex 'mutex'.

       return err;

       nr = do_write_log_from_user(log, iov->iov_base, len);

   case1:

ssize_t logger_aio_write(struct kiocb *iocb,conststruct iovec*iov,

       mutex_unlock(&log->mutex);

   if(count!= len)

}

/*

       if(unlikely(nr<0)){

/*

   .read= logger_read,

 * Simplified macro to send a verbose log message using the current LOG_TAG.

   log->w_off= logger_offset(log->w_off+ count);

   ANDROID_LOG_DEFAULT,   /* only for SetMinPriority() */

   __s32       tid;   /* generating process's tid */

Android系统在Frameworks层中定义了Log接口(frameworks/base/core/java/android/util/Log.java):

       USER-NAME@MACHINE-NAME:~/Android$ emulator &

   header.pid= current->tgid;

 * ================================================================

   }

   /*

 */

#endif

还有一点时需注意的是,原困Logger驱动多多程序运行 模块在退出系统时,是无需卸载的,所以你这俩 模块那末module_exit函数,而对于模块上方定义的对象,也那末用对引用计数技术。

       break;

       使用logcat命令查看日志:

       goto out;

   log->w_off= logger_offset(log->w_off+ count);

   fix_up_readers(log,sizeof(struct logger_entry)+ header.len);

#endif/* _LINUX_LOGGER_H */

               }

   switch(log->size- off){

    * Priority constant for the println method; use Log.i.

{

   struct list_head   list;  /* entry in logger_log's list */

 */

       /* figure out how much of this vector we can keep */

DEFINE_LOGGER_DEVICE(log_radio, LOGGER_LOG_RADIO,64*1024)

/*

       struct logger_entry header;

       structtimespec now;

       now =current_kernel_time();

       header.pid= current->tgid;

       header.tid= current->pid;

       header.sec= now.tv_sec;

       header.nsec= now.tv_nsec;

       header.len= min_t(size_t, iocb->ki_left, LOGGER_ENTRY_MAX_PAYLOAD);

#endif

       .name = NAME, \

       if(copy_to_user(buf+ len, log->buffer, count - len))

logger.c文件,注册的写入日志设备文件的最好的法子为logger_aio_write

           return-EFAULT;

注册的日志设备文件操作最好的法子为logger_fops

#deinfe KERN_NOTICE "<5>"  /* normal but significant condition */

   ret = misc_register(&log->misc);

 判断log->head和所有读者reader的当前读偏移reader->r_off是是否是在被覆盖的区域内,原困是,就时需调用get_next_entry来取得下有一八个 有效的记录的起始位置来调整当前位置:

#deinfe KERN_INFO  "<6>"   /* informational            */

 * logger_open - the log's open() file operation

 * bytes after 'off'.

   size_t          head;  /* new readers start here */

       }

   .head = 0, \

 */

   .buffer = _buf_ ## VAR, \

    * Priority constant for the println method; use Log.w.

DEFINE_LOGGER_DEVICE(log_events, LOGGER_LOG_EVENTS,256*1024)

 */

    * Priority constant for the println method; use Log.v.

           break;

   .misc = { \

#ifndef _LINUX_LOGGER_H

#endif

/*

               if(c->minor== misc->minor){

{

     从这有一八个 宏可不时需看出,每条日志记录的有效负载长度换成底部形态体logger_entry的长度那末超过4K个字节。

而判断log->head和所有读者reader的当前读偏移reader->r_off是是否是在被覆盖的区域内,是通过clock_interval函数来实现的:

 * LOG(LOG_WARN, NULL, "Failed with error %d", errno);

          最后,日志写入完毕,还时需唤醒正等待的图片 的图片 新日志的reader多多程序运行 :

   }

       struct logger_log底部形态体中用于保存日志信息的内存缓冲区buffer是有一八个 循环使用的环形缓冲区,缓冲区中保存的内容是以struct logger_entry为单位的,每个单位的组成为:

   struct timespec now;

struct logger_log {

       goto out;

       /* wake up any blocked readers */

       wake_up_interruptible(&log->wq);

       size_t len;