- 浏览: 21485289 次
- 性别:
- 来自: 杭州
最新评论
-
ZY199266:
配置文件还需要额外的配置ma
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
ZY199266:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我又一个问题就是 如果像你的这种形式写。配置文件还需要额外的 ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
lvgaga:
我的一访问为什么是 /mavenwebdemo/WEB-I ...
Android 客户端通过内置API(HttpClient) 访问 服务器(用Spring MVC 架构) 返回的json数据全过程 -
y1210251848:
你的那个错误应该是项目所使用的目标框架不支持吧
log4net配置(web中使用log4net,把web.config放在单独的文件中)
Android日志系统驱动程序Logger源代码分析
我们知道,在Android系统中,提供了一个轻量级的日志系统,这个日志系统是以驱动程序的形式实现在内核空间的,而在用户空间分别提供了Java接口和C/C++接口来使用这个日志系统,取决于你编写的是Android应用程序还是系统组件。在前面的文章浅谈Android系统开发中LOG的使用中,已经简要地介绍了在Android应用程序开发中Log的使用方法,在这一篇文章中,我们将更进一步地分析Logger驱动程序的源代码,使得我们对Android日志系统有一个深刻的认识。
既然Android 日志系统是以驱动程序的形式实现在内核空间的,我们就需要获取Android内核源代码来分析了,请参照前面在Ubuntu上下载、编译和安装Android最新源代码和在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)两篇文章,下载好Android源代码工程。Logger驱动程序主要由两个文件构成,分别是:
kernel/common/drivers/staging/android/logger.h
kernel/common/drivers/staging/android/logger.c
接下来,我们将分别介绍Logger驱动程序的相关数据结构,然后对Logger驱动程序源代码进行情景分析,分别日志系统初始化情景、日志读取情景和日志写入情景。
一.Logger驱动程序的相关数据结构。
我们首先来看logger.h头文件的内容:
- #ifndef_LINUX_LOGGER_H
- #define_LINUX_LOGGER_H
- #include<linux/types.h>
- #include<linux/ioctl.h>
- structlogger_entry{
- __u16len;/*lengthofthepayload*/
- __u16__pad;/*nomatterwhat,weget2bytesofpadding*/
- __s32pid;/*generatingprocess'spid*/
- __s32tid;/*generatingprocess'stid*/
- __s32sec;/*secondssinceEpoch*/
- __s32nsec;/*nanoseconds*/
- charmsg[0];/*theentry'spayload*/
- };
- #defineLOGGER_LOG_RADIO"log_radio"/*radio-relatedmessages*/
- #defineLOGGER_LOG_EVENTS"log_events"/*system/hardwareevents*/
- #defineLOGGER_LOG_MAIN"log_main"/*everythingelse*/
- #defineLOGGER_ENTRY_MAX_LEN(4*1024)
- #defineLOGGER_ENTRY_MAX_PAYLOAD\
- (LOGGER_ENTRY_MAX_LEN-sizeof(structlogger_entry))
- #define__LOGGERIO0xAE
- #defineLOGGER_GET_LOG_BUF_SIZE_IO(__LOGGERIO,1)/*sizeoflog*/
- #defineLOGGER_GET_LOG_LEN_IO(__LOGGERIO,2)/*usedloglen*/
- #defineLOGGER_GET_NEXT_ENTRY_LEN_IO(__LOGGERIO,3)/*nextentrylen*/
- #defineLOGGER_FLUSH_LOG_IO(__LOGGERIO,4)/*flushlog*/
- #endif/*_LINUX_LOGGER_H*/
接着定义两个宏:
#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文件中,其它相关数据结构的定义:
- /*
- *structlogger_log-representsaspecificlog,suchas'main'or'radio'
- *
- *Thisstructurelivesfrommoduleinsertionuntilmoduleremoval,soitdoes
- *notneedadditionalreferencecounting.Thestructureisprotectedbythe
- *mutex'mutex'.
- */
- structlogger_log{
- unsignedchar*buffer;/*theringbufferitself*/
- structmiscdevicemisc;/*miscdevicerepresentingthelog*/
- wait_queue_head_twq;/*waitqueueforreaders*/
- structlist_headreaders;/*thislog'sreaders*/
- structmutexmutex;/*mutexprotectingbuffer*/
- size_tw_off;/*currentwriteheadoffset*/
- size_thead;/*newreadersstarthere*/
- size_tsize;/*sizeofthelog*/
- };
- /*
- *structlogger_reader-aloggingdeviceopenforreading
- *
- *Thisobjectlivesfromopentorelease,sowedon'tneedadditional
- *referencecounting.Thestructureisprotectedbylog->mutex.
- */
- structlogger_reader{
- structlogger_log*log;/*associatedlog*/
- structlist_headlist;/*entryinlogger_log'slist*/
- size_tr_off;/*currentreadheadoffset*/
- };
- /*logger_offset-returnsindex'n'intothelogvia(optimized)modulus*/
- #definelogger_offset(n)((n)&(log->size-1))
结构体struct logger_reader用来表示一个读取日志的进程,log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者进程。r_off成员变量表示当前要读取的日志在缓冲区中的位置。
struct logger_log结构体中用于保存日志信息的内存缓冲区buffer是一个循环使用的环形缓冲区,缓冲区中保存的内容是以struct logger_entry为单位的,每个单位的组成为:
struct logger_entry | priority | tag | msg
由于是内存缓冲区buffer是一个循环使用的环形缓冲区,给定一个偏移值,它在buffer中的位置由下logger_offset来确定:
#define logger_offset(n) ((n) & (log->size - 1))
二. Logger驱动程序模块的初始化过程分析。
继续看logger.c文件,定义了三个日志设备:
- /*
- *Definesalogstructurewithname'NAME'andasizeof'SIZE'bytes,which
- *mustbeapoweroftwo,greaterthanLOGGER_ENTRY_MAX_LEN,andlessthan
- *LONG_MAXminusLOGGER_ENTRY_MAX_LEN.
- */
- #defineDEFINE_LOGGER_DEVICE(VAR,NAME,SIZE)\
- staticunsignedchar_buf_##VAR[SIZE];\
- staticstructlogger_logVAR={\
- .buffer=_buf_##VAR,\
- .misc={\
- .minor=MISC_DYNAMIC_MINOR,\
- .name=NAME,\
- .fops=&logger_fops,\
- .parent=NULL,\
- },\
- .wq=__WAIT_QUEUE_HEAD_INITIALIZER(VAR.wq),\
- .readers=LIST_HEAD_INIT(VAR.readers),\
- .mutex=__MUTEX_INITIALIZER(VAR.mutex),\
- .w_off=0,\
- .head=0,\
- .size=SIZE,\
- };
- DEFINE_LOGGER_DEVICE(log_main,LOGGER_LOG_MAIN,64*1024)
- DEFINE_LOGGER_DEVICE(log_events,LOGGER_LOG_EVENTS,256*1024)
- DEFINE_LOGGER_DEVICE(log_radio,LOGGER_LOG_RADIO,64*1024)
#define LOGGER_LOG_RADIO "log_radio"/* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events"/* system/hardware events */
#define LOGGER_LOG_MAIN "log_main"/* everything else */
注释说明了这三个日志设备的用途。注册的日志设备文件操作方法为logger_fops:
- staticstructfile_operationslogger_fops={
- .owner=THIS_MODULE,
- .read=logger_read,
- .aio_write=logger_aio_write,
- .poll=logger_poll,
- .unlocked_ioctl=logger_ioctl,
- .compat_ioctl=logger_ioctl,
- .open=logger_open,
- .release=logger_release,
- };
日志驱动程序模块的初始化函数为logger_init:
- staticint__initlogger_init(void)
- {
- intret;
- ret=init_log(&log_main);
- if(unlikely(ret))
- gotoout;
- ret=init_log(&log_events);
- if(unlikely(ret))
- gotoout;
- ret=init_log(&log_radio);
- if(unlikely(ret))
- gotoout;
- out:
- returnret;
- }
- device_initcall(logger_init);
- staticint__initinit_log(structlogger_log*log)
- {
- intret;
- ret=misc_register(&log->misc);
- if(unlikely(ret)){
- printk(KERN_ERR"logger:failedtoregistermisc"
- "deviceforlog'%s'!\n",log->misc.name);
- returnret;
- }
- printk(KERN_INFO"logger:created%luKlog'%s'\n",
- (unsignedlong)log->size>>10,log->misc.name);
- return0;
- }
- /**
- *misc_register-registeramiscellaneousdevice
- *@misc:devicestructure
- *
- *Registeramiscellaneousdevicewiththekernel.Iftheminor
- *numberissetto%MISC_DYNAMIC_MINORaminornumberisassigned
- *andplacedintheminorfieldofthestructure.Forothercases
- *theminornumberrequestedisused.
- *
- *Thestructurepassedislinkedintothekernelandmaynotbe
- *destroyeduntilithasbeenunregistered.
- *
- *Azeroisreturnedonsuccessandanegativeerrnocodefor
- *failure.
- */
- intmisc_register(structmiscdevice*misc)
- {
- structmiscdevice*c;
- dev_tdev;
- interr=0;
- INIT_LIST_HEAD(&misc->list);
- mutex_lock(&misc_mtx);
- list_for_each_entry(c,&misc_list,list){
- if(c->minor==misc->minor){
- mutex_unlock(&misc_mtx);
- return-EBUSY;
- }
- }
- if(misc->minor==MISC_DYNAMIC_MINOR){
- inti=DYNAMIC_MINORS;
- while(--i>=0)
- if((misc_minors[i>>3]&(1<<(i&7)))==0)
- break;
- if(i<0){
- mutex_unlock(&misc_mtx);
- return-EBUSY;
- }
- misc->minor=i;
- }
- if(misc->minor<DYNAMIC_MINORS)
- misc_minors[misc->minor>>3]|=1<<(misc->minor&7);
- dev=MKDEV(MISC_MAJOR,misc->minor);
- misc->this_device=device_create(misc_class,misc->parent,dev,NULL,
- "%s",misc->name);
- if(IS_ERR(misc->this_device)){
- err=PTR_ERR(misc->this_device);
- gotoout;
- }
- /*
- *Addittothefront,sothatlaterdevicescan"override"
- *earlierdefaults
- */
- list_add(&misc->list,&misc_list);
- out:
- mutex_unlock(&misc_mtx);
- returnerr;
- }
三. Logger驱动程序的日志记录读取过程分析。
继续看logger.c 文件,注册的读取日志设备文件的方法为logger_read:
- /*
- *logger_read-ourlog'sread()method
- *
- *Behavior:
- *
- *-O_NONBLOCKworks
- *-Iftherearenologentriestoread,blocksuntillogiswrittento
- *-Atomicallyreadsexactlyonelogentry
- *
- *OptimalreadsizeisLOGGER_ENTRY_MAX_LEN.WillseterrnotoEINVALifread
- *bufferisinsufficienttoholdnextentry.
- */
- staticssize_tlogger_read(structfile*file,char__user*buf,
- size_tcount,loff_t*pos)
- {
- structlogger_reader*reader=file->private_data;
- structlogger_log*log=reader->log;
- ssize_tret;
- DEFINE_WAIT(wait);
- start:
- while(1){
- prepare_to_wait(&log->wq,&wait,TASK_INTERRUPTIBLE);
- mutex_lock(&log->mutex);
- ret=(log->w_off==reader->r_off);
- mutex_unlock(&log->mutex);
- if(!ret)
- break;
- if(file->f_flags&O_NONBLOCK){
- ret=-EAGAIN;
- break;
- }
- if(signal_pending(current)){
- ret=-EINTR;
- break;
- }
- schedule();
- }
- finish_wait(&log->wq,&wait);
- if(ret)
- returnret;
- mutex_lock(&log->mutex);
- /*istherestillsomethingtoreadordidwerace?*/
- if(unlikely(log->w_off==reader->r_off)){
- mutex_unlock(&log->mutex);
- gotostart;
- }
- /*getthesizeofthenextentry*/
- ret=get_entry_len(log,reader->r_off);
- if(count<ret){
- ret=-EINVAL;
- gotoout;
- }
- /*getexactlyoneentryfromthelog*/
- ret=do_read_log_to_user(log,reader,buf,ret);
- out:
- mutex_unlock(&log->mutex);
- returnret;
- }
- /*
- *logger_open-thelog'sopen()fileoperation
- *
- *Notehownearano-opthisisinthewrite-onlycase.Keepitthatway!
- */
- staticintlogger_open(structinode*inode,structfile*file)
- {
- structlogger_log*log;
- intret;
- ret=nonseekable_open(inode,file);
- if(ret)
- returnret;
- log=get_log_from_minor(MINOR(inode->i_rdev));
- if(!log)
- return-ENODEV;
- if(file->f_mode&FMODE_READ){
- structlogger_reader*reader;
- reader=kmalloc(sizeof(structlogger_reader),GFP_KERNEL);
- if(!reader)
- return-ENOMEM;
- reader->log=log;
- INIT_LIST_HEAD(&reader->list);
- mutex_lock(&log->mutex);
- reader->r_off=log->head;
- list_add_tail(&reader->list,&log->readers);
- mutex_unlock(&log->mutex);
- file->private_data=reader;
- }else
- file->private_data=log;
- return0;
- }
start标号处的while循环是在等待日志可读,如果已经没有新的日志可读了,那么就要读进程就要进入休眠状态,等待新的日志写入后再唤醒,这是通过prepare_wait和schedule两个调用来实现的。如果没有新的日志可读,并且设备文件不是以非阻塞O_NONBLOCK的方式打开或者这时有信号要处理(signal_pending(current)),那么就直接返回,不再等待新的日志写入。判断当前是否有新的日志可读的方法是:
ret = (log->w_off == reader->r_off);
即判断当前缓冲区的写入位置和当前读进程的读取位置是否相等,如果不相等,则说明有新的日志可读。
继续向下看,如果有新的日志可读,那么就,首先通过get_entry_len来获取下一条可读的日志记录的长度,从这里可以看出,日志读取进程是以日志记录为单位进行读取的,一次只读取一条记录。get_entry_len的函数实现如下:
- /*
- *get_entry_len-Grabsthelengthofthepayloadofthenextentrystarting
- *from'off'.
- *
- *Callerneedstoholdlog->mutex.
- */
- static__u32get_entry_len(structlogger_log*log,size_toff)
- {
- __u16val;
- switch(log->size-off){
- case1:
- memcpy(&val,log->buffer+off,1);
- memcpy(((char*)&val)+1,log->buffer,1);
- break;
- default:
- memcpy(&val,log->buffer+off,2);
- }
- returnsizeof(structlogger_entry)+val;
- }
接着往下看,得到了要读取的记录的长度,就调用do_read_log_to_user函数来执行真正的读取动作:
- staticssize_tdo_read_log_to_user(structlogger_log*log,
- structlogger_reader*reader,
- char__user*buf,
- size_tcount)
- {
- size_tlen;
- /*
- *Wereadfromthelogintwodisjointoperations.First,wereadfrom
- *thecurrentreadheadoffsetupto'count'bytesortotheendof
- *thelog,whichevercomesfirst.
- */
- len=min(count,log->size-reader->r_off);
- if(copy_to_user(buf,log->buffer+reader->r_off,len))
- return-EFAULT;
- /*
- *Second,wereadanyremainingbytes,startingbackattheheadof
- *thelog.
- */
- if(count!=len)
- if(copy_to_user(buf+len,log->buffer,count-len))
- return-EFAULT;
- reader->r_off=logger_offset(reader->r_off+count);
- returncount;
- }
四. Logger驱动程序的日志记录写入过程分析。
继续看logger.c 文件,注册的写入日志设备文件的方法为logger_aio_write:
- /*
- *logger_aio_write-ourwritemethod,implementingsupportforwrite(),
- *writev(),andaio_write().Writesareourfastpath,andwetrytooptimize
- *themaboveallelse.
- */
- ssize_tlogger_aio_write(structkiocb*iocb,conststructiovec*iov,
- unsignedlongnr_segs,loff_tppos)
- {
- structlogger_log*log=file_get_log(iocb->ki_filp);
- size_torig=log->w_off;
- structlogger_entryheader;
- structtimespecnow;
- ssize_tret=0;
- 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);
- /*nullwritessucceed,returnzero*/
- if(unlikely(!header.len))
- return0;
- mutex_lock(&log->mutex);
- /*
- *Fixupanyreaders,pullingthemforwardtothefirstreadable
- *entryafter(whatwillbe)thenewwriteoffset.Wedothisnow
- *becauseifwepartiallyfail,wecanendupwithclobberedlog
- *entriesthatencroachonreadablebuffer.
- */
- fix_up_readers(log,sizeof(structlogger_entry)+header.len);
- do_write_log(log,&header,sizeof(structlogger_entry));
- while(nr_segs-->0){
- size_tlen;
- ssize_tnr;
- /*figureouthowmuchofthisvectorwecankeep*/
- len=min_t(size_t,iov->iov_len,header.len-ret);
- /*writeoutthissegment'spayload*/
- nr=do_write_log_from_user(log,iov->iov_base,len);
- if(unlikely(nr<0)){
- log->w_off=orig;
- mutex_unlock(&log->mutex);
- returnnr;
- }
- iov++;
- ret+=nr;
- }
- mutex_unlock(&log->mutex);
- /*wakeupanyblockedreaders*/
- wake_up_interruptible(&log->wq);
- returnret;
- }
struct logger_entry | priority | tag | msg
其中, priority、tag和msg这三个段的内容是由iov参数从用户空间传递下来的,分别对应iov里面的三个元素。而logger_entry是由内核空间来构造的:
struct logger_entry header;
struct timespec 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);
然后调用do_write_log首先把logger_entry结构体写入到日志缓冲区中:
- /*
- *do_write_log-writes'len'bytesfrom'buf'to'log'
- *
- *Thecallerneedstoholdlog->mutex.
- */
- staticvoiddo_write_log(structlogger_log*log,constvoid*buf,size_tcount)
- {
- size_tlen;
- len=min(count,log->size-log->w_off);
- memcpy(log->buffer+log->w_off,buf,len);
- if(count!=len)
- memcpy(log->buffer,buf+len,count-len);
- log->w_off=logger_offset(log->w_off+count);
- }
接着,通过一个while循环把iov的内容写入到日志缓冲区中,也就是日志的优先级别priority、日志Tag和日志主体Msg:
- while(nr_segs-->0){
- size_tlen;
- ssize_tnr;
- /*figureouthowmuchofthisvectorwecankeep*/
- len=min_t(size_t,iov->iov_len,header.len-ret);
- /*writeoutthissegment'spayload*/
- nr=do_write_log_from_user(log,iov->iov_base,len);
- if(unlikely(nr<0)){
- log->w_off=orig;
- mutex_unlock(&log->mutex);
- returnnr;
- }
- iov++;
- ret+=nr;
- }
- staticssize_tdo_write_log_from_user(structlogger_log*log,
- constvoid__user*buf,size_tcount)
- {
- size_tlen;
- len=min(count,log->size-log->w_off);
- if(len&©_from_user(log->buffer+log->w_off,buf,len))
- return-EFAULT;
- if(count!=len)
- if(copy_from_user(log->buffer,buf+len,count-len))
- return-EFAULT;
- log->w_off=logger_offset(log->w_off+count);
- returncount;
- }
- /*
- *Fixupanyreaders,pullingthemforwardtothefirstreadable
- *entryafter(whatwillbe)thenewwriteoffset.Wedothisnow
- *becauseifwepartiallyfail,wecanendupwithclobberedlog
- *entriesthatencroachonreadablebuffer.
- */
- fix_up_readers(log,sizeof(structlogger_entry)+header.len);
- /*
- *fix_up_readers-walkthelistofallreadersand"fixup"anywhowere
- *lappedbythewriter;alsodothesameforthedefault"starthead".
- *Wedothisby"pullingforward"thereadersandstartheadtothefirst
- *entryafterthenewwritehead.
- *
- *Thecallerneedstoholdlog->mutex.
- */
- staticvoidfix_up_readers(structlogger_log*log,size_tlen)
- {
- size_told=log->w_off;
- size_tnew=logger_offset(old+len);
- structlogger_reader*reader;
- if(clock_interval(old,new,log->head))
- log->head=get_next_entry(log,log->head,len);
- list_for_each_entry(reader,&log->readers,list)
- if(clock_interval(old,new,reader->r_off))
- reader->r_off=get_next_entry(log,reader->r_off,len);
- }
- /*
- *get_next_entry-returntheoffsetofthefirstvalidentryatleast'len'
- *bytesafter'off'.
- *
- *Callermustholdlog->mutex.
- */
- staticsize_tget_next_entry(structlogger_log*log,size_toff,size_tlen)
- {
- size_tcount=0;
- do{
- size_tnr=get_entry_len(log,off);
- off=logger_offset(off+nr);
- count+=nr;
- }while(count<len);
- returnoff;
- }
- /*
- *clock_interval-isa<c<binmod-space?Putanotherway,doestheline
- *fromatobcrossc?
- */
- staticinlineintclock_interval(size_ta,size_tb,size_tc)
- {
- if(b<a){
- if(a<c||b>=c)
- return1;
- }else{
- if(a<c&&b>=c)
- return1;
- }
- return0;
- }
/* wake up any blocked readers */
wake_up_interruptible(&log->wq);
至此, Logger驱动程序的主要逻辑就分析完成了,还有其它的一些接口,如logger_poll、logger_ioctl和logger_release函数,比较简单,读取可以自行分析。这里还需要提到的一点是,由于Logger驱动程序模块在退出系统时,是不会卸载的,所以这个模块没有module_exit函数,而对于模块里面定义的对象,也没有用对引用计数技术。
这篇文章着重介绍了Android日志系统在内核空间的实现,在下一篇文章中,我们将接着介绍在用户空间中,提供给Android应用程序使用的Java和C/C++ LOG调用接口的实现过程,敬请关注。
我们知道,在Android系统中,提供了一个轻量级的日志系统,这个日志系统是以驱动程序的形式实现在内核空间的,而在用户空间分别提供了Java接口和C/C++接口来使用这个日志系统,取决于你编写的是Android应用程序还是系统组件。在前面的文章浅谈Android系统开发中LOG的使用中,已经简要地介绍了在Android应用程序开发中Log的使用方法,在这一篇文章中,我们将更进一步地分析Logger驱动程序的源代码,使得我们对Android日志系统有一个深刻的认识。
既然Android 日志系统是以驱动程序的形式实现在内核空间的,我们就需要获取Android内核源代码来分析了,请参照前面在Ubuntu上下载、编译和安装Android最新源代码和在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)两篇文章,下载好Android源代码工程。Logger驱动程序主要由两个文件构成,分别是:
kernel/common/drivers/staging/android/logger.h
kernel/common/drivers/staging/android/logger.c
接下来,我们将分别介绍Logger驱动程序的相关数据结构,然后对Logger驱动程序源代码进行情景分析,分别日志系统初始化情景、日志读取情景和日志写入情景。
一.Logger驱动程序的相关数据结构。
我们首先来看logger.h头文件的内容:
- #ifndef_LINUX_LOGGER_H
- #define_LINUX_LOGGER_H
- #include<linux/types.h>
- #include<linux/ioctl.h>
- structlogger_entry{
- __u16len;/*lengthofthepayload*/
- __u16__pad;/*nomatterwhat,weget2bytesofpadding*/
- __s32pid;/*generatingprocess'spid*/
- __s32tid;/*generatingprocess'stid*/
- __s32sec;/*secondssinceEpoch*/
- __s32nsec;/*nanoseconds*/
- charmsg[0];/*theentry'spayload*/
- };
- #defineLOGGER_LOG_RADIO"log_radio"/*radio-relatedmessages*/
- #defineLOGGER_LOG_EVENTS"log_events"/*system/hardwareevents*/
- #defineLOGGER_LOG_MAIN"log_main"/*everythingelse*/
- #defineLOGGER_ENTRY_MAX_LEN(4*1024)
- #defineLOGGER_ENTRY_MAX_PAYLOAD\
- (LOGGER_ENTRY_MAX_LEN-sizeof(structlogger_entry))
- #define__LOGGERIO0xAE
- #defineLOGGER_GET_LOG_BUF_SIZE_IO(__LOGGERIO,1)/*sizeoflog*/
- #defineLOGGER_GET_LOG_LEN_IO(__LOGGERIO,2)/*usedloglen*/
- #defineLOGGER_GET_NEXT_ENTRY_LEN_IO(__LOGGERIO,3)/*nextentrylen*/
- #defineLOGGER_FLUSH_LOG_IO(__LOGGERIO,4)/*flushlog*/
- #endif/*_LINUX_LOGGER_H*/
接着定义两个宏:
#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文件中,其它相关数据结构的定义:
- /*
- *structlogger_log-representsaspecificlog,suchas'main'or'radio'
- *
- *Thisstructurelivesfrommoduleinsertionuntilmoduleremoval,soitdoes
- *notneedadditionalreferencecounting.Thestructureisprotectedbythe
- *mutex'mutex'.
- */
- structlogger_log{
- unsignedchar*buffer;/*theringbufferitself*/
- structmiscdevicemisc;/*miscdevicerepresentingthelog*/
- wait_queue_head_twq;/*waitqueueforreaders*/
- structlist_headreaders;/*thislog'sreaders*/
- structmutexmutex;/*mutexprotectingbuffer*/
- size_tw_off;/*currentwriteheadoffset*/
- size_thead;/*newreadersstarthere*/
- size_tsize;/*sizeofthelog*/
- };
- /*
- *structlogger_reader-aloggingdeviceopenforreading
- *
- *Thisobjectlivesfromopentorelease,sowedon'tneedadditional
- *referencecounting.Thestructureisprotectedbylog->mutex.
- */
- structlogger_reader{
- structlogger_log*log;/*associatedlog*/
- structlist_headlist;/*entryinlogger_log'slist*/
- size_tr_off;/*currentreadheadoffset*/
- };
- /*logger_offset-returnsindex'n'intothelogvia(optimized)modulus*/
- #definelogger_offset(n)((n)&(log->size-1))
结构体struct logger_reader用来表示一个读取日志的进程,log成员变量指向要读取的日志缓冲区。list成员变量用来连接其它读者进程。r_off成员变量表示当前要读取的日志在缓冲区中的位置。
struct logger_log结构体中用于保存日志信息的内存缓冲区buffer是一个循环使用的环形缓冲区,缓冲区中保存的内容是以struct logger_entry为单位的,每个单位的组成为:
struct logger_entry | priority | tag | msg
由于是内存缓冲区buffer是一个循环使用的环形缓冲区,给定一个偏移值,它在buffer中的位置由下logger_offset来确定:
#define logger_offset(n) ((n) & (log->size - 1))
二. Logger驱动程序模块的初始化过程分析。
继续看logger.c文件,定义了三个日志设备:
- /*
- *Definesalogstructurewithname'NAME'andasizeof'SIZE'bytes,which
- *mustbeapoweroftwo,greaterthanLOGGER_ENTRY_MAX_LEN,andlessthan
- *LONG_MAXminusLOGGER_ENTRY_MAX_LEN.
- */
- #defineDEFINE_LOGGER_DEVICE(VAR,NAME,SIZE)\
- staticunsignedchar_buf_##VAR[SIZE];\
- staticstructlogger_logVAR={\
- .buffer=_buf_##VAR,\
- .misc={\
- .minor=MISC_DYNAMIC_MINOR,\
- .name=NAME,\
- .fops=&logger_fops,\
- .parent=NULL,\
- },\
- .wq=__WAIT_QUEUE_HEAD_INITIALIZER(VAR.wq),\
- .readers=LIST_HEAD_INIT(VAR.readers),\
- .mutex=__MUTEX_INITIALIZER(VAR.mutex),\
- .w_off=0,\
- .head=0,\
- .size=SIZE,\
- };
- DEFINE_LOGGER_DEVICE(log_main,LOGGER_LOG_MAIN,64*1024)
- DEFINE_LOGGER_DEVICE(log_events,LOGGER_LOG_EVENTS,256*1024)
- DEFINE_LOGGER_DEVICE(log_radio,LOGGER_LOG_RADIO,64*1024)
#define LOGGER_LOG_RADIO "log_radio"/* radio-related messages */
#define LOGGER_LOG_EVENTS "log_events"/* system/hardware events */
#define LOGGER_LOG_MAIN "log_main"/* everything else */
注释说明了这三个日志设备的用途。注册的日志设备文件操作方法为logger_fops:
- staticstructfile_operationslogger_fops={
- .owner=THIS_MODULE,
- .read=logger_read,
- .aio_write=logger_aio_write,
- .poll=logger_poll,
- .unlocked_ioctl=logger_ioctl,
- .compat_ioctl=logger_ioctl,
- .open=logger_open,
- .release=logger_release,
- };
日志驱动程序模块的初始化函数为logger_init:
- staticint__initlogger_init(void)
- {
- intret;
- ret=init_log(&log_main);
- if(unlikely(ret))
- gotoout;
- ret=init_log(&log_events);
- if(unlikely(ret))
- gotoout;
- ret=init_log(&log_radio);
- if(unlikely(ret))
- gotoout;
- out:
- returnret;
- }
- device_initcall(logger_init);
- staticint__initinit_log(structlogger_log*log)
- {
- intret;
- ret=misc_register(&log->misc);
- if(unlikely(ret)){
- printk(KERN_ERR"logger:failedtoregistermisc"
- "deviceforlog'%s'!\n",log->misc.name);
- returnret;
- }
- printk(KERN_INFO"logger:created%luKlog'%s'\n",
- (unsignedlong)log->size>>10,log->misc.name);
- return0;
- }
- /**
- *misc_register-registeramiscellaneousdevice
- *@misc:devicestructure
- *
- *Registeramiscellaneousdevicewiththekernel.Iftheminor
- *numberissetto%MISC_DYNAMIC_MINORaminornumberisassigned
- *andplacedintheminorfieldofthestructure.Forothercases
- *theminornumberrequestedisused.
- *
- *Thestructurepassedislinkedintothekernelandmaynotbe
- *destroyeduntilithasbeenunregistered.
- *
- *Azeroisreturnedonsuccessandanegativeerrnocodefor
- *failure.
- */
- intmisc_register(structmiscdevice*misc)
- {
- structmiscdevice*c;
- dev_tdev;
- interr=0;
- INIT_LIST_HEAD(&misc->list);
- mutex_lock(&misc_mtx);
- list_for_each_entry(c,&misc_list,list){
- if(c->minor==misc->minor){
- mutex_unlock(&misc_mtx);
- return-EBUSY;
- }
- }
- if(misc->minor==MISC_DYNAMIC_MINOR){
- inti=DYNAMIC_MINORS;
- while(--i>=0)
- if((misc_minors[i>>3]&(1<<(i&7)))==0)
- break;
- if(i<0){
- mutex_unlock(&misc_mtx);
- return-EBUSY;
- }
- misc->minor=i;
- }
- if(misc->minor<DYNAMIC_MINORS)
- misc_minors[misc->minor>>3]|=1<<(misc->minor&7);
- dev=MKDEV(MISC_MAJOR,misc->minor);
- misc->this_device=device_create(misc_class,misc->parent,dev,NULL,
- "%s",misc->name);
- if(IS_ERR(misc->this_device)){
- err=PTR_ERR(misc->this_device);
- gotoout;
- }
- /*
- *Addittothefront,sothatlaterdevicescan"override"
- *earlierdefaults
- */
- list_add(&misc->list,&misc_list);
- out:
- mutex_unlock(&misc_mtx);
- returnerr;
- }
三. Logger驱动程序的日志记录读取过程分析。
继续看logger.c 文件,注册的读取日志设备文件的方法为logger_read:
- /*
- *logger_read-ourlog'sread()method
- *
- *Behavior:
- *
- *-O_NONBLOCKworks
- *-Iftherearenologentriestoread,blocksuntillogiswrittento
- *-Atomicallyreadsexactlyonelogentry
- *
- *OptimalreadsizeisLOGGER_ENTRY_MAX_LEN.WillseterrnotoEINVALifread
- *bufferisinsufficienttoholdnextentry.
- */
- staticssize_tlogger_read(structfile*file,char__user*buf,
- size_tcount,loff_t*pos)
- {
- structlogger_reader*reader=file->private_data;
- structlogger_log*log=reader->log;
- ssize_tret;
- DEFINE_WAIT(wait);
- start:
- while(1){
- prepare_to_wait(&log->wq,&wait,TASK_INTERRUPTIBLE);
- mutex_lock(&log->mutex);
- ret=(log->w_off==reader->r_off);
- mutex_unlock(&log->mutex);
- if(!ret)
- break;
- if(file->f_flags&O_NONBLOCK){
- ret=-EAGAIN;
- break;
- }
- if(signal_pending(current)){
- ret=-EINTR;
- break;
- }
- schedule();
- }
- finish_wait(&log->wq,&wait);
- if(ret)
- returnret;
- mutex_lock(&log->mutex);
- /*istherestillsomethingtoreadordidwerace?*/
- if(unlikely(log->w_off==reader->r_off)){
- mutex_unlock(&log->mutex);
- gotostart;
- }
- /*getthesizeofthenextentry*/
- ret=get_entry_len(log,reader->r_off);
- if(count<ret){
- ret=-EINVAL;
- gotoout;
- }
- /*getexactlyoneentryfromthelog*/
- ret=do_read_log_to_user(log,reader,buf,ret);
- out:
- mutex_unlock(&log->mutex);
- returnret;
- }
- /*
- *logger_open-thelog'sopen()fileoperation
- *
- *Notehownearano-opthisisinthewrite-onlycase.Keepitthatway!
- */
- staticintlogger_open(structinode*inode,structfile*file)
- {
- structlogger_log*log;
- intret;
- ret=nonseekable_open(inode,file);
- if(ret)
- returnret;
- log=get_log_from_minor(MINOR(inode->i_rdev));
- if(!log)
- return-ENODEV;
- if(file->f_mode&FMODE_READ){
- structlogger_reader*reader;
- reader=kmalloc(sizeof(structlogger_reader),GFP_KERNEL);
- if(!reader)
- return-ENOMEM;
- reader->log=log;
- INIT_LIST_HEAD(&reader->list);
- mutex_lock(&log->mutex);
- reader->r_off=log->head;
- list_add_tail(&reader->list,&log->readers);
- mutex_unlock(&log->mutex);
- file->private_data=reader;
- }else
- file->private_data=log;
- return0;
- }
start标号处的while循环是在等待日志可读,如果已经没有新的日志可读了,那么就要读进程就要进入休眠状态,等待新的日志写入后再唤醒,这是通过prepare_wait和schedule两个调用来实现的。如果没有新的日志可读,并且设备文件不是以非阻塞O_NONBLOCK的方式打开或者这时有信号要处理(signal_pending(current)),那么就直接返回,不再等待新的日志写入。判断当前是否有新的日志可读的方法是:
ret = (log->w_off == reader->r_off);
即判断当前缓冲区的写入位置和当前读进程的读取位置是否相等,如果不相等,则说明有新的日志可读。
继续向下看,如果有新的日志可读,那么就,首先通过get_entry_len来获取下一条可读的日志记录的长度,从这里可以看出,日志读取进程是以日志记录为单位进行读取的,一次只读取一条记录。get_entry_len的函数实现如下:
- /*
- *get_entry_len-Grabsthelengthofthepayloadofthenextentrystarting
- *from'off'.
- *
- *Callerneedstoholdlog->mutex.
- */
- static__u32get_entry_len(structlogger_log*log,size_toff)
- {
- __u16val;
- switch(log->size-off){
- case1:
- memcpy(&val,log->buffer+off,1);
- memcpy(((char*)&val)+1,log->buffer,1);
- break;
- default:
- memcpy(&val,log->buffer+off,2);
- }
- returnsizeof(structlogger_entry)+val;
- }
接着往下看,得到了要读取的记录的长度,就调用do_read_log_to_user函数来执行真正的读取动作:
- staticssize_tdo_read_log_to_user(structlogger_log*log,
- structlogger_reader*reader,
- char__user*buf,
- size_tcount)
- {
- size_tlen;
- /*
- *Wereadfromthelogintwodisjointoperations.First,wereadfrom
- *thecurrentreadheadoffsetupto'count'bytesortotheendof
- *thelog,whichevercomesfirst.
- */
- len=min(count,log->size-reader->r_off);
- if(copy_to_user(buf,log->buffer+reader->r_off,len))
- return-EFAULT;
- /*
- *Second,wereadanyremainingbytes,startingbackattheheadof
- *thelog.
- */
- if(count!=len)
- if(copy_to_user(buf+len,log->buffer,count-len))
- return-EFAULT;
- reader->r_off=logger_offset(reader->r_off+count);
- returncount;
- }
四. Logger驱动程序的日志记录写入过程分析。
继续看logger.c 文件,注册的写入日志设备文件的方法为logger_aio_write:
- /*
- *logger_aio_write-ourwritemethod,implementingsupportforwrite(),
- *writev(),andaio_write().Writesareourfastpath,andwetrytooptimize
- *themaboveallelse.
- */
- ssize_tlogger_aio_write(structkiocb*iocb,conststructiovec*iov,
- unsignedlongnr_segs,loff_tppos)
- {
- structlogger_log*log=file_get_log(iocb->ki_filp);
- size_torig=log->w_off;
- structlogger_entryheader;
- structtimespecnow;
- ssize_tret=0;
- 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);
- /*nullwritessucceed,returnzero*/
- if(unlikely(!header.len))
- return0;
- mutex_lock(&log->mutex);
- /*
- *Fixupanyreaders,pullingthemforwardtothefirstreadable
- *entryafter(whatwillbe)thenewwriteoffset.Wedothisnow
- *becauseifwepartiallyfail,wecanendupwithclobberedlog
- *entriesthatencroachonreadablebuffer.
- */
- fix_up_readers(log,sizeof(structlogger_entry)+header.len);
- do_write_log(log,&header,sizeof(structlogger_entry));
- while(nr_segs-->0){
- size_tlen;
- ssize_tnr;
- /*figureouthowmuchofthisvectorwecankeep*/
- len=min_t(size_t,iov->iov_len,header.len-ret);
- /*writeoutthissegment'spayload*/
- nr=do_write_log_from_user(log,iov->iov_base,len);
- if(unlikely(nr<0)){
- log->w_off=orig;
- mutex_unlock(&log->mutex);
- returnnr;
- }
- iov++;
- ret+=nr;
- }
- mutex_unlock(&log->mutex);
- /*wakeupanyblockedreaders*/
- wake_up_interruptible(&log->wq);
- returnret;
- }
struct logger_entry | priority | tag | msg
其中, priority、tag和msg这三个段的内容是由iov参数从用户空间传递下来的,分别对应iov里面的三个元素。而logger_entry是由内核空间来构造的:
struct logger_entry header;
struct timespec 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);
然后调用do_write_log首先把logger_entry结构体写入到日志缓冲区中:
- /*
- *do_write_log-writes'len'bytesfrom'buf'to'log'
- *
- *Thecallerneedstoholdlog->mutex.
- */
- staticvoiddo_write_log(structlogger_log*log,constvoid*buf,size_tcount)
- {
- size_tlen;
- len=min(count,log->size-log->w_off);
- memcpy(log->buffer+log->w_off,buf,len);
- if(count!=len)
- memcpy(log->buffer,buf+len,count-len);
- log->w_off=logger_offset(log->w_off+count);
- }
接着,通过一个while循环把iov的内容写入到日志缓冲区中,也就是日志的优先级别priority、日志Tag和日志主体Msg:
- while(nr_segs-->0){
- size_tlen;
- ssize_tnr;
- /*figureouthowmuchofthisvectorwecankeep*/
- len=min_t(size_t,iov->iov_len,header.len-ret);
- /*writeoutthissegment'spayload*/
- nr=do_write_log_from_user(log,iov->iov_base,len);
- if(unlikely(nr<0)){
- log->w_off=orig;
- mutex_unlock(&log->mutex);
- returnnr;
- }
- iov++;
- ret+=nr;
- }
- staticssize_tdo_write_log_from_user(structlogger_log*log,
- constvoid__user*buf,size_tcount)
- {
- size_tlen;
- len=min(count,log->size-log->w_off);
- if(len&©_from_user(log->buffer+log->w_off,buf,len))
- return-EFAULT;
- if(count!=len)
- if(copy_from_user(log->buffer,buf+len,count-len))
- return-EFAULT;
- log->w_off=logger_offset(log->w_off+count);
- returncount;
- }
- /*
- *Fixupanyreaders,pullingthemforwardtothefirstreadable
- *entryafter(whatwillbe)thenewwriteoffset.Wedothisnow
- *becauseifwepartiallyfail,wecanendupwithclobberedlog
- *entriesthatencroachonreadablebuffer.
- */
- fix_up_readers(log,sizeof(structlogger_entry)+header.len);
- /*
- *fix_up_readers-walkthelistofallreadersand"fixup"anywhowere
- *lappedbythewriter;alsodothesameforthedefault"starthead".
- *Wedothisby"pullingforward"thereadersandstartheadtothefirst
- *entryafterthenewwritehead.
- *
- *Thecallerneedstoholdlog->mutex.
- */
- staticvoidfix_up_readers(structlogger_log*log,size_tlen)
- {
- size_told=log->w_off;
- size_tnew=logger_offset(old+len);
- structlogger_reader*reader;
- if(clock_interval(old,new,log->head))
- log->head=get_next_entry(log,log->head,len);
- list_for_each_entry(reader,&log->readers,list)
- if(clock_interval(old,new,reader->r_off))
- reader->r_off=get_next_entry(log,reader->r_off,len);
- }
- /*
- *get_next_entry-returntheoffsetofthefirstvalidentryatleast'len'
- *bytesafter'off'.
- *
- *Callermustholdlog->mutex.
- */
- staticsize_tget_next_entry(structlogger_log*log,size_toff,size_tlen)
- {
- size_tcount=0;
- do{
- size_tnr=get_entry_len(log,off);
- off=logger_offset(off+nr);
- count+=nr;
- }while(count<len);
- returnoff;
- }
- /*
- *clock_interval-isa<c<binmod-space?Putanotherway,doestheline
- *fromatobcrossc?
- */
- staticinlineintclock_interval(size_ta,size_tb,size_tc)
- {
- if(b<a){
- if(a<c||b>=c)
- return1;
- }else{
- if(a<c&&b>=c)
- return1;
- }
- return0;
- }
/* wake up any blocked readers */
wake_up_interruptible(&log->wq);
至此, Logger驱动程序的主要逻辑就分析完成了,还有其它的一些接口,如logger_poll、logger_ioctl和logger_release函数,比较简单,读取可以自行分析。这里还需要提到的一点是,由于Logger驱动程序模块在退出系统时,是不会卸载的,所以这个模块没有module_exit函数,而对于模块里面定义的对象,也没有用对引用计数技术。
这篇文章着重介绍了Android日志系统在内核空间的实现,在下一篇文章中,我们将接着介绍在用户空间中,提供给Android应用程序使用的Java和C/C++ LOG调用接口的实现过程,敬请关注。
评论