0

关于投票
基于Linux的便携嵌入式设备电源管理解决方案
作者:吴快快,都思丹    时间:2006-10-31  

 

摘 要:本文分析了嵌入式设备现有的节电技术和Linux系统的电源管理机制,并以iPAQ为例提出了一套使用以上机制来实现便携嵌入式设备的电源管理解决方案。

关键词:电源管理;Linux系统;嵌入式设备;iPAQ掌上电脑;驱动程序;系统调用

前 言

随着各种便携嵌入式设备性能的日益提高,功能日益丰富,其电源紧张的问题也日益突出,国内新推出的某些具有PDA等多种功能的智能电话在密集使用下只能维持半天,多数摄像机和数码相机在一次充电后都只有一个小时左右的累积工作时间。东京大学的一位教授指出,除非系统功耗成倍降低,否则半导体技术的进步可能带给我们的享受都只能是空谈。可见有效的减少便携设备耗电量,提高其电能使用效率,确实是一个迫在眉睫的问题。

Linux作为一个开放源代码的操作系统,拥有非常丰富的软件资源和平台支持,这使得嵌入式系统开发的周期大大缩短,越来越多的商用和通用嵌入式系统都采用Linux作为软件平台。因此有必要对Linux系统的电源管理机制进行深入研究。

Linux内核电源管理机制分析

Linux作为一个强大而成熟的操作系统,本身提供了一套电源管理机制。这是一套从用户空间到系统空间的,由上而下的软件管理机制。

电源管理子系统
Linux内核实现了一个电源管理子系统用于统一管理每个设备。源代码pm.h和pm.c中定义和实现了主要的接口函数。如表1所示。

表1 Linux电源管理子系统接口函数

  通过这些接口函数就可以将自己的硬件设备纳入电源管理子系统使其成为系统电源管理的一部分。这需要在编写设备驱动程序的时候完成下面的工作:

(1)在初始化驱动时,使用pm_register对设备的每个实例(instance)进行注册;在清除驱动时使用pm_unregister来取消设备的注册。

(2)在对硬件进行操作之前调用pm_access(这样会保证设备已被唤醒并且处于ready状态)。

(3)编写自己的pm_callback函数。开发人员应该在设备或系统进入suspend状态时保留设备和系统的上下文到安全的地方,并在设备或系统resume时恢复其上下文,使之能够继续运行,编写pm_callback函数是驱动实现设备电源管理的重点。

(4)当设备不在被使用的时候调用pm_dev_idle函数,这个操作是可选的,可以增强设备idle状态的监测能力。

电源管理设备
将设备加入到电源管理子系统后,该设备就已经有了处理电源管理请求的能力,但是系统的电源管理行为并不会主动发生。因此还需要一个电源管理设备来接受用户请求,产生电源管理行为。这里所指的设备并不是一个真实的硬件设备,而是一个在Linux系统空间里接受用户控制的虚拟设备,它可以是一个简单的字符型设备。有了这个设备,就可以方便的实现来自于用户空间的电源管理请求和方案。

Linux电源管理行为过程如图1所示。

图1 Linux电源管理行为过程

Linux的电源管理机制在iPAQ上的应用

iPAQ是康柏公司(现在已和惠普公司合并)推出的基于StrongArm CPU的高性能掌上电脑,不仅提供了卓越的个人信息管理工具,还集成了较为强大的多媒体功能和其他娱乐功能。Linux 2.4的内核已经被成功的移植到上面,基于Linux系统众多的应用软件也已经或正在被移植。

iPAQ硬件耗电量分析
要实现对iPAQ耗电量的有效调节,就必须清楚各个硬件耗电量,从而确定出需要管理和调节的对象。iPAQ上的各种硬件的耗电量比例如图2所示。

可见,Frontlight,LCD,SDRAM,Audio,CPU等是主要的耗电设备,应该尽可能的减少这些设备的工作时间和强度,以减少耗电量,其关键步骤如下:首先,开启SDRAM的自动节能模式。iPAQ所使用的SA-1110支持SDRAM的自动节能模式;在这种模式中,当内存不被使用时,CPU将关闭输入到内存的时钟信号,内存停止工作;这样将减少大约190mW的功率。

图2 iPAQ上的各种硬件的耗电量比例

接着,调节显示驱动。可以选择(1)在必要的时候关闭背光;(2)降低LCD的刷新率。LCD在正常情况下刷新率是60Hz,通过调节LCD 定时器可以调节LCD的刷新率使其低于60Hz。降低LCD刷新率后,可以减少SDRAM,总线的使用和减少功耗;(3)在不使用屏幕的时候关闭LCD控制器。

随后,降低时钟频率。SA-1110的时钟频率可以在57.3MHz到214.8MHz之间动态调节。降低CPU时钟频率可以减少CPU本身的功耗,同时也能减少时钟由CPU提供的其他硬件的功耗。例如:SA-1110工作在最低频率时可比工作在最高频率时减少100mW到200mW的功耗。

最后,关闭音频芯片。在不使用声音的时候,尽量关闭音频芯片,并保持CPU到音频芯片的低输入。

