Android日志系统驱动程序Logger源代码分析.docx_第1页
Android日志系统驱动程序Logger源代码分析.docx_第2页
Android日志系统驱动程序Logger源代码分析.docx_第3页
Android日志系统驱动程序Logger源代码分析.docx_第4页
Android日志系统驱动程序Logger源代码分析.docx_第5页
已阅读5页,还剩15页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

我们知道,在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头文件的内容:view plain1. #ifndef_LINUX_LOGGER_H2. #define_LINUX_LOGGER_H3. 4. #include5. #include6. 7. structlogger_entry8. _u16len;/*lengthofthepayload*/9. _u16_pad;/*nomatterwhat,weget2bytesofpadding*/10. _s32pid;/*generatingprocessspid*/11. _s32tid;/*generatingprocessstid*/12. _s32sec;/*secondssinceEpoch*/13. _s32nsec;/*nanoseconds*/14. charmsg0;/*theentryspayload*/15. ;16. 17. #defineLOGGER_LOG_RADIOlog_radio/*radio-relatedmessages*/18. #defineLOGGER_LOG_EVENTSlog_events/*system/hardwareevents*/19. #defineLOGGER_LOG_MAINlog_main/*everythingelse*/20. 21. #defineLOGGER_ENTRY_MAX_LEN(4*1024)22. #defineLOGGER_ENTRY_MAX_PAYLOAD23. (LOGGER_ENTRY_MAX_LEN-sizeof(structlogger_entry)24. 25. #define_LOGGERIO0xAE26. 27. #defineLOGGER_GET_LOG_BUF_SIZE_IO(_LOGGERIO,1)/*sizeoflog*/28. #defineLOGGER_GET_LOG_LEN_IO(_LOGGERIO,2)/*usedloglen*/29. #defineLOGGER_GET_NEXT_ENTRY_LEN_IO(_LOGGERIO,3)/*nextentrylen*/30. #defineLOGGER_FLUSH_LOG_IO(_LOGGERIO,4)/*flushlog*/31. 32. #endif/*_LINUX_LOGGER_H*/ struct 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文件中,其它相关数据结构的定义:view plain1. /*2. *structlogger_log-representsaspecificlog,suchasmainorradio3. *4. *Thisstructurelivesfrommoduleinsertionuntilmoduleremoval,soitdoes5. *notneedadditionalreferencecounting.Thestructureisprotectedbythe6. *mutexmutex.7. */8. structlogger_log9. unsignedchar*buffer;/*theringbufferitself*/10. structmiscdevicemisc;/*miscdevicerepresentingthelog*/11. wait_queue_head_twq;/*waitqueueforreaders*/12. structlist_headreaders;/*thislogsreaders*/13. structmutexmutex;/*mutexprotectingbuffer*/14. size_tw_off;/*currentwriteheadoffset*/15. size_thead;/*newreadersstarthere*/16. size_tsize;/*sizeofthelog*/17. ;18. 19. /*20. *structlogger_reader-aloggingdeviceopenforreading21. *22. *Thisobjectlivesfromopentorelease,sowedontneedadditional23. *referencecounting.Thestructureisprotectedbylog-mutex.24. */25. structlogger_reader26. structlogger_log*log;/*associatedlog*/27. structlist_headlist;/*entryinlogger_logslist*/28. size_tr_off;/*currentreadheadoffset*/29. ;30. 31. /*logger_offset-returnsindexnintothelogvia(optimized)modulus*/32. #definelogger_offset(n)(n)&(log-size-1) 结构体struct logger_log就是真正用来保存日志的地方了。buffer成员变量变是用保存日志信息的内存缓冲区,它的大小由size成员变量确定。从misc成员变量可以看出,logger驱动程序使用的设备属于misc类型的设备,通过在Android模拟器上执行cat /proc/devices命令(可参考在Ubuntu上下载、编译和安装Android最新内核源代码(Linux Kernel)一文),可以看出,misc类型设备的主设备号是10。关于主设备号的相关知识,可以参考Android学习启动篇一文中提到的Linux Driver Development一书。wq成员变量是一个等待队列,用于保存正在等待读取日志的进程。readers成员变量用来保存当前正在读取日志的进程,正在读取日志的进程由结构体logger_reader来描述。mutex成员变量是一个互斥量,用来保护log的并发访问。可以看出,这里的日志系统的读写问题,其实是一个生产者-消费者的问题,因此,需要互斥量来保护log的并发访问。 w_off成员变量用来记录下一条日志应该从哪里开始写。head成员变量用来表示打开日志文件中,应该从哪一个位置开始读取日志。 结构体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文件,定义了三个日志设备:view plain1. /*2. *DefinesalogstructurewithnameNAMEandasizeofSIZEbytes,which3. *mustbeapoweroftwo,greaterthanLOGGER_ENTRY_MAX_LEN,andlessthan4. *LONG_MAXminusLOGGER_ENTRY_MAX_LEN.5. */6. #defineDEFINE_LOGGER_DEVICE(VAR,NAME,SIZE)7. staticunsignedchar_buf_#VARSIZE;8. staticstructlogger_logVAR=9. .buffer=_buf_#VAR,10. .misc=11. .minor=MISC_DYNAMIC_MINOR,12. .name=NAME,13. .fops=&logger_fops,14. .parent=NULL,15. ,16. .wq=_WAIT_QUEUE_HEAD_INITIALIZER(VAR.wq),17. .readers=LIST_HEAD_INIT(VAR.readers),18. .mutex=_MUTEX_INITIALIZER(VAR.mutex),19. .w_off=0,20. .head=0,21. .size=SIZE,22. ;23. 24. DEFINE_LOGGER_DEVICE(log_main,LOGGER_LOG_MAIN,64*1024)25. DEFINE_LOGGER_DEVICE(log_events,LOGGER_LOG_EVENTS,256*1024)26. DEFINE_LOGGER_DEVICE(log_radio,LOGGER_LOG_RADIO,64*1024) 分别是log_main、log_events和log_radio,名称分别LOGGER_LOG_MAIN、LOGGER_LOG_EVENTS和LOGGER_LOG_RADIO,它们的次设备号为MISC_DYNAMIC_MINOR,即为在注册时动态分配。在logger.h文件中,有这三个宏的定义: #define LOGGER_LOG_RADIOlog_radio/* radio-related messages */ #define LOGGER_LOG_EVENTSlog_events/* system/hardware events */ #define LOGGER_LOG_MAINlog_main/* everything else */ 注释说明了这三个日志设备的用途。注册的日志设备文件操作方法为logger_fops:view plain1. staticstructfile_operationslogger_fops=2. .owner=THIS_MODULE,3. .read=logger_read,4. .aio_write=logger_aio_write,5. .poll=logger_poll,6. .unlocked_ioctl=logger_ioctl,7. .compat_ioctl=logger_ioctl,8. .open=logger_open,9. .release=logger_release,10. ; 日志驱动程序模块的初始化函数为logger_init:view plain1. staticint_initlogger_init(void)2. 3. intret;4. 5. ret=init_log(&log_main);6. if(unlikely(ret)7. gotoout;8. 9. ret=init_log(&log_events);10. if(unlikely(ret)11. gotoout;12. 13. ret=init_log(&log_radio);14. if(unlikely(ret)15. gotoout;16. 17. out:18. returnret;19. 20. device_initcall(logger_init); logger_init函数通过调用init_log函数来初始化了上述提到的三个日志设备:view plain1. staticint_initinit_log(structlogger_log*log)2. 3. intret;4. 5. ret=misc_register(&log-misc);6. if(unlikely(ret)7. printk(KERN_ERRlogger:failedtoregistermisc8. deviceforlog%s!n,);9. returnret;10. 11. 12. printk(KERN_INFOlogger:created%luKlog%sn,13. (unsignedlong)log-size10,);14. 15. return0;16. init_log函数主要调用了misc_register函数来注册misc设备,misc_register函数定义在kernel/common/drivers/char/misc.c文件中:view plain1. /*2. *misc_register-registeramiscellaneousdevice3. *misc:devicestructure4. *5. *Registeramiscellaneousdevicewiththekernel.Iftheminor6. *numberissetto%MISC_DYNAMIC_MINORaminornumberisassigned7. *andplacedintheminorfieldofthestructure.Forothercases8. *theminornumberrequestedisused.9. *10. *Thestructurepassedislinkedintothekernelandmaynotbe11. *destroyeduntilithasbeenunregistered.12. *13. *Azeroisreturnedonsuccessandanegativeerrnocodefor14. *failure.15. */16. 17. intmisc_register(structmiscdevice*misc)18. 19. structmiscdevice*c;20. dev_tdev;21. interr=0;22. 23. INIT_LIST_HEAD(&misc-list);24. 25. mutex_lock(&misc_mtx);26. list_for_each_entry(c,&misc_list,list)27. if(c-minor=misc-minor)28. mutex_unlock(&misc_mtx);29. return-EBUSY;30. 31. 32. 33. if(misc-minor=MISC_DYNAMIC_MINOR)34. inti=DYNAMIC_MINORS;35. while(-i=0)36. if(misc_minorsi3&(1(i&7)=0)37. break;38. if(iminor=i;43. 44. 45. if(misc-minorminor3|=1minor&7);47. dev=MKDEV(MISC_MAJOR,misc-minor);48. 49. misc-this_device=device_create(misc_class,misc-parent,dev,NULL,50. %s,misc-name);51. if(IS_ERR(misc-this_device)52. err=PTR_ERR(misc-this_device);53. gotoout;54. 55. 56. /*57. *Addittothefront,sothatlaterdevicescanoverride58. *earlierdefaults59. */60. list_add(&misc-list,&misc_list);61. out:62. mutex_unlock(&misc_mtx);63. returnerr;64. 注册完成后,通过device_create创建设备文件节点。这里,将创建/dev/log/main、/dev/log/events和/dev/log/radio三个设备文件,这样,用户空间就可以通过读写这三个文件和驱动程序进行交互。 三. Logger驱动程序的日志记录读取过程分析。 继续看logger.c 文件,注册的读取日志设备文件的方法为logger_read:view plain1. /*2. *logger_read-ourlogsread()method3. *4. *Behavior:5. *6. *-O_NONBLOCKworks7. *-Iftherearenologentriestoread,blocksuntillogiswrittento8. *-Atomicallyreadsexactlyonelogentry9. *10. *OptimalreadsizeisLOGGER_ENTRY_MAX_LEN.WillseterrnotoEINVALifread11. *bufferisinsufficienttoholdnextentry.12. */13. staticssize_tlogger_read(structfile*file,char_user*buf,14. size_tcount,loff_t*pos)15. 16. structlogger_reader*reader=file-private_data;17. structlogger_log*log=reader-log;18. ssize_tret;19. DEFINE_WAIT(wait);20. 21. start:22. while(1)23. prepare_to_wait(&log-wq,&wait,TASK_INTERRUPTIBLE);24. 25. mutex_lock(&log-mutex);26. ret=(log-w_off=reader-r_off);27. mutex_unlock(&log-mutex);28. if(!ret)29. break;30. 31. if(file-f_flags&O_NONBLOCK)32. ret=-EAGAIN;33. break;34. 35. 36. if(signal_pending(current)37. ret=-EINTR;38. break;39. 40. 41. schedule();42. 43. 44. finish_wait(&log-wq,&wait);45. if(ret)46. returnret;47. 48. mutex_lock(&log-mutex);49. 50. /*istherestillsomethingtoreadordidwerace?*/51. if(unlikely(log-w_off=reader-r_off)52. mutex_unlock(&log-mutex);53. gotostart;54. 55. 56. /*getthesizeofthenextentry*/57. ret=get_entry_len(log,reader-r_off);58. if(countmutex);68. 69. returnret;70. 注意,在函数开始的地方,表示读取日志上下文的struct logger_reader是保存在文件指针的private_data成员变量里面的,这是在打开设备文件时设置的,设备文件打开方法为logger_open:view plain1. /*2. *logger_open-thelogsopen()fileoperation3. *4. *Notehownearano-opthisisinthewrite-onlycase.Keepitthatway!5. */6. staticintlogger_open(structinode*inode,structfile*file)7. 8. structlogger_log*log;9. intret;10. 11. ret=nonseekable_open(inode,file);12. if(ret)13. returnret;14. 15. log=get_log_from_minor(MINOR(inode-i_rdev);16. if(!log)17. return-ENODEV;18. 19. if(file-f_mode&FMODE_READ)20. structlogger_reader*reader;21. 22. reader=kmalloc(sizeof(structlogger_reader),GFP_KERNEL);23. if(!reader)24. return-ENOMEM;25. 26. reader-log=log;27. INIT_LIST_HEAD(&reader-list);28. 29. mutex_lock(&log-mutex);30. reader-r_off=log-head;31. list_add_tail(&reader-list,&log-readers);32. mutex_unlock(&log-mutex);33. 34. file-private_data=reader;35. else36. file-private_data=log;37. 38. return0;39. 新打开日志设备文件时,是从log-head位置开始读取日志的,保存在struct logger_reader的成员变量r_off中。 start标号处的while循环是在等待日志可读,如果已经没有新的日志可读了,那么就要读进程就要进入休眠状态,等待新的日志写入后再唤醒,这是通过prepare_wait和schedule两个调用来实现的。如果没有新的日志可读,并且设备文件不是以非阻塞O_NONBLOCK的方式打开或者这时有信号要处理(signal_pending(current)),那么就直接返回,不再等待新的日志写入。判断当前是否有新的日志可读的方法是: ret = (log-w_off = reader-r_off); 即判断当前缓冲区的写入位置和当前读进程的读取位置是否相等,如果不相等,则说明有新的日志可读。 继续向下看,如果有新的日志可读,那么就,首先通过get_entry_len来获取下一条可读的日志记录的长度,从这里可以看出,日志读取进程是以日志记录为单位进行读取的,一次只读取一条记录。get_entry_len的函数实现如下:view plain1. /*2. *get_entry_len-Grabsthelengthofthepayloadofthenextentrystarting3. *fromoff.4. *5. *Callerneedstoholdlog-mutex.6. */7. static_u32get_entry_len(structlogger_log*log,size_toff)8. 9. _u16val;10. 11. switch(log-size-off)12. case1:13. memcpy(&val,log-buffer+off,1);14. memcpy(char*)&val)+1,log-buffer,1);15. break;16. default:17. memcpy(&val,log-buffer+off,2);18. 19. 20. returnsizeof(structlogger_entry)+val;21. 上面我们提到,每一条日志记录是由两大部分组成的,一个用于描述这条日志记录的结构体struct logger_entry,另一个是记录体本身,即有效负载。结构体struct logger_entry的长度是固定的,只要知道有效负载的长度,就可以知道整条日志记录的长度了。而有效负载的长度是记录在结构体struct logger_entry的成员变量len中,而len成员变量的地址与struct logger_entry的地址相同,因此,只需要读取记录的开始位置的两个字节就可以了。又由于日志记录缓冲区是循环使用的,这两个节字有可能是第一个字节存放在缓冲区最后一个字节,而第二个字节存放在缓冲区的第一个节,除此之外,这两个字节都是连在一起的。因此,分两种情况来考虑,对于前者,分别通过读取缓冲区最后一个字节和第一个字节来得到日志记录的有效负载长度到本地变量val中,对于后者,直接读取连续两个字节的值到本地变量val中。这两种情况是通过判断日志缓冲区的大小和要读取的日志记录在缓冲区中的位置的差值来区别的,如果相差1,就说明是前一种情况了。最后,把有效负载的长度val加上struct logger_entry的长度就得到了要读取的日志记录的总长度了。 接着往下看,得到了要读取的记录的长度,就调用do_read_log_to_user函数来执行真正的读取动作:view plain1. staticssize_tdo_read_log_to_user(structlogger_log*log,2. structlogger_reader*reader,3. char_user*buf,4. size_tcount)5. 6. size_tlen;7. 8. /*9. *Wereadfromthelogintwodisjointoperations.First,wereadfrom10. *thecurrentreadheadoffsetuptocountbytesortotheendof11. *thelog,whichevercomesfirst.12. */13. len=min(count,log-size-reader-r_off);14. if(copy_to_user(buf,log-buffer+reader-r_off,len)15. return-EFAULT;16. 17. /*18. *Second,wereadanyremainingbytes,startingbackattheheadof19. *thelog.20. */21. if(count!=len)22. if(copy_to_user(buf+len,log-buffer,count-len)23. return-EFAULT;24. 25. reader-r_off=logger_offset(reader-r_off+count);26. 27. returncount;28. 这个函数简单地调用copy_to_user函数来把位于内核空间的日志缓冲区指定的内容拷贝到用户空间的内存缓冲区就可以了,同时,把当前读取日志进程的上下文信息中的读偏移r_off前进到下一条日志记录的开始的位置上。 四. Logger驱动程序的日志记录写入过程分析。 继续看logger.c 文件,注册的写入日志设备文件的方法为logger_aio_write:view plain1. /*2. *logger_aio_write-ourwritemethod,implementingsupportforwrite(),3. *writev(),andaio_write().Writesareourfastpath,andwetrytooptimize4. *themaboveallelse.5. */6. ssize_tlogger_aio_write(structkiocb*iocb,conststructiovec*iov,7. unsignedlongnr_segs,loff_tppos)8. 9. structlogger_log*log=file_get_log(iocb-ki_filp);10. size_torig=log-w_off;11. structlogger_entryheader

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论