2009年2月23日星期一

浅析GTK 输入法模块

浅析GTK 输入法模块 GTK IMContext


作者:刘旭晖 Raymond转载请注明出处


Emailcolorant@163.com


BLOGhttp://blog.csdn.net/colorant/


主页:http://sites.google.com/site/rgbbones/


 


 

这段时间因为需要移植一个输入法程序,所以学习研究了GTK下输入法实现的相关程序,这才发现输入法所涉及到的内容不光是输入法应用程序本身,GTKXWindow系统中,还必须要有相应的模块支持,这篇文档是我对于GTK的输入法支持模块GtkIMContext的一点粗浅的理解。



1相关参考资料


http://library.gnome.org/devel/gtk/stable/GtkIMContext.html GtkIMContextAPI文档


http://www.ibm.com/developerworks/cn/linux/i18n/xim/xim-2/index.html XIM协议的原理及其实现,这篇文章和本文没有太大的关系,但是可以了解一下XWindow系统的输入法协议作为辅助的背景知识。




2IM-Context模块框架




2.1类层次结构




  • GtkIMContext:虚基类,定义了IMContext类支持的信号和接口函数。其主要用途是作为GtkIMMultiContext及其它子类的基类,并提供了一组的函数封装用于调用这些类函数。



  • GtkIMMultiContextGtkIMContext的子类,实现了一个可以动态加载不同的Slave IMContext模块的框架,自己并不真正处理输入法相关的任务,而是作为一个类似二传手的角色,在GTK Widget和具体的Slave IMContext之间传递数据,转发信号。



  • GtkIMContextSimpleGtkIMContext的子类,实现了一个基于查表实现文字检索的IMContext,主要用途:一是作为其它具体的基于查表实现文字检索的IMContext的父类(具体子类只需要添加自己的索引表格即可),二是作为GtkIMMultiContext的一个默认的Slave Context,当特定的Slave由于某些原因无法被创建时,SimpleContext将被创建并返回。



  • Build-in IMContext:除了上述IMContext相关类实现,GTK还自带了GtkIMContextTaiGtkIMContextXIM等基于GtkIMContext实现的具体的全功能IMContext,以及Am_et等基于GtkIMContextSimple的查表类的IMContext的具体实现。



  • Other IMContext:此外,不同的输入法可以根据自己的需要,实现自己的子类。如SCIM基于GtkIMContext实现的GTKIMContextSCIMMatchbox-keyboard基于GtkIMContextSimple实现的MbIMContext




3工作机制


3.1GTK WidgetIMContext的关系


GTKWidget中,涉及到IMContext的主要是和文字编辑相关的GTK Entry GTK TextView这两个Widget。这两个widget的类成员中都包含了一个指向GtkIMContext对象的指针,并在类实例的初始化函数中分配并将指针指向了一个GtkIMMultiContext的实例对象。




GtkIMMultiContext对象在初始化过程中,首先设置其Slave成员指针为空,而后在IMContext相关的后续操作中,再通过gtk_im_multicontext_set_slavegtk_im_multicontext_get_slave函数设置其具体的slave IMContext对象。

 


3.2GtkIMContext成员函数和信号


GtkIMContext的类成员函数中:






  • set_client_window



  • focus_in



  • focus_out


这几个函数主要是由相关的GTK Widget在初始化,以及焦点,可视等状态变化的时候调用,用来通知IMContext相关信息的。具体的IMContext通常用这些函数来初始化事件通讯机制,显示消隐UI界面等。






  • reset



  • set_cursor_location



  • filter_keypress


这几个函数主要是在用户在执行输入等操作时,由相关的GTK Widget根据当前文本的状态进行调用,通知IMContext模块状态重置,光标位置变动(可以用来实现光标跟随)和键值输入的情况。






  • get_surrounding



  • delete_surrounding



  • get_preedit_string



  • set_use_preedit



  • set_surrounding


这几个函数的主要目的是用来实现Preeditauto correct或者其它前后文相关操作功能,前三个函数是由IM端发起调用,用于从相关GTK Widget读写数据。后两个函数则是由Widget端发起调用,用于通知IM是否使用preedit模式,和反馈surrounding数据。




GtkIMContext的信号包括:




  • "commit"



  • "delete-surrounding"



  • "preedit-changed"



  • "preedit-end"



  • "preedit-start"



  • "retrieve-surrounding"


这些信号用于IM模块通知相关的GTK Widget对应事件的发生,是IM模块向Widget发送信息的主要途径。


3.3WidgetIMContext的交互