通过Linux电源管理机制及上层应用实现对iPAQ电源管理和耗电量调节
确定了要调控的对象和方法后,需要通过Linux的电源管理机制和上层应用软件来实现对这些硬件设备的控制。这包括编写CPU电源管理代码、外设驱动程序及电源管理代码、电源管理设备实现代码和用户空间控制应用代码。

(1)实现SA-1110进入Sleep电源模式的代码SA-1110有Normal,Idle,Sleep 等几种电源模式,其中在Sleep模式下,SA-1110具有最小的电力消耗。

由于SA-1110进入Sleep 模式后,到外设和SDRAM的时钟将停止,多数的寄存器信息将丢失。因此需要事先将重要的寄存器值保存到内存中,并将SDRAM设置为自刷新模式,以保持SDRAM中的数据。当SA-1110收到硬件中断等唤醒源退出Sleep模式后不会接着执行先前未执行的指令,而是回到初始状态去执行启动代码。因此为了让CPU在唤醒后能够持续的工作,需要将返回代码的地址保存到PowerManager Scratch Pad Register(PSPR)寄存器中,使得启动代码能让CPU重新跳到返回代码的地址处,执行返回代码从而回到睡眠前的工作。SA-1110进入Sleep模式的代码片断如下:

extern void cpu_sa1110_resume(void);/* SA-1110返回函数*/
extern int cpu_sa1110_do_suspend(void);/* SA-1110睡眠函数*/
int sa1110_suspend(void)
{
...
cli();/* 关闭中断*/
sys_ctx.osmr0=OSMR0;/* 保存重要的寄存器*/
...
sys_ctx.p sdr="PSDR";
...
PSPR=virt_to_phys(cpu_sa1100_resume);/* 设置返回函数地址*/
cpu_sa1110_do_suspend();/* 进入睡眠*/
/* 退出睡眠*/
GPDR=sys_ctx.gpdr;/* 恢复寄存器*/
GRER=sys_ctx.grer;
GFER=sys_ctx.gfer;
GAFR=sys_ctx.gafr;
...
sti();/* 启动中断*/
return 0;
}

(2)实现各个外设的电源管理代码
利用Linux内核提供电源管理子系统,可以将iPAQ中的每个需要实现电源管理的外部设备纳入统一的管理。这需要在各个设备的驱动程序中使用电源管理子系统的接口函数和实际的硬件操作代码,这里将以显示设备为例:

/* SA-1110frame buffer电源管理请求处理函数*/
Static int sa1110fb_pm_callback(struct pm_dev * pm_dev,pm
_request_t req,void * data)
{
struct sa1110fb_info * fbi="pm"_dev->data;
if(req==PM_SUSPEND || req==PM_RESUME){
int state=(int)data;
if(state==0){
set_ctrlr_state(fbi,C_ENABLE);/* 进入D0模式,开启LCD控制器*/
} else {
set_ctrlr_state(fbi,C_D ISABLE);/* 进入D1-D3模式关闭LCD 控制器.*/
} }
return 0;
}
/* SA-1110frame buffer驱动初始化函数*/
int__init sa1110fb_init(void)
{
struct sa1110fb_info * fbi;
int ret;
...
/* 在电源管理子系统中注册*/
fbi->pm=pm_register(PM_SYS_DEV,PM_SYS_VGA,sa1110fb_pm_callback);
if(fbi->pm)
fbi->pm->data=fbi;/* 设置私有数据*/...
return ret;
}

(3)实现电源管理设备
这个设备实际是用于接受用户空间程序的控制所用,所以只需要简单的实现“ioctl”调用就可以了。

/* ioctl调用方法*/
static int do_ioctL(struct inode * inode,struct file * filp,u_int
cmd,u_long arg)
{ ...
switch(cmd){
case APM_IOC_STANDBY: {
pm_send_alL(PM_SUSPEND,(void *)2);/* 外设挂起*/
} break;
case APM_IOC_RESUME: {
pm_send_alL(PM_RESUME,(void *)0);/* 外设唤醒*/
} break;
case APM_IOC_SUSPEND: {
pm_send_alL(PM_SUSPEND,(void *)2);/* 外设挂起*/
sa1110_suspend();/* CPU进入休眠模式*/
/* CPU醒来,继续执行*/
pm_send_alL(PM_RESUME,(void *)0);/* 唤醒外设*/
} break;
default:
return-EINVAL;
}
return 0;
}

最后,使用命令“mknod/dev/apm c 254 0”,可以在文件系统中建立起该设备的访问节点。该节点名为/dev/apm,是一个字符设备(c),主设备号为254,此设备号为0。

(4)编写用户空间电源管理程序
用户可以在适当的时候选择是否改变CPU的时钟频率和显示刷新率,是否关闭某些外部设备,是否使整个系统进入睡眠模式等等。这只需要使用系统调用“ioctl”对电源管理设备(/dev/apm)发送命令就可以了。

int fd;
...
fd=open("/dev/apm" ,O_RDONLY);/* 打开电源管理设备*/
ioctL(fd,APM_IOC_SUSPEND,NULL);/* 发送电源管理命令*/
close(fd);/* 关闭电源管理设备*/


