第一行代码-android安卓技术_第1页
第一行代码-android安卓技术_第2页
第一行代码-android安卓技术_第3页
第一行代码-android安卓技术_第4页
第一行代码-android安卓技术_第5页
已阅读5页,还剩27页未读 继续免费阅读

下载本文档

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

文档简介

❖2❖2章Android的内核机制和结构剖LinuxAndroid的关AndroidLinuxLinuxAndroid在Linux内核的基础上添加了自己所特有的驱动程序。下面我们就来分析一下它们之间究竟有什为什么会选择000支持共享00Linux关于上述特性的信息可以参考Linux2.6版内核的文档,这便于我们在后面的学习中更好地理解Android所特有的功能特性。接下来分析AndroidLinux的关系。Android不是AndroidLinux内核的吗,怎行分析,看完后你就会觉得Android不是Linux了。00glibc 0LinuxQuartz等。不同的操作系统的窗口系统可能不一样,Android并没有使用(也不需要使用)LinuxX窗口系统,这是Android不是Linux的一个基本原因。36❖Android技 ·系统并没有采用glibc作为C库,而是自己开发了一套BionicLibc来代替glibc。AndroidLiunxLiunxBug之外,还增加了AndroidBinderOpenBinder框架的一个驱动,用于提供Android平台的进程间通信(InterProcessCommunication,IPC)功能。源代码位于drivers/staging/android/binder.c。Android电源管理(PM)LinuxAndroid电源低内存管理器(LowMemoryKiller)LinuxOOM(OutOfMemory)机制更加灵活,它可以根据需要杀死进程以释放需要的内存。源代码位于drivers/staging/共享内存(Ashmem)理这个内存的机制。源代码位于mm/ashmem.c。AndroidPMEM(Physical)PMEM用于向用户空间提供连续的物理内存区域,DSP和某些设备只能工作在连续的物理内存上。源代码位于drivers/misc/pmem.c。AndroidLogger一个轻量级的日志设备,用于抓取Android系统的各种日志。源代码位于drivers/staging/android/logger.c。AndroidAlarm提供了一个定时器,用于把设备从睡眠状态唤醒,同时它还提供了一个即使在设备睡眠时也会运行的时钟基准。源代码位于drivers/rtc/alarm.c。USBGadget驱动LinuxUSBgadget驱动框架的设备驱动,AndroidAndroidRamConsole为了提供调试功能,Android允许将调试日志信息写入一个被RAMConsoleRAMBuffer。源代码位于drivers/staging/android/2章Android的内核机制和结构剖析Yaffs2文件系统AndroidYaffs2MTDnandflash文件系统,源代码位于fs/yaffs2/下。Yaffs2NANDNORFlash的跨平台的嵌入式设备Flash文件系统相比,Yaffs2能使用更小的内存来保存其运行状态,因此它占用内存小。Yaffs2的回收非常简单而且快速,因此能表现出更好的性能。Yaffs2在大容量的NANDFlash上的性能表现尤为突出,非常适合大容量的Flash。上面这些要点足以AndroidLinux本书的主要内容将Android的这些特有的部分展开,我们的讲解会尽量通俗易懂,但还是建议大家先复下Linux内核的基本知识。在AndroidLinux内核进行了哪些改动,在移植时AndroidLinux内核的armv5构架/LCD输出。这个模拟器其实是在qemu之上开发的,输入/libSDLGoldfishCPU,那么当Android在真实的硬件设备上运行时,我们就需要去掉它,因此,只有知道对Goldfish做了哪些具体改动之后才能正确地去掉。据统计,AndroidGoldfish的改动主要涉及44个文件,具体汇总如下。说明说明12默认配置文3arch/arm/mach-4arch/arm/mach-board-5arch/arm/mach-GoldfishCPU进行启动配6的输入/输7中断请求、输入/输出89arch/arm/mach-arch/arm/mach-arch/arm/mach-获取和设置时添加ARCH_GOLDFISH38❖Android技 ·系统TTY驱为Goldfish的键盘添加配置文GoldfishMMC卡驱drivers/mmc/host/goldfish多驱GoldfishNANDflashdeviceKernel配置选NANDflash驱drivers/mtd/devices/goldfish_nand_regGoldfishbattery(电池)驱动添加kernel配置选能源和电池状态rtc-drivers/rtc/rtc-实时时钟驱drivers/Goldfishdrivers/Goldfishdrivers/framebufferinclude/asm-arm/arch-include/asm-arm/arch-goldfish/entry-include/asm-arm/arch-include/asm-arm/arch-include/asm-arm/arch-include/asm-arm/arch-include/asm-arm/arch-include/asm-arm/arch-include/asm- include/asm-arm/arch-,密度也越来越高(已经出现几十GB大小的NANDFLASHYAFFS2是专门用在FLASH上的文件系统,YAFFS2是“YetAnotherFlashFileSystem,2ndedition”的缩写。YAFFS2为Linux内核提供了一个高效NANDFLASH的接口。但是NANDFLASH的支持并不包含在标准的2.6.25内核中,所以在其中添加了对2章Android的内核机制和结构剖析1YAFFS配2123456789 pat 蓝显的Bug,以及一些与蓝牙调试和控制相关的函数,具体如下所示。12如果HCIUARTDebugKernel配置中,则添加BT_DBG()3添加配置选L2CAP,HCI_CORE,HCI_SOCK,以及通用接口和语4如果CONFIG_ANDROID_PARANOID_NETWORK

牙功能的安全检 net/bluetooth/sco m m_dlc中添加“out”参调度器40❖Android技 ·系统12修改内核的调度3修改CPU调4kernel/time/tick-修改CPU调5include/linux/tick如果CONFIG_NO_HZ被定义,则添加tick_nohz_update_Android新增的驱

IPCBinderIPC(进程间通信)机制。它的进程能够为其他进程提供服务——通过标准的Linux系统调用API。IPCBinder的概念于一家名为Be.Inc的公司,在之前就已经被Palm软件采用了。LowMemoryKiller其实内核里已经有一个类似的功能,名称为oomkiller(outofmemorykiller)。当内存不够的时候,该策略会试图结束一个进程。smm共享内存。该能使得进程间能够享大块的内存。比如说,系可以使用Ahmem保存一些图标,多个应用程序可以这个共享内存来获取这些图标。Ashmem为内核提供了一种回收这些使用完的共享内存块的方法,如果某个进程试图这些已经被回收的内存块,它将会得到错误的返回值,以便它重新进行内存块分配和数据初始化。RAMConsoleandLogDevice为了调试方便,Android添加了一个功能,使调试信息可以输入到一个内存块中。此外,Android还添加了一个独立的日志模块,这样用户空间的AndroidDebugBridge嵌入式设备的调试的确比较麻烦,为了便于调试,设ADB,使用USB作为连接方式,ADB可以看做是连接Android设备和PC机的一套协议。除了这些主要的功能之外,Android还增加了诸如real-timeclock、switch、timedGPIO等功能,所有这些改动和增加包含在以下28个文件之中。1进入配置文2添加switch3添加BINDER_IPC、POWER、POWER_STAT、POWER_ALARMLOGGER、RAM_CONSOLE、TIMED_GPIO、45drivers/android/alarm系统硬件时钟和实时时钟管6IPC机制72章Android的内核机制和结构剖析8RAM控制台和日志设备方便调9GPIO定时驱动GPIO添加配置选GPIO驱ADB配置选编译ADB所需的配置选include/linux/android_aid添加AIDs、INET、时钟功能设GPIOBinderIPCAPI定Logger定内存共享实添加LOW_MEMORY_KILLER配置选当内存过低时,选择并结束进电源高的一个功能。添加了一个新的电源管理系统,不包含原有的apm和dpm等。这项改动主要涉及以下5个文件:123修改Android电源处理方4修改Android电源处理方5修改Android电源处理方杂管理等,共涉及36个文件,如下所示。为了调试方便,Android添加了一个功能,使得调试信息可以输入到一个内存块中。此Android添加了一个独42❖Android技 ·系统12添加HAVE_LATENCYTOP_SUPPORT和3添加dump_task_regs方4解决系统无法重新启动的问5arch/arm/kernel/stacktrace改进调试6arch/arm/mm/abort-7MemorydevicedriverGoldfishTTY8使编译结果输出到/dev/kmemand9CPU运行时打开LEDS,但是屏幕是关闭添加编译ledtrig-drivers/leds/ledtrig-修正实时时钟误差的fs/fat/dir添加VFAT_IOCTL_GET_VOLUME_ID到当内存不足时调整/proc文修正kpagecount_readkpageflags_read返回的一些错简化add_to_pagemap中的错误检include/asm-添加ELF_CORE_COPY_TASK_REGS()宏调用dump_task_include/linux/mm添加shmem_set_file(...)函数原添加VFAT_IOCTL_GET_VOLUME_ID修复run_hrtimer_pending错添加PANIC_TIMEOUT默认为设置默认panic_timeoutkernel配置修复虚拟控制台的错误信mm/filemapmm/tiny-include/linux/sockios添加SIOCKILLADDR控添加tcp_v4_nuke_addr函如果设置CONFIG_SYSFS,编译如果定义CONFIG_ANDROID_PARANOID_NETWORK,则添加安全添加tcp_v4_nuke_addr函net/ipv6/af_inet6如果定义CONFIG_ANDROID_PARANOID_NETWORK,则添加安全AndroidLinux内核的增2章Android的内核机制和结构剖析强,主要包括Alarm(硬件时钟、Ashmem(内存共享、LowMemoryKiller(低内存管Alarm(硬件时钟Alarm就是一个硬件时钟,前面我们已经知道它提供了一个定时器,用于把设备从睡眠状应用都需要Alarm的支持,源代码位于“drivers/rtc/alarm.c”。<,其中定义了一些enum{ANDROID_ALARM_RTC_WAKEUP,主要包括了5种类型的Alarm,_WAKEUP类型表示在触发Alarm时需要唤醒设备,反之则不需要唤醒设备;ANDROID_ALARM_RTC类型表示在指定的某一时刻出发Alarm;AlarmANDROID_ALARM_SYSTEMTIME类型则表示系统时间;ANDROID_ALARM_TYPE_COUNT则是Alram类型的计数。注意注意流逝的时间也包括设备睡眠的时间,流逝时间的计算点从它最后一次启动enumandroid_alarm_return_flagsANDROID_ALARM_RTC_WAKEUP_MASK=1U<<ANDROID_ALARM_RTC_WAKEUP,ANDROID_ALARM_RTC_MASK=1U<<ANDROID_ALARM_RTC,ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP_MASK=1U<<ANDROID_ALARM_ELAPSED_REALTIME_WAKEUP,ANDROID_ALARM_ELAPSED_REALTIME_MASK=1U<<ANDROID_ALARM_ELAPSED_REALTIME,ANDROID_ALARM_SYSTEMTIME_MASK=1U<<ANDROID_ALARM_SYSTEMTIME,ANDROID_ALARM_TIME_CHANGE_MASK=1U<<16Alarm等待、设置Alarm等。下面我们来分析Alarm驱动的具体实现。44❖Android技 ·系统调用,最后退出时需要调用alarm_exit来销毁和卸载Alarm接口及驱动。staticstructtform_driveralarm_driver=.suspend=.resume=.driver=.name=}该参数主要指定了当系统挂起(suspend)和唤醒(Desume)所需要实现的分别为alarm_suspendalarm_resumeAlarm设备驱动的名称设置为了“alarm”。wake_lock_init(&alarm_wake_lock,WAKE_LOCK_SUSPEND,"alarm");wake_lock_init(&alarm_rtc_wake_lock,WAKE_LOCK_SUSPEND,"alarm_rtc");staticstructclass_interfacertc_alarm_interface=.add_dev=.remove_dev=注意wake注意wakelock是一种锁机制,只要有用户持有该锁,系统就无法进入休眠状态, 2章Android的内核机制和结构剖析当arm动们的T间需在作过不被中断,或者在中断之后能告诉其他进程该过程没有完成,不能被请求,因此这里需要通过pin_oc_irqave和pin_unlock_irqretore来对其执行锁定和操作。实现代码如下:static init{unsignedlong structtimespecsystem_time;spin_lock_irqsave(&alarm_slock,elapsed_rtc_delta=timespec_sub(elapsed_rtc_delta,system_time);spin_unlock_irqrestore(&alarm_slock,flags);"alarm_late_init:rtctoelapsedrealtimedelta%ld.%09ld\n",elapsed_rtc_delta.tv_sec,elapsed_rtc_delta.tv_nsec);return}当Alarm退出时,就需要通过class_interface_unregister函数来卸载在初始化时的Alarm接口,通过wake_lock_destroy函数来销毁SUSPENDlock,以及通过tform_driver_unregister函数来卸载Alarm驱动。实现代码如下:static{

exit}将设备转换成rtc_device类型,然后,通过misc_register函数将自己成为一个Misc设备。其staticstructfile_operationsalarm_fops=.owner=.unlocked_ioctl=46❖Android技 ·系统.open=.release=staticstructmiscdevicealarm_device=.minor=.name=.fops=alarm_device中的“.namealarm_fopsAlarm的常用操作,包括打开、释放和I/O控制。这里还需要通过rtc_irq_register函数一个rtc_task,用来处理Alarm触发的方法,其定义如下:staticstructrtc_taskalarm_rtc_task=.func=注注意如果在添加设备的 需要实现:设置时间、设置RTC、获取时间、设置Alarm等待等。Android中最简单的设备驱动——Alarm的实现流程进行了分析,大家应该可以自己绘制出一个流程图来了吧。对于Alarm的具体实现,大家可以参考源代码Ashmem(内存共享Ashmem是Android的内存分配与共享dev下对应的设备文件为/dev/ashmem,(pin/unpin2章Android的内核机制和结构剖析handlerunpinmmapAshmem的内核驱动是如何完成(ashe.h//设备文件名#define //从ASHMEM_PIN#defineASHMEM_NOT_PURGED#defineASHMEM_WAS_PURGED//从ASHMEM_GET_PIN_STATUS返回的值,判断是pin还是unpin#defineASHMEM_IS_UNPINNED0#defineASHMEM_IS_PINNED1structashmem_pin{u32offset;//Ashmem区域的偏移u32 //从偏移量开始的长Ashmempinunpin等操作。接下来看一下2-1Ashmem实现函数列始化函数ashmem_init和退出函数ashmem_exit。ashmem_init的实现很简单,首先,定义一个结构体ashmem_area代表共享内存区;然后,定义一个结构体ashmem_range代表unpinned页面的区域,代码如下:48❖Android技 ·系统structashmem_areacharname[ASHMEM_FULL_NAME_LEN];/*用于/proc/pid/maps中的一个标*/structlist_headunpinned_list;/*所有的共享内存区列表*/structfile /*Ashmem所支持的文件size_tsize; /*字节数*/unsignedlongprot_mask; /*vm_flags*/structashmem_rangestructlist_headlru; /*LRU列表*/structlist_headunpinned; /*unpinned列表*/structashmem_area*asma; /*ashmem_area结构*/size_tpgstart; /*开始页面*/size_t /*结束页面unsignedint /*是否需要清除(ASHMEM_NOT_PURGEDashmem_area的生命周期为open()release()操作之间ashmem_range的生命周期unpinpinkmem_cache_createcache,所需参DDSLABDcacheNULLcacheashmem_rangecache(ashmem_area一样。创建完成之后,通过misc_register函数将Ashmem为misc设备这里需要注意,我们对所创建的这些cache都需要进行回收,因此,再紧接着需调用register_shrinker回收函数ashmem_shrinker。而从图2-1可以看出,ashmem_shrinker实际上是一个真正的回收ashmem_shrinker中定义的ashmem_shrink。到这里,初始化操作则完成了,实现代码如下:static init{intashmem_area_cachep=kmem_cache_create("ashmem_area_cache",sizeof(structashmem_area),0,0,if(unlikely(!ashmem_area_cachep))printk(KERN_ERR"ashmem:failedtocreateslabcache\n");return-ENOMEM;}2章Android的内核机制和结构剖析ashmem_range_cachep=kmem_cache_create("ashmem_range_cache",sizeof(structashmem_range),0,0,if(unlikely(!ashmem_range_cachep))printk(KERN_ERR"ashmem:failedtocreateslabcache\n");return-ENOMEM;}ret=misc_register(&ashmem_misc);if(unlikely(ret)){printk(KERN_ERR"ashmem:failedtoregistermiscdevice!\n");returnret;}/*回收函数*/printk(KERN_INFO"ashmem:initialized\n");return}static exit{int/*卸载回收函数*/ret=misc_deregister(&ashmem_misc);if(unlikely(ret))printk(KERN_ERR"ashmem:failedtounregistermisc/*卸载cache*/printk(KERN_INFO"ashmem:unloaded\n");}现在我们已经很清楚Ashmem的初始化和退出操作了,接下来分析使用Ashmem对内50❖Android技 ·系统staticstructfile_operationsashmem_fops=.owner=.open .release .mmap /*mmap函数.unlocked_ioctl= /*pat_ioctl=staticstructmiscdeviceashmem_misc=.minor=.name=.fops=其中,ashmem_openunpinnedAshmem分配的地址fileprivate_data,这就排除了进程间共享的可能性。ashmem_release方法用于将指定的节点的空间从链表中删除并释放掉。需要的是当使用list_for_each_entry_safe(pos,n,head,member)posnforpos节点的下一个节点的地址,避免因pos节点被释放而造成断链。ashmem_release函数的实现如staticintashmem_release(structinode*ignored,structfile{structashmem_area*asma=file->private_data;structashmem_range*range,*next;list_for_each_entry_safe(range,next,&asma->unpinned_list,unpinned)range_del(range);/*删除*/if(asma->file)fput(asma- (ashmem_area_cachep,return}接下来就是将分配的空间映射到进程空间。在ashmem_mmap函数中需要的是,它借Linuxshmem_file_setup(支撑文件)工具,使得我们不需要自己去实现这一复杂ashmem_mmap的整个实现过程很简单,大家可以参考它的源代码。最后,我们ioctlpinunpin某一段映射的空间的实现方式。ashmem_ioctl函数的功能很获取pin的一些状态。最终对pin/unpin的处理会通过下面这个函数来完成:2章Android的内核机制和结构剖析//pin/unpin处理staticintashmem_pin_unpin(structashmem_area*asma,unsignedlong user//如果页面是unpinned和ASHMEM_IS_PINNED,则返回ASHMEM_IS_UNPINNEDstaticintashmem_get_pin_status(structashmem_area*asma,size_tpgstart,size_t//unpin0//调用者必须持有staticintashmem_unpin(structashmem_area*asma,size_tpgstart,size_t//pinashmem指定的区//返回是否曾被清除过(即ASHMEM_WAS_PURGED或者//调用者必须持有staticintashmem_pin(structashmem_area*asma,size_tpgstart,size_tstaticintashmem_shrink(intnr_to_scan,gfp_t{structashmem_range*range,if(nr_to_scan&&!(gfp_mask& return-1;if(!nr_to_scan)returnlru_count;list_for_each_entry_safe(range,next,&ashmem_lru_list,{structinode*inode=range->asma->file->f_dentry->d_inode;loff_tstart=range->pgstart*PAGE_SIZE;loff_tend=(range->pgend+1)*PAGE_SIZE-1;vmtruncate_range(inode,start,end);range->purged=ASHMEM_WAS_PURGED;nr_to_scan-=range_size(range);if(nr_to_scan<=0)}returnlru_count;}释放。该方被mm/vmscan.c::shrink_slab调用,其中参数nr_to_scan表示有多少个页面对0,则表示查询所有的页面对象总数。而“gfp_mask”是一个配置,返回值使得mutex_lock不能进行安全的死锁。Ashmem700Linux内52❖Android技 ·系统置名字和大小,以及执行pin和unpin操作等。LowMemoryKiller(低内存管理PC来说,内存是至关重要。如果某个程序发生了内存泄漏,那么一般情况下系统就Kill掉。LinuxOOM(OutOfMemory,内存不足)的机制来完Kill掉。Android则使用了一个新的机制——LowMemoryKillerLowMemoryKiller机制的原理以及它是如何选择将被Kill的进程的。LowMemoryKiller在用户空间中指定了一组内存临界值,当其中的某个值与进程描述中的oom_adj值在同一范围时,该进程将被Kill掉。通常,在“/sys/module/lowmemorykiller/oom_adj值为0或者更大的进被Kill掉。我们发现在lowmemorykiller.c中就指定了这样的staticintlowmem_adj[6]{staticintlowmem_adj_size=4;staticsize_tlowmem_min [6]={3*512,//2*1024,//4*1024,//16*1024,//staticintlowmem_min_size=2章Android的内核机制和结构剖析❖Dtask_struct->signal_struct->oom_adjKill 后,内核会发送SIGKILL信号将其Kill掉。实际上,LowMemoryKiller驱动程序会认为被用于缓存的空间都要被释放,但是,常的oomkiller被触发之前,进程是不会被Kill掉的。LowMemoryKiller的原理之后,我们再来看如何实现这个驱动。LowMemoryKiller驱动的实现位于drivers/misc/lowmemorykiller.c。static init{return0;}static exit{}在初始化函数lowmem_init中通过register_shrinker了一个shrinker为lowmem_shrinker。其中lowmem_shrinker的定义如下:staticstructshrinkerlowmem_shrinker=.shrink=.seeks=DEFAULT_SEEKS*staticintlowmem_shrink(intnr_to_scan,gfp_t{structtask_struct54❖Android技 ·系统structtask_struct*selected=NULL;intrem=0;inttasksize;inti;intmin_adj=OOM_ADJUST_MAX+1;intselected_tasksize=0;intarray_size=intother_=global_page_state(NR__PAGES);intother_file=global_page_state(NR_FILE_PAGES);if(lowmem_adj_size<array_size)array_size=lowmem_adj_size; _size<array_size)array_size=lowmem_min for(i=0;i<array_size;i++){if(other_ <lowmem_min [i]&&other_file< [i])min_adj=lowmem_adj[i];}}if(nr_to_scan>lowmem_print(3,"lowmem_shrink%d,%x,o %d%d,ma%d\n",nr_to_scan,gfp_mask,other_ ,other_file,min_adj);rem=global_page_state(NR_ACTIVE_ANON)+global_page_state(NR_ACTIVE_FILE)+global_page_state(NR_INACTIVE_ANON)+if(nr_to_scan<=0||min_adj==OOM_ADJUST_MAX+1)return}for_each_process(p){if(p->oomkilladj<min_adj||!p->mm)tasksize=get_mm_rss(p->mm);if(tasksize<=0)if(selected){if(p->oomkilladj<selected->oomkilladj)if(p->oomkilladj==selected->oomkilladj&&tasksize<=selected_tasksize)}2章Android的内核机制和结构剖析selected=p;selected_tasksize=tasksize;lowmem_print(2,"select%d(%s),adj%d,size%d,tokill\n",p->pid,p->comm,p->oomkilladj,tasksize);}if(selected!=NULL)lowmem_print(1,"sendsigkillto%d(%s),adj%d,size%d\n",selected->pid,selected->comm,selected->oomkilladj,_sig(SIGKILL,selected);rem-=selected_tasksize;}lowmem_print(4,"lowmem_shrink%d,%x,return%d\n",nr_to_scan,gfp_mask,rem);return}global_page_state函数。有很多人找不到这个函数,其实它被linux/vmstathzone_stat_itemlinux/mmzoneh中,具enum{NR_ NR_INACTIVE_ANON=NR_LRU_BASE,#ifdefCONFIG_UNEVICTABLE_LRUNR_UNEVICTABLENR_ACTIVE_FILE,/*避免编译错误*/NR_MLOCK=NR_ACTIVE_FILE, /*映射页面 /*使用临时缓冲区#ifdef56❖Android技 ·系统 /*在预定节点上分配 /*从本地页面分配 NR_VM_ZONE_STAT_ITEMS再回过头来看owmem_shrink函数,首先确定我们所定义的lowmem_adj和 需要通过比较lowmem_min 中的空闲空间的值,以确定最小min_adj值(当满足其条件时,通过其数组索引来寻找lowmem_adj中对应元素的值;之后检测min_adj的值是否是初始然后使用循环对每一个进程块进行判断,通过min_adj来寻找满足条件的具体进程(主要包括对oomkilladj和task_struct进行判断);最后,对找到的进程进行NULL判断,通过 _sig(SIGKILL,selected)”发送一条SIGKILL信号到内核,Kill掉被选中的LowMemoryKiller的分析就到这里,在了解了其机制和原理之后,我们发现它的实机制在mm/oom_kill.c中实现,且会被alloc_pages_may_oom调用(最高的被选中并Kill。一般而言,占用内存越多,oom_adj就越大,也就越有可能被选中。Logger(日志设备我们在开发Android应用的过可以很方便地使用Log信息来调试程序,这都归功于Logger这个日志设备来进行调试。Logger一共包括三个设备节点,它们分别是:2章Android的内核机制和结构剖析结构体structlogger_entry其中,len表示日志信息的有效长度;pad目前没有什么实质作用,但是需要使用两个字节来占位;pid表示生成该日志信息的进程的pid;tid表示生成该日志信息的进程的tid;secmsg着该日志的有效信息,即我们前面说的长度为len的日志信息属于有效信息。#defineLOGGER_LOG_RADIO"log_radio"/*无线相关消息*/#defineLOGGER_LOG_EVENTS"log_events"/*系统硬件*/#defineLOGGER_LOG_MAIN"log_main"/*任何*/们上面所说的radio、events和main都将使用logger_log结构体来表示,定义如下:structlogger_logunsignedchar* structmiscdevice struct struct (等待在该设备上日志的进程readers;readers表示日志的readers链表;mutex则是用于多线程同步和保护该结构体的mutex;w_off代表当前写入日志的位置,即在环形缓冲区中缓冲区中(buffer)的偏移量;size则代表该日志的大小,即环形缓冲区中(buffer)的大小。根据上面这个日志设备结构logger_log可以得知,要日志还需要一个用于日志的58❖Android技 ·系统structlogger_readerstructlogger_log* structlist_head logger_reader结构体的实现就很简单,其中log代表相关的日志设备,即当前将要数(logger_loglit(raders;r_off取日志的一个偏移量,即日志设备中将要被的buffer的偏移量。static init{}

intret=init_log(&log_main);if(unlikely(ret))gotoret=init_log(&log_events);if(unlikely(ret))gotoret=init_log(&log_radio);if(unlikely(ret))gotoout;returnret;logger_initinit_log函数来初始化前面所提到的init_log的实现如下:static initinit_log(structlogger_log{intret=misc_register(&log->misc);if(unlikely(ret)){printk(KERN_ERR"logger:failedtoregistermisc""deviceforlog'%s'!\n",log->);return}2章Android的内核机制和结构剖析printk(KERN_INFO"logger:created%luKlog'%s'\n",(unsignedlong)log->size>>10,log->);return}miscdevicemisc我们并没有看到具体的初始化日志设备的操作,那是因为这些工作都由DEFINE_LOGGER_DEVICE宏来完成了,DEFINE_LOGGER_DEVICE的实现如下:#defineDEFINE_LOGGER_DEVICE(VAR,NAME,staticunsignedchar_buf_##VAR[SIZE];staticstructlogger_logVAR={.buffer=_buf_##.misc=.minor=.name=.fops=.parent=.wq WAIT_QUEUE_HEAD_INITIALIZER(VAR.readers=LIST_HEAD_INIT(VAR.mutex MUTEX_INITIALIZER(VAR.w_off=.head=.size=DEFINE_LOGGER_DEVICENAME作为名SIZE作为尺寸来创建一个日志设备。这里需要注意:SIZE2的幂,并ENTRY_MAX_PAYLOAD表示日志的最大有效长度。#defineLOGGER_ENTRY_MAX_LEN #defineLOGGER_ENTRY_MAX_PAYLOAD(LOGGER_ENTRY_MAX_LEN-sizeof(structDEFINE_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)staticstructfile_operationslogger_fops=.owner=60❖Android技 ·系统.read=.aio_write=.poll=.unlocked_ioctl=.compat_ioctl=.open=.release=开日志设备文件的logger_open数据的logger_read,等等。下面,分别对这些函数staticintlogger_open(structinode*inode,structfile{structlogger_log*log;intret;ret=nonseekable_open(inode,file);if(ret)return//判断log=get_log_from_minor(MINOR(inode->i_rdev));if(!log)return-//只读if(file->f_mode&{structlogger_readerreader=kmalloc(sizeof(structlogger_reader),GFP_KERNEL);if(!reader)return-reader->loglog;INIT_LIST_HEAD(&reader-//mutex//指定偏移reader->r_off=log->head;list_add_tail(&reader->list,&log->readers);//保存数据file->private_data=else读写模file->private_data=2章Android的内核机制和结构剖析return}常简单,直接判断日志设备的miscminor参数和minor参数即可,实现代码如下:staticstructlogger_log*get_log_from_minor(int{if(log_main.misc.minor==minor)return&log_main;if(log_events.misc.minor==minor)return&log_events;if(log_radio.misc.minor==minor)return&log_radio;return}logger_open函数,在取得了日志设备的类型之后,我们需要判断其读写模志设备、mutex、偏移量r_off),最后将该logger_reader保存到file->private_data中;如果logfile->private_data中,这样做就方便我们在以后的读写过直接通过file->private_data来取得logger_reader和logger_log。staticintlogger_release(structinode*ignored,structfile{if(file->f_mode&FMODE_READ)structlogger_reader*reader=file->private_data; }return}file->private_data取得其对应staticssize_tlogger_read(structfile*file,char user*buf,size_tcount,loff_t*pos){//通过file->private_data获取logger_reader及其日志设备logger_logstructlogger_reader*reader=file->private_data;structlogger_log*log=reader-62❖Android技 ·系统ssize_twhile(1)//添加进程到等待队prepare_to_wait(&log->wq,&wait,TASK_INTERRUPTIBLE);ret=(log->w_off==reader->r_off);ifif(file->f_flags&{ret=-}if{ret=-}}finish_wait(&log->wq,&wait);if(ret)returnret;if(unlikely(log->w_off==reader-gotostart;}//下一条日ret=get_entry_len(log,reader->r_off);if(count<ret){ret=-goto}

//到用户空ret=do_read_log_to_user(log,reader,buf,returnret;while循环来重复该过程,直到buffer中有可供的日志为止。最后,通过get_entry_len函数下一条日志,并通过do_read_log_to_user将其到用户空间,完毕。2章Android的内核机制和结构剖析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=//初始化日志数据logger_entryheader.pidcurrent->tgid;header.tid=current->pid;header.sec=now.tv_sec;head

温馨提示

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

评论

0/150

提交评论