GtkEntry为例,GtkWidgetIMContext交互的流程大致如下:




  • GtkEntry对象初始化:创建GtkIMMultiContext成员对象,设置GtkIMMultiContextcommitpreedit等信号的处理函数。GtkIMMultiContext的初始化函数设置IM模块默认工作模式等。



  • GtkEntry对象实例化(realize):调用set_client_window设置IMContext对应的GDKWindow。通常这是GtkIMMultiContext第一次获取并创建它具体的slave IMContext对象的时刻,GtkIMMultiContext将自己实现的callback函数链接到slave IMContext的上述信号上,这些Callback函数通常再将这些信号通过自身的对象emit出去,从而让GtkEntry所注册的callback函数得到调用。



  • GtkEntry窗口获得、失去焦点:调用focus_in, focus_out函数通知IM模块焦点的变化情况



  • GtkEntry窗口获得按键事件:调用filter_keypress函数通知IM模块键值的输入,IM模块通常会根据当前的输入法状态,通过preedit-startpreedit-changed信号通知GtkEntry处于预编辑状态的文字内容发生变化,GtkEntry通过get_preedit_string函数获得实际的预编辑状态的文字内容并更新显示。用户选取文字完成输入后,IM模块通过preedit-end信号通知GtkEntry预编辑状态结束,并通过commit信号通知GtkEntry用户选择的文字内容。如果Preedit模式不适用,如英文输入状态等,则输入法模块可能直接通过commit信号通知GtkEntry文字内容。


3.4IMContextIM的交互


需要注意的是GtkIMContext是作为相关GTK Widget的成员对象运行在用户程序的进程内,而不是输入法程序的进程内。所以IMContextIM之间还存在着通讯问题。


因为通过使用GtkIMMultiContext实现了IMContext模块的动态加载机制,所以具体的IMContext和对应的IM的通讯机制根据各自的实现而不同。从而实现了对各种不同通讯协议的兼容支持。


GtkIMContextXIM支持XWindow标准所定义的XIM协议,通过XIM协议规定的xeventXIM APPXIM Server之间发送相关信息。


SCIM所支持的GtkIMContextSCIM则通过socket通讯机制与SCIM进行通讯。


3.5其它输入方式


对于字母或其它可由X11逻辑键值所对应的各种符号,除了通过GtkIMContext输入以外,有些程序如虚拟键盘(xvkbd, matchbox-keyboard)等,还可能通过发送fake key_event来模拟物理键盘的输入,通过X11的事件分发机制将键值传送到当前on focusGTK Widget上实现字母和符号的输入,此外即使在非字母符号的输入状态下,某些特殊的键值如回车,删除等,在一些输入法实现中,也可能通过这个途径来发送。


2008年8月25日星期一

嵌入式系统的动态电源管理技术

嵌入式系统的动态电源管理技术

作者:刘旭晖 Raymond转载请注明出处

Emailcolorant@163.com

BLOGhttp://blog.csdn.net/colorant/

在嵌入式系统中,电源的管理和功耗的控制,始终是一个非常重要的任务。所以通常面向嵌入式应用的CPU都会有各自不同的软硬件策略来辅助电源管理和功耗控制,最常见的包括各种低功耗模式,以及动态的频率切换等功能。本文的目的主要结合自己最近在MarvellMonahans平台上所做的工作,总结一下所学习的相关知识,及软硬件框架和实现。


1电源和功耗管理的工作内容

嵌入式设备上的电源和功耗管理,从大的方面来说,所要做的工作应该会包括以下几点:

1.1系统状态的切换

从很大程度上来说,嵌入式系统的电源和功耗管理都是围绕着CPU和系统总线的工作状态来进行的,(当然,LCD等背光的管理也是很重要的一个方面)。所以,通常来说,我们所讨论的嵌入式系统中的系统状态,从CPU和系统总线的角度来说,大致可分为以下几种:

1.1.1全速工作模式

嵌入式系统的CPU通常不会工作在超频状态下,所以全速工作状态就是指CPU运行在手册所推荐的最大工作频率下。

1.1.2降频工作模式

除去背光所消耗的功率,在系统中,CPU本身所消耗的功率通常占据了相当大的一个比例,所以为了降低功耗,让CPU工作在非全速状态也是一个常见的做法。

ARM架构CPU的变频操作通常是通过协处理器指令来实现的。变频过程中,除了要处理CPUCore频率,还需要处理相关总线和外设的频率变换,因为通常各种总线频率在变频过程中都有可能改变,而外设的时钟信号等也有可能相应的受到影响。

1.1.3低功耗模式

不同的CPU通常都会实现各自不同的低功耗模式,不过,如果抛开名字和具体细节上的差异,仅从本质上讨论其相类似的工作原理,那么,大致所涉及到的内容会包括:


  • CPU Core的时钟是否保持

  • CPUCORE电压是否保持

  • CPU核心寄存器是否保持

  • 外设总线时钟是否保持

  • LCD是否保持刷新

  • 外部SDRAM是否保持刷新

  • 内部SRAM是否保持刷新

  • 睡眠唤醒用RTC时钟是否保持刷新