实现iPAQ电源管理前后耗电量比较
实现电源管理以前:开启LCD,CPU处于空闲状态,大多数其他芯片关闭,功耗为470mW。实现电源管理以后:在电源管理前的基础上开启SDRAM的自动节能模式,功耗下降到280mW。然后降低LCD刷新率到30Hz,功耗下降到238mW。再把CPU频率降低到57.3MHz,功耗下降到172mW。最后关闭LCD,功耗下降到98mW。可见,通过本文方案的调节和优化,iPAQ的耗电量确实可以得到有效地改善,最大优化后的耗电量仅为优化前的五分之一,从而大大提高了iPAQ的电池使用时间。

小结

在嵌入式设备中,电源管理是一个硬件和软件相结合的系统工程。本文介绍了已有的节能方法和Linux电源管理的机制,并且以iPAQ为例通过Linux的电源管理机制和上层应用软件,设计和实现了一个较完整和有效的电源管理方案,为众多基于Linux系统的嵌入式设备的电源管理提供了一个有用的参考。

系统分类: ARM
用户分类: LINUX
标签: 无标签
来源: 整理
发表评论 阅读全文(96) | 回复(0)

0

关于投票
S3C2410通用IO口按键驱动分析
arm开发板买了大半年了一直都没怎么用,趁着这些天回家不能上网,每天研究一点点,也算小有所得,便就记录下来,以备后忘.(不能上网虽然有很多不便,但也确实更能合理分配业余时间).

S3C2410_gpio_button.c
    模块初始化module_init(init_gpio_button)
    init_gpio_button
        循环处理gpio_buttons数组, 这个数组是在gpio_button_smdk2410_meri.c或者gpio_button_smdk2410_aiji.c中定义的,meri与 aiji是两个不同的开发板开发公司,所以他们的按键定义会有所不同,需要根据自己所采用的板子来配置(在内核编译里配置), 我们这里分析meri公司的.
        如果当前按键有对应的irq,
            设置此按键对应的irq的触发方式(set_external_irq). 包括上升沿/下降沿触发, 是否允许内部上拉电阻. 这两个参数是在gpio_buttons数组里已经作为常量定义好了的,这里只要读出来就行了
            申请中断(request_irq). 注册中断处理函数gpio_button_interrupt, 设置中断属性为FIQ(SA_INTERRUPT), 注册按键名, 设置gpio_buttons数组的地址作为dev_id(其实是gpio_button_interrupt的第二个参数)
        如果当前按键没有对应的irq
            如果当前按键有对应的pm_callback(就是按键对应的电源管理的回调函数), 就初始化请求(PM_MZ_INIT)作为参数调用这个函数
        打印一些信息
        重复循环过程
        如果定义了了CONFIG_PM, 即内核有电源管理模块
             初始化一个定时器双向链表的结构体,这里结构提里主要是设置了回调函数pwButton_timer_callback
             向电源管理模块注册按键的回调函数(pm_register). 注册的是设备类型(PM_USER_DEV, 在这个enum的定义后面有这么一句话: when wakeup, user must be handle this), 设备ID(PM_USER_INPUT), 以及回调函数(gpio_button_pm_callback)
             gpio_button_pm_callback
                如果当前新状态是休眠
                   如果此按键有对应的pm_callback, 则以睡眠状态为参数运行此函数
                   若此按键无对应的pm_callback
                      若此按键使用irq, 则禁止之
                如果当前新状态是唤醒
                    如果此按键有对应的pm_callback, 则以唤醒状态做参数运行此函数
                   若此按键无对应的pm_callback
                      若此按键使用irq, 则开放之                
        如果定义了CONFIG_S3C2410_SMDK, 也就是基于SMDK2410构架的实现
             初始化2410按键(s3c2410tk_button_init). 这里的按键指非特殊按键(不是Ctrl, Alt, shift等)
             s3c2410tk_button_init
                设置GPIO的B5为输入模式并禁止上拉(set_gpio_ctrl)
                设置GPIO的B6为输入模式并禁止上拉(set_gpio_ctrl)
                设置GPIO的E11为输入模式并禁止上拉(set_gpio_ctrl)
                设置GPIO的E12为输入模式并禁止上拉(set_gpio_ctrl) 
                初始化一个定时器(init_timer(&button_check_timer))并设置回调函数为tk_button_check_timer_handler, 超时时间为BUTTON_DELAY
                将定时器加入定时器链表(即现在开始这个定时器开始工作, 并在BUTTON_DELAY毫秒后第一次调用函数tk_button_check_timer_handler)
                    tk_button_check_timer_handler:这个函数每隔BUTTON_DELAY毫秒运行一次,并扫描gpio_buttons_tk数组中的按键
                    禁止中断(cli). 为了防止在扫描期间有按键中断进来,因为handle_scancode不是可重入函数
                    开始循环检测gpio_buttons_tk数组中每个按键是否有改变
                       暂存按键的原状态(按下还是未按)
                       读取按键的现状态(read_gpio_bit), 也就是读此按键对应的IO口电平
                       若现状态与原状态同,则表示此按键未改变,继续下一按键的检测
                       若状态有改变,则将按键的扫描值与按键状态发送给系统处理(handle_scancode). 在handle_scancode内主要是做长按的autorepeat处理, 将scancode转换成keycode, 唤醒等待进程, 将键加入队列等. 在这里有个函数比较有意思: handle_scancode->add_keyboard_randomness->add_timer_randomness->int_ln_12bits. 这个函数分析看附录.
                       重复循环
                    循环结束
                    开放中断
                    修改定时器的下次触发时间
    模块初始化结束

