本文编辑整理自:
http://www.linuxidc.com/Linux/2011-07/38987.htm
一、简介
Android为我们提供了一个轻量级的日志系统,在用户空间分别提供了Java接口和C/C++接口来使用这个日志系统。
Android日志系统最终还是以驱动程序的形式实现在内核空间的。Logger驱动程序主要由两个文件构成,分别是:
kernel/common/drivers/staging/Android/logger.h
kernel/common/drivers/staging/Android/logger.c
本文将介绍Logger驱动程序的相关数据结构,然后对Logger驱动程序源代码进行情景分析,分别日志系统初始化情景、日志读取情景和日志写入情景。
二、基本数据结构
我们首先来看logger.h头文件的内容:
#ifndef _LINUX_LOGGER_H
#define _LINUX_LOGGER_H
#include <linux/types.h>
#include <linux/ioctl.h>
struct logger_entry {
__u16
len;
/* length of the payload */
__u16
__pad;
/* no matter what, we get 2 bytes of padding */
__s32
pid;
/* generating process's pid */
__s32
tid;
/* generating process's tid */
__s32
sec;
/* seconds since Epoch */
__s32
nsec;
/* nanoseconds */
char
msg[0];
/* the entry's payload */
};
#define LOGGER_LOG_RADIO "log_radio" /* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events" /* system/hardware events */
#define LOGGER_LOG_SYSTEM "log_SYSTEM" /* system/framework message*/
#define LOGGER_LOG_MAIN "log_main" /* everything else */
#define
LOGGER_ENTRY_MAX_LEN (4*1024)
#define
LOGGER_ENTRY_MAX_PAYLOAD \
(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
#define __LOGGERIO 0xAE
#define LOGGER_GET_LOG_BUF_SIZE _IO(__LOGGERIO, 1) /* size of log */
#define LOGGER_GET_LOG_LEN _IO(__LOGGERIO, 2) /* used log len */
#define LOGGER_GET_NEXT_ENTRY_LEN _IO(__LOGGERIO, 3) /* next entry len */
#define LOGGER_FLUSH_LOG _IO(__LOGGERIO, 4) /* flush log */
#endif /* _LINUX_LOGGER_H */
logger_entry
是一个用于描述一条
Log
记录的结构体。
len
成员变量记录了这
条记录的有效负载的长度,有效负载指定的日志记录本身的长度,但是不包括用于描述这个记录的
struct logger_entry
结构体
。我们调用Android.util.Log接口来使用日志系统时,会指定日志的优先级别Priority、Tag字符串以及Msg字符串;Priority + Tag + Msg三者内容的长度加起来就是记录的有效负载长度了。
__pad
成员变量是用来对齐结构体的。
pid
和
tid
成员变量分别用来标明是
哪条进程
,
哪个线程
写入的这条记录。
sec
和
nsec
成员变量记录日志写的时间。
msg
成员变量记录的就有效负载的内容了,它的大小由
len
成员变量来确定。
接着定义两个宏:
#define
LOGGER_ENTRY_MAX_LEN
(4*1024)
#define
LOGGER_ENTRY_MAX_PAYLOAD
\
(LOGGER_ENTRY_MAX_LEN - sizeof(struct logger_entry))
从这两个宏可以看出,每条日志记录的有效负载长度加上结构体
logger_entry
的长度不能超过4K个字节。
logger.h文件中还定义了其它宏,读者可以自己分析,在下面的分析中,碰到时,我们也会详细解释。再来看logger.c文件中,其它相关数据结构的定义:
/*
* struct logger_log - represents a specific log, such as 'main' or 'radio'
*
* This structure lives from module insertion until module removal, so it does
* not need additional reference counting. The structure is protected by the
* mutex 'mutex'.
*/
struct
logger_log {
unsigned char *
buffer;
/* the ring buffer itself */
struct miscdevice
misc;
/* misc device representing the log */
wait_queue_head_t
wq;
/* wait queue for readers */
struct list_head
readers;
/* this log's readers */
struct mutex
mutex;
/* mutex protecting buffer */
size_t
w_off;
/* current write head offset */
size_t
head;
/* new readers start here */
size_t
size;
/* size of the log */
};
/*
* struct logger_reader - a logging device open for reading
*
* This object lives from open to release, so we don't need additional
* reference counting. The structure is protected by log->mutex.
*/
struct
logger_reader {
struct logger_log *
log;
/* associated log */
struct list_head
list;
/* entry in logger_log's list */
size_t
r_off;
/* current read head offset */
};
/* logger_offset - returns index 'n' into the log via (optimized) modulus */
#define
logger_offset(n) ((n) & (log->size - 1))
结构体
logger_log
就是真正用来保存日志的地方了。
buffer
成员变量变是用保存日志信息的内存缓冲区,它的大小由
size
成员变量确定。从
misc
成员变量可以看出,
logger驱动程序使用的设备属于misc类型的设备,通过在Android模拟器上执行cat /proc/devices命令,可以看出,misc类型设备的
主设备号是10
。
wq
成员变量是
一个等待队列,用于保存正在等待读取日志的进程
。
readers
成员变量用来保存当前正在读取日志的进程,正在读取日志的进程由结构体
logger_reader
来描述。
mutex
成员变量是一个互斥量,用来保护log的并发访问。可以看出,
这里的日志系统的读写问题,其实是一个生产者-消费者的问题,因此,需要互斥量来保护log的并发访问
。
w_off
成员变量
用来记录下一条日志应该从哪里开始写
。
head
成员变量用来表示打开日志文件中,应该从哪一个位置开始读取日志。
结构体
logger_reader
用来表示一个读取日志的进程,
log
成员变量指向要读取的日志缓冲区。
list
成员变量用来连接其它读者进程。
r_off
成员变量
表示当前要读取的日志在缓冲区中的位置
。
logger_log
结构体中用于保存日志信息的内存缓冲区
buffer
是一个循环使用的环形缓冲区,缓冲区中保存的内容是以结构体
logger_entry
为单位的,每个单位的组成为:
logger_entry
+
priority
+
tag
+
msg
由于是内存缓冲区buffer是一个循环使用的环形缓冲区,给定一个偏移值,它在buffer中的位置由下logger_offset来确定:
#define logger_offset(n) ((n) & (log->size - 1))