按照其中这些状态的组合,不同的CPU会定义各自不同阶段程度的低功耗工作模式。

低功耗模式对消费类的嵌入式系统来说尤其重要,它关系到设备的最大待机时间。

1.1.4深度休眠模式

严格来说,深度休眠模式也是低功耗模式的一种,这里单独提出来是因为通常它会被作为系统的关机状态来使用。


例如手机这类设备,在关机后,通常系统也并没有完全断电,因为还要考虑到系统时间的更新以及闹钟唤醒等功能,所以系统关机后通常都是处于所谓的深度休眠模式。这时候,通常只有RTC时钟相关的一小部分电路处于工作状态,这时候也会采用和系统总线相独立的低频率的晶振作为时钟来源。


为了保证RTC的电路的不间断工作,通常还会在主板上放置一个小的备用电池。当然,如果主电源和备用电池都切断了,那系统就真的进入完全断电的状态了。


1.2低功耗模式下,系统底电流的控制

当系统工作在低功耗模式,尤其是休眠状态下,系统底电流的控制是非常重要的。首先要定义一下休眠状态的含义,这里所说的休眠状态,不是指上述关机状态下的深度休眠模式,也不是指PC上的Hibernate。我们所说的休眠模式是指在保证CPU和整个系统能够从内存中快速恢复工作的前提下,系统所可能进入的最低功耗状态。这通常指:


  • CPU核心电压和和时钟关闭

  • 外设时钟关闭

  • 外部SDRAM保持自刷新状态

  • 内部SRAM维持刷新


在这种状态下CPU自身的功耗被降到最低,但系统总体的底电流还和外设工作状态相关,所以需要根据系统状态在恰当的时刻控制外设的供电和工作状态以保证总体电流的最优化。


Linux内核中,这个恰当的操作时机,通常是指在统一设备驱动模型中,设备驱动所注册的SuspendResume函数被系统电源管理模块所调用时。设备驱动需要自己保证在Suspend函数被调用时完成自身必要的休眠或者断电等操作,而在Resume函数中重新恢复自己的正常工作状态。


除此之外,底电流的控制和主板本身的硬件设计也有很大关系。


1.3系统运行模式下,功耗的控制

如果说低功耗模式下系统功耗的控制关系到消费类嵌入式设备的待机时间,那么运行模式下的功耗控制则关系到系统正常使用的时间,同样也是至关重要的。


从总体来说,要降低系统运行时的功耗,所涉及到的内容大致会包括:


背光亮度的控制,例如在适当的时候自动调节系统背光亮度,时间等,在某些场合关闭系统背光。


尽量关闭不必要的外设,只有在需要使用的时候才对相关外设进行上电操作(例如Camera,蓝牙,wifi等),或者让一些不能关闭的外设在工作间隙处于休眠状态。


适时的变频操作,在CPU和总线负载较低的情况下,进行恰当的降频操作,降低CPU本身的功率消耗,同时在负载升高的时候,能够保证及时的恢复较高的工作频率,不影响用户的正常使用。


此外应该还包括恰当的器件选型,软件性能优化等其它手段。


2硬件组成

嵌入式系统中,为了支持动态电源和功耗的管理,对硬件和CPU有着特定的需求,而不同的CPU和外设为了满足这些需求,也有自己不同的实现方式。个人所接触的硬件种类有限,所以,这里主要以Monahans系列CPU为核心的系统为例,大致列一下所涉及到的硬件及相关功能特性。


2.1CPU

首先当然是CPU


为了支持变频操作,CPU需要提供系统和总线频率的动态调整功能。

为了保证某些外设在系统总线频率改变时的正常工作,应该提供具体外设时钟的可变控制。

能够提供低功耗模式的状态控制和切换能力

能够提供相应的硬件唤醒机制,保证系统能从休眠状态恢复到正常工作状态。


Monahans系列的CPU还提供了一套硬件功能模块,用于监控CPU的各种核心操作。例如Cache的命中,内存控制器的请求数量统计等等,一方面可以用来为软件性能调试提供参考数据,另一方面,也可以作为系统硬件状态的一种监控手段,为动态电源管理提供信息来源。


2.2电源管理芯片

由于CPU在低功耗模式下通常会工作在更低的核心电压下,所以要求电源管理芯片能对系统提供可以动态调整的输出电压。通过降低核心电压的手段降低系统的整体功耗。


2.3内存

对于外部内存SDRAM来说,为了保证系统在休眠时不丢失信息,通常要求内存能够在无需CPU内存控制器的参与下,工作在自刷新模式。


2.4其它外设

对于外设来说,理想的要求当然是能够受控或自动将自己致于尽可能低的功耗状态。


3软件框架


X86架构的PC中,BIOS在很大程度上承担了核心的电源管理功能,所以APMACPI或多或少在一定程度上都依赖于BIOS所提供的功能。