我们再来看按键的中断响应部分gpio_button_interrupt
    循环寻找当前irq对应的按键结构
    读取此按键状态(按下还是松开)
    判断此按键是否有对应的按键处理函数, 如果有,则以按键状态和一个私有成员(BUTTON_TYPE::priv, 其实就是当前按键在gpio_buttons数组中的索引)作为参数调用处理函数. 我们查看gpio_buttons数组中的各个按键对应的处理函数可以发现,除了电源键外,其他所有按键对应的处理函数都是keypad_handler
        keypad_handler:(普通功能键的处理函数)
           根据传入的priv(实际上是索引值)取得按键信息结构
           根据传入的按键状态, 将从按键信息结构体中取得的按键值和按键状态给系统处理(handle_scancode).这里与上面定时扫描部分相同
           (这里有点不大明白的是,为什么不直接传按键值或按键信息结构指针而是要传索引号呢?)
        pwButtonHandler:(电源键的处理函数)
           如果电源键之前未被按下
              打印一条信息
              任务调度power_button_task::power_button_task_handler, 这里函数将调用pm_user_suspend使系统进入休眠状态
           如果电源建之前已经被按下
              设置电源键新状态为未按
    如果没有处理函数,则将按键的扫描值与按键状态发送给系统处理(handle_scancode).

    模块卸载exit_gpio_button就是将一些申请的资源释放掉

总结: S3C2410的按键驱动模块分两部分:功能键(包括电源键)和普通键
功能键全部用irq来实现,这样就可以实现同时按多个功能键以及功能键与普通键的组合. 其主数据结构数组为gpio_buttons
普通键用定时扫描的方法取得按键值.其初始化函数为s3c2410tk_button_init, 扫描算法函数为tk_button_check_timer_handler, 其数据结构数组为gpio_buttons_tk
系统分类: ARM
用户分类: LINUX
标签: 无标签
来源: 整理
发表评论 阅读全文(209) | 回复(0)

0

关于投票
GCC警告选项例解

 

GCC警告选项例解

http://bigwhite.blogbus.com/logs/2062606.html

程序员是追求完美的一族,即使是一般的程序员大多也都不想看到自己的程序中有甚至那么一点点的瑕疵。遇到任意一条编译器警告都坚决不放过。有人会说:我们可以使用比编译器更加严格的静态代码检查工具,如splint。这个建议也很不错。不过lint工具使用起来较繁琐,有时候还需要记住一些特定符号并插入到你自己的代码中才行,门槛较高,这也让很多人止步于此。那么我们就从此放弃么?不,如今的编译器做得都很好,它可以帮助我们的找到绝大多数可能出现问题的代码,前提是你要学会控制编译器去找到这些问题代码,而熟悉编译器的警告选项恰恰是体现控制力的好方法。当你可以自如控制编译器警告输出的时候,你就算是'入道'了,同时你对语言的理解也更进一步了。

有人说:我就是用一个-Wall选项就可以了,一般选手可以这么做,而且他可以不知道-Wall会跟踪哪些类型的问题;但是高级选手是不会只使用-Wall的,他会把每条警告都研究的很透彻,会在Makefile中列出他想让编译器输出哪些类型的警告以替代-Wall,他会屏蔽掉那些对他的代码'毫无用处'的警告(很可能他使用了编译器对语言的扩展功能),他会有个和编译器交流的过程。

俗话说:'工欲善其事,必先利其器',一直在工作中使用GNU C编译器(以下简称GCC),这里对GCC的一些警告选项细致的分析,并列举几个简单的例子[注1]供分析参考。

1. -Wall集合警告选项
我们平时可能大多数情况只使用-Wall编译警告选项,实际上-Wall选项是一系列警告编译选项的集合。下面逐一分析这一集合中的各个选项:

[-Wchar-subscripts]
如果数组使用char类型变量做为下标值的话,则发出警告。因为在某些平台上char可能默认为signed char,一旦溢出,就可能导致某些意外的结果。

e.g.
/* test_signed_char.c */
#include

int main () {
        char    c       = 255; // 我们以为char是无符号的,其范围应该是[0,255]
        int     i       = 0;
        int     a[256];

        for (i = 0; i < 256; i++) {
                a[i] = 1;
        }

        printf("%d\n", c); // 我们期待输出255
        printf("%d\n", a[c]); // 我们期待输出1
        printf("%d\n", a[255]);
        return 0;
}

gcc -Wchar-subscripts test_signed_char.c
test_signed_char.c: In function `main':
test_signed_char.c:13: warning: array subscript has type `char'