ARM架构的嵌入系统中,因为通常不会有BIOS,加上具体的CPU和外设千差万别,所以动态电源管理的软件实现框架也不尽相同。不过就我接触到的平台而言,基本上都会在PM的基础上实现休眠等相关的电源管理策略,而动态调频等功能,则各有各的实现方式,有基于DPM实现的,也有自己实现一套框架的。


3.1基于Monahans的电源管理框架

基于Monahans系列CPU所提供的功能,我们在MarvellBSP所提供的相关代码的基础上所实现的动态电源管理的软件框架如下图所示:


3.1.1Power Mode Management

这部分代码基于PM实现,主要负责低功耗模式的进入退出相关过程的控制,它定义并实现了几种不同程度的低功耗模式,并负责完成对外设驱动的Suspend / Resume函数的调用。

3.1.2DVFM

动态电压频率调整模块主要负责变频操作相关过程的控制,它定义和维护了一系列的操作点,用来描述和控制CPU核心频率,电压,各种总线频率等参数。同时它还负责维护和调用外设驱动向其注册的DVFM_notifier函数,在其完成变频操作前后,给外设提供一个机会执行相应的应对措施。

3.1.3Device Drivers

设备驱动需要实现自己的DVFM_notifer函数,用来处理变频操作给自己带来的影响。或者用来通知DVFM禁止变频操作的执行。

此外,一些设备驱动还要为电源管理状态切换的决策提供输入信息,例如RTC闹钟等可能做为系统唤醒的唤醒源,而像键盘,触摸屏等的操作则标志着用户交互行为的发生等等。

3.1.4Workload Profiler

系统负载采样模块同样是为电源管理状态的切换决策提供输入信息,这部分代码主要用来控制Monahans中性能监控相关硬件模块的工作。采样硬件活动信息,以监控例如内存总线的负载等。同时,在系统IDLE进程中,也利用对Idle的时间的统计等手段,为动态电源管理系统提供一个近似的CPU负载信息。

3.1.5Policy Maker

Policy Maker工作在用户层空间,它负责采集内核中上述各模块的相关信息并进行调度控制。它在用户空间实现了一套电源管理策略。作为Daemon进程,它为其它用户层代码提供一个统一的接口,用来通知其它应用程序电源状态的变化,以及接受并处理各种用户限制条件(例如播放视频时禁止熄灭背光等等)以正确进行系统状态的切换。


3.2动态电源管理策略的制定

3.2.1电源管理的决策依据

电源管理的决策依据,不外乎会有几个来源:


  • 系统硬件的工作状态

  • 上层应用程序的工作状态

  • 用户的交互情况


从系统硬件的角度来说,大致会包括:CPU和系统总线的负载情况,输入输出类设备的工作情况:例如是否存在大量的内存操作,是否有网络数据交互等,另外还包括电池是否处于低电量状态,是否在使用某些特殊的硬件设备等等。


上层应用程序的工作状态,可能包括:是否存在视频应用,是否允许关闭背光,是否对CPU速度有限制要求,是否允许系统进入睡眠状态等等。


而用户的交互情况,包括用户是否有键盘或其它IO设备输入等行为。


应该说,上述的几类决策来源往往不会是完全独立相互无关的,例如视频类应用的存在通常会使CPU处于一个较高的负载水平,大量的用户交互也可能让IO设备处于忙碌状态等等。所以,有时候,在进行电源管理决策时,可以合并一些信息来源,或者由一类情况的数据来涵盖另一类情况的发生可能。

3.2.2从用户的角度划分系统状态

CPU和硬件的角度,设备可能处在不同的频点和不同的低功耗状态。

而从用户的角度,外在的表现大体可以划分为:


  • 系统正常工作状态:各类设备可以正常使用,背光全亮

  • 背光变暗状态:一段时间没有交互,系统可能自动调暗背光,提醒用户状态的变化。

  • 背光熄灭状态:背光熄灭,但是部分功能可能保持正常工作状态,如MP3的播放等。

  • 休眠状态:所有功能关闭,但是可以快速唤醒

  • 关机状态


因此,在Policy Maker中,需要根据实际情况维护系统的当前状态,并根据一定的顺序进行状态切换,例如按照CPU等占用情况进行变频控制,根据应用和用户的交互控制背光状态等,以求最大限度的兼顾系统的易用性和功耗。


2008年7月22日星期二

驱动编程调优相关辅助工具-图形图像类

驱动编程调优相关辅助工具-图形图像类

作者:刘旭晖 Raymond转载请注明出处

Emailcolorant@163.com

BLOGhttp://blog.csdn.net/colorant/

主页:http://sites.google.com/site/rgbbones/

1综述

说到驱动编程和调优的辅助工具,我想说的不是像source insight Visual Studio这类的代码阅读开发工具,也不是像GDB Oprofile Strace之类的代码跟踪调试工具。


我所说的辅助工具,更多的是指针对各种各样类型的硬件,以硬件开发调优和测试为目的,提供数据来源,数据仿真,数据抓包,提供测试用例,进行数据转换,格式转换等等各类辅助用途的工具。


这其中多数是一些现成的工具,也有些是为了方便,自己编写的一些小程序或应用。


也许有些工具,你从来不曾用过,也同样能完成自己的工作,或者出于麻烦,不愿意去学习了解相关的知识。但是要成为一个专业的驱动开发者,我始终认为,只有认真学习相关硬件以及所涉及的子系统和应用领域,深入掌握其相关背景知识,才能真正开发出高效,有用的驱动,才能真正发挥硬件的所有潜在能力,从硬件能力软件应用的背景等角度去系统的思考驱动的框架结构。而不仅仅是简单的把硬件Run起来,做些修修补补的工作。


这篇说的是图形图像类的工具,通常会在LCDCameraTVOUT等驱动开发过程中使用到。有些工具需要你对一些相关知识有些了解。应该说,了解一些图像处理,以及光学等方面的原理,对于驱动的开发调试常常会起到事半功倍的指导性作用。至少能让你自己显得更专业些 8


2颜色校准类:

2.1LCD校准工具

校准你的PC显示器,应该是严肃的对待图像处理相关的工作第一步需要做的事吧。因为通常显示器都会有一定的偏色。不论你所做的驱动工作是调整LCD的颜色,对比度,gamma曲线,以期获得理想的显示效果,还是调整摄像头的相关参数,希望得到真实的成像效果。你都不可避免的,或者使用PC的显示器作为参照,对比显示效果,或者用PC检查摄像头的成像结果。要做到客观科学,就要求你的PC显示器本身所呈现的图像效果是标准的,真实的,这也就要求你对显示器进行校准。

最好的工具之一当然是Spyder,俗称蜘蛛。这是一套硬件工具,在使用时,由一个感光组件吸附在显示屏幕上,运行配套的软件,采集屏幕发出的色彩亮度信息,生成校准数据,以颜色配置文件的形式对屏幕进行校准。蜘蛛有不同的规格,价格从数百到数千不等,我只用过最便宜的Express版本。

此外,如果实在搞不到蜘蛛,也可以使用Photoshop中的色彩校正功能,按照步骤,进行校正,这种方法存在较大的主观因素的影响。

2.2颜色采样软件

很多场合,我们会想知道图像上某一点的具体颜色值是多少,例如分析camera拍摄的色板图像等。使用Photoshop当然是可以的,不过,毕竟有些大材小用。可以找到很多颜色采样的小软件,例如下面这个Color Cop

不过需要注意一点的是,有些图像编辑显示软件在显示图像时,会根据图像内嵌的ICC文件等在显示时对图像色彩进行校正,所以用屏幕捕捉工具捕获的颜色值,可能会和图像编辑软件取得的色彩值不同。

2.3色度分析计

调试LCD驱动时,与PCLCD进行比对,毕竟是一种主观的间接的方法,如果想要精确的分析LCD的色彩,亮度指标,可以使用色度亮度分析计。类似下面这样的东西:

它的主要功能就是测量发光体的亮度值和色度信息。使用时,可以让LCD显示例如红、绿、蓝、黑、白等纯色图像,用它采集LCD的亮度和色彩值。然后和理论标准值进行比对,进而调整。

这玩意用过几次,确实不错,不过价格不便宜。。。

2.4标准灯箱

如果你做过Camera驱动,或者你对摄影有一些兴趣,那你应该听过白平衡这样一个概念。简单的来说,物体显示的颜色,和它所处的环境光源是有密切联系的。标准灯箱,就是用来模拟几种常见的光源环境。不同的灯箱有不同的用途,我所用过的灯箱可以模拟例如标准日光,日光灯光等光源。

标准灯箱的应用领域很广了,对于我来说,主要是用来调试Camera驱动的各种白平衡设置,以及标准光源下Camera的各种色彩相关的寄存器的设置。

3颜色转换类

颜色转化,严格地说是色彩空间的变换,其包含的内容是很广泛的,也是很复杂的一门科学。常见的转换包括例如从RGB色彩空间转换为打印领域常用的CMYK色彩空间等。

3.1YUV2RGB

对于驱动开发来说,最常遇到的应该是RGBYUV色彩空间的转换了。在图形显示,Camera数据采集等等场合都可能会遇到RGBYUV的转换问题。

实际上YUV本身也存在很多不同的标准,所以RGBYUV的转换公式会有好多种,在数字领域,常见的应该还是RGBYCrCb的转换。

没有找到很简单的工具直接计算色彩值,所以我用Excel表格制作了一个转换工具,填入RGBYUV值,就能得到相对应的色彩值。