其输出结果:
-1
-4197476
1
从输出结果来看Solaris 9/gcc 3.2上char默认实现类型为signed char;在Windows XP/gcc-3.4.2上也是一样。
Windows上的输出结果:
-1
16 (随机值)
1

[-Wcomment]
当'/*'出现在 '/* ... */'注释中,或者'\'出现在'// ...'注释结尾处时,使用-Wcomment会给出警告。不要小觑这些马虎代码,它很可能会影响程序的运行结果。如下面的例子:

e.g.
/*
 * test_comment.c
 * gcc -Wcomment test_comment.c
 */
#include

int main() {
        int     a       = 1;
        int     b       = 2;
        int     c       = 0; // ok just test\
        c = a + b;

        /*
         * 这里我们期待c = 3
         * /* 但实际上输出c = 0
         */
        printf("the c is %d\n", c);
        return 0;

}

gcc -Wcomment test_comment.c
test_comment.c:10:30: warning: multi-line comment
test_comment.c:15:12: warning: "/*" within comment

输出:
the c is 0

[-Wformat]
检查printf和scanf等格式化输入输出函数的格式字符串与参数类型的匹配情况,如果发现不匹配则发出警告。某些时候格式字符串与参数类型的不匹配会导致程序运行错误,所以这是个很有用的警告选项。

e.g.
/*
 * test_format.c
 */
#include

int main() {
        long    l       = 1;
        double  d       = 55.67;
        printf("%d\n", l);
        printf("%d\n", d);
        return 0;
}

gcc -Wformat test_format.c
test_format.c: In function `main':
test_format.c:10: warning: int format, long int arg (arg 2)
test_format.c:11: warning: int format, double arg (arg 2)

输出:
1
1078711746

[-Wimplicit]
该警告选项实际上是-Wimplicit-int和-Wimplicit-function-declaration两个警告选项的集合。前者在声明函数却未指明函数返回类型时给出警告,后者则是在函数声明前调用该函数时给出警告。

e.g.
/*
 * test_implicit.c
 */
#include

add(int a, int b) { //函数没有声明返回类型
        return a + b;
}

int test() {
        int     a       = 0;
        int     b       = 0;
        int     c       = 0;
        int     d       = 0;

        c = add(a, b);
        d = sub(a, b); //未声明sub的函数原型
        return 0;
}

gcc -Wimplicit -c test_implicit.c
test_implicit.c:7: warning: return type defaults to `int'
test_implicit.c: In function `test':
test_implicit.c:18: warning: implicit declaration of function `sub'

[-Wmissing-braces]
当聚合类型或者数组变量的初始化表达式没有'充分'用括号{}括起时,给出警告。文字表述很难理解,举例说明则清晰些。看下面的例子:

e.g.
/*
 * test_missing_braces.c
 */
struct point {
        int     x;
        int     y;
};

struct line {
        struct point start;
        struct point end;
};

typedef struct line line;

int main() {
        int     array1[2][2]    = {11, 12, 13, 14};
        int     array2[2][2]    = {{11, 12}, {13, 14}}; // ok
        line    l1              = {1, 1, 2, 2};
        line    l2              = {{2, 2}, {3, 3}}; // ok

        return 0;
}