上传到CSDN了:计算YUV转RGB的EXCEL表格

4图像转换类

4.1格式转换

图像格式的转换,如果是各种图形格式之间的转换,那么有很多工具可以实现,最简单常用的不外乎就是Acdsee了。

除了这类图形格式的转换,在驱动开发中,最常用的应该还是将一幅图像转换成二进制的RAW数据,用于直接在LCD上显示,这类工具我最常用的就是Image2Lcd了。

Image2Lcd能够将图像转换成LCD显示所需的点阵数据,可以设置输出数据的色深和颜色分量的排列顺序。也可以将数据输出成C语言数组的形式。

5图像分析类

5.1色板

标准色板,我认为是调试Camera类的驱动不可缺少的工具,常用的是这种24色的标准色板

进口的这种色板价格可不便宜,大概要100-200美金吧。没有找到合适的国产替代品。这24个色块是很有代表性的颜色,下面6个是灰阶,上来6个是三原色和三补色。其它还有一些肤色啊CMYK啊之类的颜色。

5.2灰板

灰板主要用来调整白平衡和进行曝光测试。相对而言比较便宜,几十块钱就能搞定了。很多摄影爱好者还随身携带一块呢。


5.3色板数据分析工具

有个很牛的软件叫

Imatest,通常很多数码媒体评测相机或镜头时都会使用这个软件。它的功能很多了,包括测试MTF曲线,镜头光场变形等。

对于我来说,在Camera驱动中,可以用它来分析拍摄得到的色板数据,进而调整摄像头的gamma曲线,对比度设置,颜色空间变换设置等参数。下面是一个分析色板底部6个灰阶块的亮度信息,计算Gamma值的示例:

5.4其它分析工具

其它还有一些辅助图表,工具等是用来测试镜头的分辨率,形变等,这个在小的摄像头上基本上就用不上了,因为即使测了,也没法做什么改变 8 )这个是由硬件本身决定的。

6其它

6.1Gamma曲线计算工具

很多LCDCamera Sensor都会有gamma曲线的寄存器设置,不过,通常不同的厂商,gamma曲线的寄存器的设置方式都不一样,有的是分段取点,有的是分段取斜率。

不过,最基本的,第一步总是需要计算,对应于多少的gamma值,曲线上的数值应该是多少。这个我也没有找到现成的工具,还是用excel表格制作一个简单的计算工具如下,对应曲线上的数值输出为十进制和十六进制:


上传到CSDN了:Gamma曲线计算工具

6.2DisplayX

这是一个很小的显示器测试软件,主要用来测试颜色,清晰度,聚焦,延迟等等。本来和驱动开发没什么关系,不过,它的测试项目很有代表性,自己写LCD驱动的测试程序时可以参考它的测试项目编写测试用例。


2008年7月13日星期日

移植和使用内核函数跟踪系统KFT

移植和使用内核函数跟踪系统KFT

作者:刘旭晖 Raymond转载请注明出处

Emailcolorant@163.com

BLOGhttp://blog.csdn.net/colorant/


以前在2.4内核中使用过KFI来跟踪内核的函数调用,分析性能和帮助理解源码,感觉在某些情况下,还是很好用的,最近在新的项目中,需要在短时间内阅读,维护和修改大量的驱动代码。所以又想到了它,因为当前使用的内核没有集成KFI的后续项目KFT的代码,所以做了一些移植和使用的脚本编程工作,记录在这里。

关于KFT的基本原理,以前写过一篇基于KFI分析的文档(用KFIGraphviz跟踪优化内核代码),现在看来原理上基本是一致的,贴在这里:http://blog.csdn.net/colorant/archive/2008/07/09/2627493.aspx

对内核函数跟踪机制不了解的可以先看那篇文章,这里对原理不再做分析。

本人的能力和时间有限,可能下文中有些理解、分析不一定准确,欢迎联系指正。


1相关说明


1.1网站资源

KFT的官方网址:http://elinux.org/Kernel_Function_Trace

Graphviz的官方网址 http://www.graphviz.org/

一篇分析函数跟踪机制和使用Graphviz进行分析的文章,Visualize function calls with Graphviz : http://www-128.ibm.com/developerworks/linux/library/l-graphvis/?S_TACT=105AGX52&S_CMP=cn-a-l


KFT2.6.21内核的patch http://elinux.org/upload/3/33/Kft-all-in-one-2.6.21.patch


1.2工作环境

由于是跟踪内核,所以KFTkernel的关联性应该还是比较密切的,KFT的主页上有2.6.8 2.6.112.6.12 及我所使用的2.6.21等几个版本的patch

至于我的环境:

  • 硬件平台:基于ARM的嵌入式板子

  • 软件环境:Linux 2.6.21 ,自制文件系统


2移植

理论上,我的内核版本和patch所针对的版本是精确匹配的,所以本来希望patch打上以后就能用。很可惜,打完patch以后,内核配置选上KFT以后,内核build不通过:


kernel/built-in.o: In function `__cyg_profile_func_exit':

utsname_sysctl.c:(.text+0x2a68c): undefined reference to `cmpxchg'

kernel/built-in.o: In function `__cyg_profile_func_enter':

utsname_sysctl.c:(.text+0x2ad74): undefined reference to `cmpxchg'

make: *** [.tmp_vmlinux1] Error 1


仔细看了一下,cmpxchg的目的应该是做一次原子性的比较和交换的动作,ARM本身不支持这样的指令。所以,在我的内核中,使用软件关中断的方式,实现了一个类似的函数,仿造那部分代码在kft.c中添加了一个cmpxchg函数如下:


static inline int __noinstrument cmpxchg(int *v, int old, int new)

{

int ret;

unsigned long flags;


raw_local_irq_save(flags);

ret = *v;

if (likely(ret == old))

*v = new;

raw_local_irq_restore(flags);


return ret;

}


然后,kernel可以Build通过了,只是,不幸的是,下载后的kernel无法启动了,Uncompressing Linux............................................................................................... done, booting the kernel. 到这一步就停止了,

大致猜想,原因是某部分的内核代码不能使用gcc-finstrument-functions参数来编译,这一点可以在KFTpatch中的很多代码上可以看到,使用了__noinstrument 禁止跟踪,这些修改,有些是为了防止KFT本身的函数循环调用,有些是因为某些函数必须禁止跟踪,还有些是未知的原因,可能导致内核崩溃。


没有合适的硬件调试器方便跟踪kernel启动的早期阶段(还没有打印输出),暂时没有精力去跟踪到底那部分出了问题,我只能假设基于ARM平台,在我使用的内核代码中,还有一些部分必须禁止跟踪,而KFTpatch中没有处理这一部分。


所以,暂时放弃完美的解决这一问题的企图,曲线救国吧,修改总的Makefile 把编译选项-finstrument-functions 去掉。这样可以使得KFT核心模块本身被编译,但是,所有的Kernel代码不调用相关函数,毫无疑问这样是可以正常把kernel跑起来的。


然后,修改底层驱动的Makefile,仅对部分模块使用-finstrument-functions参数进行编译。这样同样可以实现对这些模块涉及到的函数进行跟踪的目的。基本能满足我的要求,缺点是,由于不是所有函数都跟踪,可能出现跟踪路径不完整。(在发生进程切换的场合,似乎有时候还会导致函数调用关系的错误关联,这和KFT的跟踪机制有关,不能怪它,谁叫我没能完整的跟踪所有函数,这个问题怎么解决有点头大,好在这种错误情况一眼就能看出来,大不了重跟踪一次)


3使用


标准的基本使用方法,可以参考前面列的文章,除此之外,我所面临的问题是需要能够支持对以模块的形式插入内核的驱动的跟踪。

这里涉及到几个问题:

3.1如何triggerstop

因为模块在未插入内核之前,没有办法得到所需的函数地址,(Buildin的函数是通过 addr2sym 查询System.map来转化得到),所以我所能想到的办法不外乎:

如果确实需要设定由哪个函数进行触发,那么插入一次模块,查询模块的符号表(下一节描述),手工将trigger函数改为对应的地址,重启系统,以完全相同的步骤插入模块,希望模块加载进来以后,保持函数地址和上次一致。

如果不关心由哪个函数触发(例如我想了解一下模块插入以后,相关的驱动初始化流程),那么可以在插入模块前,加载一个基于timetrigger,(只是为了存在一个trigger,没有trigger KFT没法启动)然后,用echo “start” > /proc/kft 进行强制触发。我的trigger


new

begin

trigger start time 5000

end


Stop也类似了,手工得到函数地址,或者,强制停止。

3.2如何获得log分析所需的地址和符号表映射

符号映射表通常由System.map得到,我的使用场合下,需要动态的得到模块插入以后总的符号表。

为此,我通过cat /proc/kallsyms > /tmp/kallsyms.bin 来得到运行时的完整符号表。

这个途径得到的符号表包括所有已经插入的模块的符号,但是,和System.map比较存在一些问题:


  • 符号表没有完整按照地址排序

  • 符号表有额外的带$特殊字符的符号存在

  • 模块部分的符号表有4列输出,比System.map多了一列模块名的显示


这几个区别会导致后面的地址转换成符号的查询算法出现问题,不能正常工作,所以,写了一个简单的脚本来处理得到的符号表,使其满足所需的格式,脚本如下:


#! /bin/sh

name=$1

nameout=$name".map"

awk '{print $1,$2,$3}' $name sed /"$"[a-z]/d sort > $nameout


基本上就是3步:只输出前3列,去除$a等符号,排序。