gcc -Wmissing-braces test_missing_braces.c
test_missing_braces.c: In function `main':
test_missing_braces.c:19: warning: missing braces around initializer
test_missing_braces.c:19: warning: (near initialization for `array1[0]')
test_missing_braces.c:21: warning: missing braces around initializer
test_missing_braces.c:21: warning: (near initialization for `l1.start')

[-Wparentheses]
这是一个很有用的警告选项,它能帮助你从那些看起来语法正确但却由于操作符优先级或者代码结构'障眼'而导致错误运行的代码中解脱出来。好长的一个长句,还是看例子理解吧!:)

e.g.
/*
 * test_parentheses.c
 * gcc -Wparentheses  test_parentheses.c
 */
#include

int main() {
        int     a       = 1;
        int     b       = 1;
        int     c       = 1;
        int     d       = 1;

        if (a && b || c) { // 人们很难记住逻辑操作符的操作顺序,所以编译器建议加上()
                ;
        }

        if (a == 12)
                if (b)
                        d = 9; 
        else
                d = 10; //从代码的缩进上来看,这句仿佛是if (a == 12)的else分支

        printf("the d is %d\n", d); //期待d = 10, 而结果却是1
        return 0;
}

gcc -Wparentheses test_parentheses.c
test_parentheses.c: In function `main':
test_parentheses.c:13: warning: suggest parentheses around && within ||
test_parentheses.c:17: warning: suggest explicit braces to avoid ambiguous `else'

输出:
the d is 1

[-Wsequence-point]
关于顺序点(sequence point),在C标准中有解释,不过很晦涩。我们在平时编码中尽量避免写出与实现相关、受实现影响的代码便是了。而-Wsequence-point选项恰恰可以帮我们这个忙,它可以帮我们查出这样的代码来,并给出其警告。

e.g.
/*

 * test_sequence_point.c
 * gcc -Wsequence-point test_sequence_point.c
 */

#include

int main() {
        int     i = 12;
        i = i--;
        printf("the i is %d\n", i);
        return 0;
}

gcc -Wsequence-point test_sequence_point.c
test_sequence_point.c: In function `main':
test_sequence_point.c:10: warning: operation on `i' may be undefined

在两个平台上给出的编译警告都是一致的,但是输出结果却大相径庭。

Solaris输出:
the i is 11

Windows输出:
the i is 12

类似的像这种与顺序点相关的代码例子有:
i = i++;
a[i] = b[i++]
a[i++] = i
等等...

[-Wswitch]
这个选项的功能浅显易懂,通过文字描述也可以清晰的说明。当以一个枚举类型(enum)作为switch语句的索引时但却没有处理default情况,或者没有处理所有枚举类型定义范围内的情况时,该选项会给处警告。

e.g.
/*
 * test_switch1.c
 */
enum week {
        SUNDAY,
        MONDAY,
        TUESDAY /* only an example , we omitted the others */
};

int test1() {
        enum week       w       = SUNDAY;
        switch(w) {

        case SUNDAY:
                        break; // without default or the other case handlings
        };

        return 0;
}

int test2() { // Ok, won't invoke even a warning
        enum week       w       = SUNDAY;
        switch(w) {
                case SUNDAY:
                        break;
                default:
                        break;               
        };

        return 0;
}

int test3() { // Ok, won't invoke even a warning
        enum week       w       = SUNDAY;
        switch(w) {
                case SUNDAY:
                        break;
                case MONDAY:
                        break;
                case TUESDAY:
                        break;            
        };

        return 0;
}

gcc -Wswitch -c test_switch.c
test_switch.c: In function `test1':
test_switch.c:16: warning: enumeration value `MONDAY' not handled in switch
test_switch.c:16: warning: enumeration value `TUESDAY' not handled in switch

[-Wunused]
-Wunused是-Wunused-function、-Wunused-label、-Wunused-variable、-Wunused-value选项的集合,-Wunused-parameter需单独使用。
(1) -Wunused-function用来警告存在一个未使用的static函数的定义或者存在一个只声明却未定义的static函数,参见下面例子中的func1和func2;
(2) -Wunused-label用来警告存在一个使用了却未定义或者存在一个定义了却未使用的label,参加下面例子中的func3和func7;
(3) -Wunused-variable用来警告存在一个定义了却未使用的局部变量或者非常量static变量;参见下面例子中func5和var1;
(4) -Wunused-value用来警告一个显式计算表达式的结果未被使用;参见下面例子中func6
(5) -Wunused-parameter用来警告一个函数的参数在函数的实现中并未被用到,参见下面例子中func4。

下面是一个综合的例子
e.g.
/*
 * test_unused.c
 */
static void func1(); //to prove function used but never defined
static void func2(); //to prove function defined but not used
static void func3(); //to prove label used but never defined
static void func7(); //to prove label defined but never used
static void func4(int a); //to prove parameter declared but not used
static void func5(); //to prove local variable defined but not used
static void func6(); //to prove value evaluated but not used

static int var1;

void test() {
        func1();
        func3();
        func4(4);
        func5();
        func6();
}

static void func2() {
        ; // do nothing
}

static void func3() {
        goto over;
}

static void func4(int a) {
        ; // do nothing
}

static void func5() {
        int     a = 0;
}

static void func6() {
        int     a = 0;
        int     b = 6;
        a + b;
}

gcc -Wunused-parameter -c test_unused.c //如果不是用-Wunused-parameter,则func4函数将不被警告。
test_unused.c: In function `func3':
test_unused.c:30: label `over' used but not defined
test_unused.c: In function `func7':
test_unused.c:35: warning: deprecated use of label at end of compound statement
test_unused.c:34: warning: label `over' defined but not used
test_unused.c: In function `func4':
test_unused.c:37: warning: unused parameter `a'
test_unused.c: In function `func5':
test_unused.c:42: warning: unused variable `a'
test_unused.c: In function `func6':
test_unused.c:48: warning: statement with no effect
test_unused.c: At top level:
test_unused.c:6: warning: `func1' used but never defined
test_unused.c:25: warning: `func2' defined but not used
test_unused.c:14: warning: `var1' defined but not used

[-Wuninitialized]
该警告选项用于检查一个局部自动变量在使用之前是否已经初始化了或者在一个longjmp调用可能修改一个non-volatile automatic variable时给出警告。目前编译器还不是那么smart,所以对有些可以正确按照程序员的意思运行的代码还是给出警告。而且该警告选项需要和'-O'选项一起使用,否则你得不到任何uinitialized的警告。

e.g.
/*
 * test_uninitialized.c
 */
int test(int y) {
        int     x;

        switch (y) {
                case 1:
                        x = 11;
                        break;
                case 2:
                        x = 22;
                        break;
                case 3:
                        x = 33;
                        break;
        }

        return x;
}

gcc -Wuninitialized -O -c test_uninitialized.c
test_uninitialized.c: In function `test':
test_uninitialized.c:6: warning: `x' might be used uninitialized in this function

2、非-Wall集合警告选项
以下讨论的这些警告选项并不包含在-Wall中,需要程序员显式添加。

[-Wfloat-equal]
该项用来检查浮点值是否出现在相等比较的表达式中。

e.g.
/*
 * test_float_equal.c
 */

void test(int i) {
        double  d = 1.5;
        if (d == i) {
                ;
        }
}

gcc -Wfloat-equal -c test_float_equal.c
test_float_equal.c: In function `test':
test_float_equal.c:8: warning: comparing floating point with == or != is unsafe

[-Wshadow]
当局部变量遮蔽(shadow)了参数、全局变量或者是其他局部变量时,该警告选项会给我们以警告信息。

e.g.
/*
 * test_shadow.c
 */
int     g;

void test(int i) {
        short   i;
        double  g;
}

gcc -Wshadow -c test_shadow.c

test_shadow.c: In function `test':
test_shadow.c:9: warning: declaration of `i' shadows a parameter
test_shadow.c:10: warning: declaration of `g' shadows a global declaration
test_shadow.c:6: warning: shadowed declaration is here

[-Wbad-function-cast]
当函数(准确地说应该是函数返回类型)被转换为非匹配类型时,均产生警告。

e.g.
/*
 * test_bad_func_case.c
 */dd(int a, int b) {
        return a+b;
}

void test() {
        char *p = (char*)add(1, 13);
}

gcc -Wbad-function-cast -c test_bad_func_case.c
test_bad_func_case.c: In function `test':
test_bad_func_case.c:11: warning: cast does not match function type

[-Wcast-qual]
当去掉修饰源Target的限定词(如const)时,给出警告。

e.g.
/*
 * test_cast_qual.c
 */
void test() {
        char            c       = 0;
        const char      *p      = &c;
        char            *q;

        q = (char*)p;
}

gcc -Wcast-qual -c test_cast_qual.c
test_cast_qual.c: In function `test':
test_cast_qual.c:10: warning: cast discards qualifiers from pointer target type

[-Wcast-align]
这是个非常有用的选项,特别是对于在Solaris这样的对内存对齐校验的平台尤其重要。它用于在从对齐系数小的地址(如char*)转换为对齐系数大的地址(如int*)转换时给出警告。

e.g.
/*
 * test_cast_align.c
 */
#include
int main() {
        char    c = 1;
        char    *p = &c; //ok
        int     *q = (int*)p; //bad align-cast
        printf("the *q is %d\n", *q);
        return 0;
}

gcc -Wcast-align test_cast_align.c
test_cast_align.c: In function `main':
test_cast_align.c:9: warning: cast increases required alignment of target type

输出:
总线错误 ((主存储器)信息转储) //on Solaris 9

[-Wsign-compare]
在有符号数和无符号数进行值比较时,有符号数可能在比较之前被转换为无符号数而导致结果错误。使用该选项会对这样的情况给出警告。

e.g.
/*
 * test_sign_compare.c
 */
#include

int main() {
        unsigned int    i = 128;
        signed int      j = -1;
        if (i < j) {
                printf("i < j\n");
        } else {
                printf("i > j\n");
        }
        return 0;
}

gcc -Wsign-compare test_sign_compare.c
test_sign_compare.c: In function `main':
test_sign_compare.c:10: warning: comparison between signed and unsigned

输出:
i < j

[-Waggregate-return]
如果一个函数返回一个聚合类型,如结构体、联合或者数组,该选项就会给出警告信息。较简单不举例了。

[-Wmultichar]
当我们写下如此代码时:char c = 'peter', 使用该选项会给出警告。这个选项是默认选项,你无需单独使用该选项,不过你可以使用-Wno-multichar来关闭这些警告信息,但是这可是不建议你去做的。对于char c = 'peter'这样的代码的处理是与平台相关,不可移植的。

e.g.
/*
 * test_multichar.c
 */
int main() {
        char c = 'peter';
        printf("c is %c\n", c);
        return 0;
}
但这里在Windows和Solaris平台输出的结果却一致:
c is r

[-Wunreachable-code]
这个选项是一个检查冗余代码或疏忽代码好办法。它一旦检查到你的代码中有不可达的代码,就会发出警告。这些代码往往会存在潜在的危机。

e.g.
/*
 * test_unreachable.c
 */
int test(char c) {
        if (c < 256) {
                return 0;
        } else {
                return 1;
        }
}

gcc -Wunreachable-code -c test_unreachable.c
test_unreachable.c: In function `test':
test_unreachable.c:6: warning: comparison is always true due to limited range of data type
test_unreachable.c:9: warning: will never be executed

[-Wconvertion]
由于原型定义而引起的定点和浮点数之间的隐式转换(强制转换)或者由有符号数和无符号数之间隐式转换转换引起的警告。

e.g.
/*
 * test_conversion.c
 */
#include

void getdouble(double d) {
        ;       // do nothing
}

int main() {
        unsigned int    k;
        int             n       = 12;

        k = -1;
        k = (unsigned int)-1; // ok, explicit conversion ,no warning

        getdouble(n);
        return 0;
}

gcc -Wconversion test_conversion.c
test_conversion.c: In function `main':
test_conversion.c:15: warning: negative integer implicitly converted to unsigned type
test_conversion.c:18: warning: passing arg 1 of `getdouble' as floating rather than integer due to prototype

3、-Wtraditional和-W
这两个警告选项其实也都是一些组合(大部分都在上面提到过),前者用来在代码中使用了标准C不同于传统C的特性时,发出警告;后者也是针对一些事件打开一个警告集合。关于它们的说明具体可参见'Using the GNU Compiler Collection'。

[注1]
本文中的例子的测试环境为Solaris 9 SPARC平台,GCC-3.2和Windows XP Intel x86平台,mingw32 gcc3.4.2,如无特殊差异,所有注释均针对这两个测试环境。

系统分类: 软件开发
用户分类: LINUX
标签: 无标签
来源: 整理
发表评论 阅读全文(97) | 回复(0)

0

关于投票
warning: missing braces around initializer

typedef struct
{
  char szTagName[20];   //位号名
  WORD wTagID;          //位号ID
 WORD wTagType;         //位号类型
  WORD wRWFlag;          //读写标记
  WORD wUpNodeType;      //挂接点
  char szPhyNode[20];        //物理子项
  char szModleName[20];  //所属模块名
  WORD wHisFlag;
  char szTagDesc[40];   //位号描述
  int wparam[8];
}cfg_SIGNAL;

#define SIGN_SYS_NUM 10

const cfg_SIGNAL g_SignSys[SIGN_SYS_NUM] = {
    {"DEBUG_MONITOR", -1, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","MONITOR", HISFLAG_NO,"DEBUG_MONITOR", 0,0,0,0,0,0,0,0},
    {"DEBUG_WNC", -2, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","WNC", HISFLAG_NO,"DEBUG_WNC", 0,0,0,0,0,0,0,0},
    {"DEBUG_PIC", -3, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","PIC", HISFLAG_NO,"DEBUG_PIC", 0,0,0,0,0,0,0,0},
    {"DEBUG_DATABASE",-4, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","DATABASE",HISFLAG_NO,"DEBUG_DATABASE",0,0,0,0,0,0,0,0},
    {"DEBUG_MODBUS", -5, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","MODBUS", HISFLAG_NO,"DEBUG_MODBUS", 0,0,0,0,0,0,0,0},
    {"DEBUG_IO", -6, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","IO", HISFLAG_NO,"DEBUG_IO", 0,0,0,0,0,0,0,0},
    {"DEBUG_BAK1", -7, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK1", HISFLAG_NO,"DEBUG_BAK1", 0,0,0,0,0,0,0,0},
    {"DEBUG_BAK2", -8, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK2", HISFLAG_NO,"DEBUG_BAK2", 0,0,0,0,0,0,0,0},
    {"DEBUG_BAK3", -9, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK3", HISFLAG_NO,"DEBUG_BAK3", 0,0,0,0,0,0,0,0},
    {"DEBUG_BAK4", -10,TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK4", HISFLAG_NO,"DEBUG_BAK4", 0,0,0,0,0,0,0,0}
};

 

//编译提示:

config.c:30: warning: missing braces around initializer
config.c:30: warning: (near initialization for `g_SignSys[0].wparam')

 


//修改为:

const cfg_SIGNAL g_SignSys[SIGN_SYS_NUM] = {
    {"DEBUG_MONITOR", -1, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","MONITOR", HISFLAG_NO,"DEBUG_MONITOR", {0,0,0,0,0,0,0,0}},
    {"DEBUG_WNC", -2, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","WNC", HISFLAG_NO,"DEBUG_WNC", {0,0,0,0,0,0,0,0}},
    {"DEBUG_PIC", -3, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","PIC", HISFLAG_NO,"DEBUG_PIC", {0,0,0,0,0,0,0,0}},
    {"DEBUG_DATABASE",-4, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","DATABASE",HISFLAG_NO,"DEBUG_DATABASE",{0,0,0,0,0,0,0,0}},
    {"DEBUG_MODBUS", -5, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","MODBUS", HISFLAG_NO,"DEBUG_MODBUS", {0,0,0,0,0,0,0,0}},
    {"DEBUG_IO", -6, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","IO", HISFLAG_NO,"DEBUG_IO", {0,0,0,0,0,0,0,0}},
    {"DEBUG_BAK1", -7, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK1", HISFLAG_NO,"DEBUG_BAK1", {0,0,0,0,0,0,0,0}},
    {"DEBUG_BAK2", -8, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK2", HISFLAG_NO,"DEBUG_BAK2", {0,0,0,0,0,0,0,0}},
    {"DEBUG_BAK3", -9, TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK3", HISFLAG_NO,"DEBUG_BAK3", {0,0,0,0,0,0,0,0}},
    {"DEBUG_BAK4", -10,TAGTYPE_MC,RWFLAG_RW,UPNODETYPE_LOCAL,"","BAK4", HISFLAG_NO,"DEBUG_BAK4", {0,0,0,0,0,0,0,0}}
};

 

结论:对于初始化,需要严格按照定义,对于结构中的数组,必须加{}

系统分类: 软件开发
用户分类: LINUX
标签: 无标签
来源: 整理
发表评论 阅读全文(94) | 回复(0)
总共 , 当前 /