3.3处理__INIT函数

__init修饰符的函数,在system.map/proc/kallsyms中都没有生成符号表,如果要以相关的函数作为触发,我所能想到的只能是写一个dummy函数来触发了。例如下面这个函数:


static int __init u2d_init(void)

{

return platform_driver_register(&u2d_driver);

}

修改成下面的代码:

void dummy_u2d_init(void)

{

printk("just for kft trigger");

return;

}

static int __init u2d_init(void)

{

dummy_u2d_init();

return platform_driver_register(&u2d_driver);

}

这样,以dummy_u2d_init作为触发函数,即可。

不知道是否有更好的解决办法。


3.4绘制函数调用图

得到log,按照kft的标准用法,使用kd等工具已经可以对数据进行分析,不过我的目的之一依然是希望得到函数调用图,所以参照以前的做法,修改了转换脚本以及mtjones 所写的graphviz所需Dot文件的生成程序。

上传到了google doc

http://docs.google.com/Doc?id=dxf836w_10vzx9v6dx

3.4.1使用:

使用上述脚本,程序,完成画图所需的工作的大致流程如下:


../sym2map.sh kallsyms.bin

../mykftres.py kft.log kallsyms.bin.map > kft.call

../mypvtrace/pvtrace kft.call

dot -Tjpg graph.dot -o kft.jpg

../addr2sym < kft.log -m kallsyms.bin.map > kft.lst

../kd -c -r kft.lst > kft.ctree


下图是实际跟踪usb gadge filestorage的模块的初始化过程得到的函数调用图中的一小部分:


4遗留问题

  • __init 宏修饰的函数没法从/proc/kallsym中获得,这些函数就没法查找定位了

  • 部分 static函数,如果只被一个函数调用,那么该static函数可能会被优化成内联函数,无法跟踪。

  • 完全使能-finstrument-functions编译选项导致内核无法启动

2008年7月10日星期四

LTT (Linux Trace Toolkits) 简介

LTT (Linux Trace Toolkits) 简介

1概念

1.1功能

LTT是一个用于跟踪系统详细运行状态和流程的工具,它可以跟踪记录系统中的特定事件。这些事件包括:


  • 系统调用的进入和退出

  • 陷阱/中断(Trap / Irq)的进入和退出

  • 进程调度事件

  • 内核定时器

  • 进程管理相关事件:创建 ,唤醒,信号处理等等

  • 文件系统相关事件:Open / Read / Write / Seek / Ioctl 等等

  • 内存管理相关事件:内存分配/释放等

  • 其他事件:IPC / Socket/ 网络 等等


此外 Ltt还提供了自定义和记录需要跟踪的事件类型的函数接口。

1.2结构

LTT主要由4个部分组成:

  • 内核代码补丁:LTT目前还没有纳入正式的官方内核,在内核中的部分代码需要打Patch来获得,主要是修改了上述各类事件的相关代码,添加了Trace的调用代码

  • 内核模块:Trace功能的主要实现部分,记录内核事件,并和用户空间的守护进程进行交互

  • 用户空间的守护进程:从LTT的内核模块中获取事件相关数据,并写入文件。

  • 数据分析应用程序:读取守护进程生成的数据文件,加以分析,并以更加可读的方式(图表等)显示出来。



除此之外,LTT还具备自定义和跟踪用户空间事件的能力。


2使用

所有的安转使用相关的内容,都可以在LTT的官方站点:http://www.opersys.com/ltt/ 找到。仔细阅读里面的文档即可。

(不过,其稳定版本在我这编译起来居然有Error,修改过一些代码后,编译通过,有很多warning,或许是我的编译环境有问题,最终试用的时候图省事,没有去过多研究,使用了MontavistaPackage中现成编译好的版本 8


3体会


LTT的确是一个很有用的工具,灵活的配合其他工具的使用,如stracetime等等,可以作为分析优化系统的performance的一个可行的途径。


值得一提的是,LTT可以通过设置参数,在特定的时间段,筛选所需要记录的事件类型,还可以按进程ID等选择所跟踪的特定对象,通过合理的配置参数,可以使得其对系统所造成的额外负担减少到一个合理的程度,对系统性能的分析基本不会造成明显的干扰因素。


另外LTT提供的自定义和记录需要跟踪的事件类型的函数接口,也使它有了一些拓展能力,可以用来Debug自己编写的内核模块代码。


LTT跟踪用户空间事件的实现,是通过其内核模块中几个特定的IOCTL接口,由用户空间的一个LIB库将其包装成函数,使得其使用方法与其在内核模块中自定义和记录事件类型的函数接口相同。应该说这是一个很巧妙的实现办法,不过对其性能个人还是有些怀疑,因为这样一来,对用户空间事件的纪录就需要经由 用户空间-〉内核空间-〉用户空间 走一趟来实现。