嵌入式

编程与应用
posts - 14, comments - 1, trackbacks - 0, articles - 0
  C++博客 :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理

2012年5月18日

在编写Java程序时,Exception类的printStacktrace()可以打印异常堆栈,这个小工具极大的提高了调试效率;虽然不是一个好习惯,却很实用。习惯了Java编程,很希望 C/C++里也有这样的小工具可以帮助调试程序.
经过几天查找,发现其实每个系统都提供了打印调用堆栈的函数;这些函数是系统相关,这里仅以Linux下的函数作说明.
Linux中共提供了三个函数用于打印调用堆栈:
 1/* 
 2* 函数说明: 取得当前函数的调用堆栈 
 3* 参数: 
 4*     buffer:用于存储函数地址的数组 
 5*     size:buffer数组的长度 
 6* 返回值: 
 7*      存储到数组中的函数个数 
 8*/

 9int backtrace(void **buffer, int size); 
10  
11/* 
12
13* 函数说明:将一组函数地址转换为字符串 
14* 参数:  
15*      buffer: 经由backtrace得到的函数地址 
16*      size: buffer数组的长度 
17* 返回值:  
18*       函数在系统中对应用字符串 
19*/

20char **backtrace_symbols(void *const *buffer, int size); 
21  
22/* 
23* 函数说明:将一组函数地址转换为字符串 
24* 参数:  
25*      buffer: 经由backtrace得到的函数地址 
26*      size: buffer数组的长度 
27*      fd: 输出结果文件描述符 
28*/

29void backtrace_symbols_fd(void *const *buffer, int size, int fd); 
30
31
32 示例程序:
33
34?1
352
363
374
385
396
407
418
429
4310
4411
4512
4613
4714
4815
4916
5017
5118
5219
5320
5421
5522
5623
5724
5825
5926
6027
6128 #include <execinfo.h> 
62#include <stdio.h> 
63#include <stdlib.h> 
64#include <unistd.h> 
65  
66void myfunc3(void
67
68    int j, nptrs; 
69    #define SIZE 100 
70    void *buffer[100]; 
71    char **strings; 
72  
73    nptrs = backtrace(buffer, SIZE); 
74    printf("backtrace() returned %d addresses\n", nptrs); 
75  
76    backtrace_symbols_fd(buffer, nptrs, STDOUT_FILENO); 
77}
 
78  
79void myfunc(void
80
81    myfunc3(); 
82}
 
83  
84int main(int argc, char *argv[]) 
85
86    myfunc(); 
87    return 0
88}
 
89
程序运行结果:
[dma@bp860-10 ~]$ g++ -rdynamic t.cpp -o t #这里的参数 -rdynamic 是必须
[dma@bp860-10 ~]$ ./t
backtrace() returned 5 addresses
./t(_Z7myfunc3v+0x1c)[0x4008c4]
./t(_Z6myfuncv+0x9)[0x4008f9]
./t(main+0x14)[0x400910]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3f37c1c40b]
./t(__gxx_personality_v0+0x3a)[0x40081a]
[dma@bp860-10 ~]$
虽然现在的程序可以输出函数调用的堆栈,但是函数多了一些前缀,比如:./t(_Z7myfunc3v+0x1c);这个问题可以通过c++fileter这个工具来解决:
[dma@bp860-10 ~]$ ./t | c++filt
./t(myfunc3()+0x1c)[0x4008c4]
./t(myfunc()+0x9)[0x4008f9]
./t(main+0x14)[0x400910]
/lib64/tls/libc.so.6(__libc_start_main+0xdb)[0x3f37c1c40b]
./t(__gxx_personality_v0+0x3a)[0x40081a]
backtrace() returned 5 addresses

posted @ 2012-05-18 22:39 陈显锋 阅读(793) | 评论 (0)编辑 收藏

2012年5月14日

setjmp()和longjmp()
#include<setjmp.h>
int setjmp(jmp_buf envbuf)
------------------------------------------------
------------------------------------------------
宏函数setjmp()在缓冲区envbuf中保存系统堆栈里的内容,供longjmp()以后使用,setjmp()必须使用头文件setjmp.h。
调用setjmp()宏时,返回值为0,然而longjmp()把一个变原传(一个确认用于从那里返回的整形参数int status)递给setjmp(),该值(恒不为0)就是调用longjmp()后出现的setjmp()的值void longjmp(jmp_buf envbuf,int status);
** 函数longjmp()使程序在最近一次调用setjmp()处重新执行。
  setjmp()和longjmp()提供了一种在函数间调转的手段,必须使用头部文件setjmp.h。
  函数longjmp()通过把堆栈复位成envbuf中描述的状态进行操作,envbuf的设置是由预先调用setjmp()生成的。这样使程序的执行在setjmp()调用后的下一个语句从新开始,使计算机认为从未离开调用setjmp()的函数。从效果上看,longjmp()函数似乎“绕”过了时间和空间(内存)回到程序的原点,不必执行正常的函数返回过程。
     缓冲区envbuf具有<setjmp.h>中定义的buf_jmp类型,它必须调用longjmp()前通过调用setjmp()来设置好。
    值status变成setjmp()的返回值,由此确定长调转的来处。不允许的唯一值是0,0是程序直接调用函数setjmp()时由该函数返回的,不是间接通过执行函数longjmp()返回的。
     longjmp()函数最常用于在一个错误发生时,从一组深层嵌套的实用程序中返回。


 例子
这个例子输出“1   2   3“

#i nclude<stdio.h>

#i nclude 
<setjmp.h>
jmp_buf ebuf;
void f2(void);
int main(void)
 
{
   
int i;
printf(
"1");
  i
=setjmp(ebuf);
  
if(i==0)
   
{
     f2();
     printf(
"This will not be printed.");
    }

  printf(
"%d",i);
  
return 0;
}

void f2(void)
{
  printf(
"2");
    longjmp(ebuf,
3);
}




-------------------------------------------------
sigsetjmp/siglongjmp
-------------------------------------------------
在信号处理函数调用之前,系统自动阻塞当前被处理的信号以防止接下来发生的该类信号中断信号处理函数,
这使得使用longjmp从信号处理函数返回时,出现是否恢复信号掩码的问题。有些系统恢复,有些系统不恢复,
POSIX.1对此没有指示,而是提供了另外两个函数sigsetjmp和siglongjmp。当从信号处理函数跳出时,应该使用siglongjmp函数。

    int sigsetjmp( sigjmp_buf * env, int savemask );

    void siglongjmp ( sigjmp_buf * env, int val );

    当调用sigsetjmp时,参数savemask指示是否将当前进程的信号掩码存储在env中;

    在调用siglongjmp时,若env中有之前存储的信号掩码,则恢复进程的信号掩码。

    在信号处理函数中调用siglongjmp时,需要特殊的保护方案:

       通常在sigsetjmp调用之前部署信号处理函数,若在sigsetjmp初始化sigjmp_buf之前,信号发生,信号处理函数中的siglongjmp被调用,这将会出错。需要提供一种保护机制,保证在sigsetjmp完成之前,信号处理函数中的siglongjmp不会被调用。ISO C提供了一种sig_automic_t可以原子的写。全局定义一个sig_atomic_t类型的数据canjmp,初始值为0,在sigsetjmp完成之后将其值修改为1,在信号处理函数里面当且仅当canjmp不为0时,才回调用siglongjmp,这样就确保了在sigsetjmp调用之后才会对siglongjmp调用。通常也将canjmp限定为volatile(它被两个线程同时访问:主线程和信号处理函数)。


longjmp有一个问题,当捕捉到一个信号时,进入信号捕捉函数,此时当前信号被自动的加到进程的信号屏蔽字中。这阻止了后来产生的这种信号中断该信号处理程序。

 

#include <stdio.h>

#include 
<stdlib.h>

#include 
<assert.h>

#include 
<signal.h>

#include 
<setjmp.h>


sigjmp_buf main_env;

int trigger_cnt = 1;

int segv_cnt = 1;


void

segv_trigger(
void)

{

        printf(
"SIGSEGV tigger %d.\n", trigger_cnt++);

        
int *= NULL;

        
*= 0;

}



void

segv_handler(
int signum)

{

        assert(signum 
== SIGSEGV);

        printf(
"SIGSEGV has been catched %d times.\n", segv_cnt++);

        siglongjmp(main_env, 
1);

}



int

main(
void)

{

        
struct sigaction segv_act;

        
volatile int longjmp_cnt;


        segv_act.sa_handler 
= segv_handler;

        sigemptyset(
&segv_act.sa_mask);

        segv_act.sa_flags 
= 0;

        
if (sigaction(SIGSEGV, &segv_act, 0< 0{

                perror(
"sigaction");

                exit(EXIT_FAILURE);

        }



        longjmp_cnt 
= 1;


        
if (sigsetjmp(main_env, 1!= 0{

                printf(
"Long jump %d.\n", longjmp_cnt++);

                
if (longjmp_cnt > 3)

                        exit(EXIT_SUCCESS);

        }



        segv_trigger();


        
/* UNREACHABLE */

        exit(EXIT_SUCCESS);

}



 

posted @ 2012-05-14 10:28 陈显锋 阅读(2741) | 评论 (0)编辑 收藏

2012年4月23日

 1 #include <stdio.h>
 2 #include <execinfo.h>
 3 
 4 void print_trace(void);
 5 void funcC()
 6         {    /* 打印调用堆栈,看看谁调用了本函数 */
 7     print_trace();
 8 }
 9 void funcB()
10  {
11      funcC();
12  }
13  void funcA()
14  {
15      funcB();
16  }
17  int main (void)
18  {
19      funcA();
20      return 0;
21  }
22  void print_trace(void)
23  {
24      int i;
25      const int MAX_CALLSTACK_DEPTH = 32;    /* 需要打印堆栈的最大深度 */
26      void *traceback[MAX_CALLSTACK_DEPTH];  /* 用来存储调用堆栈中的地址 */
27     /* 利用 addr2line 命令可以打印出一个函数地址所在的源代码位置
28      * 调用格式为: addr2line -f -e /tmp/a.out 0x400618
29      * 使用前,源代码编译时要加上 -rdynamic -g 选项
30      */
31     char cmd[512= "addr2line -f -e ";
32     char *prog = cmd + strlen(cmd);
33     /* 得到当前可执行程序的路径和文件名 */
34     int r = readlink("/proc/self/exe",prog,sizeof(cmd)-(prog-cmd)-1);
35     /* popen会fork出一个子进程来调用/bin/sh, 并执行cmd字符串中的命令,
36      * 同时,会创建一个管道,由于参数是'w', 管道将与标准输入相连接,
37      * 并返回一个FILE的指针fp指向所创建的管道,以后只要用fp往管理里写任何内容,
38      * 内容都会被送往到标准输入,
39      * 在下面的代码中,会将调用堆栈中的函数地址写入管道中,
40      * addr2line程序会从标准输入中得到该函数地址,然后根据地址打印出源代码位置和函数名。
41      */
42     FILE *fp = popen(cmd, "w");
43  /* 得到当前调用堆栈中的所有函数地址,放到traceback数组中 */
44     int depth = backtrace(traceback, MAX_CALLSTACK_DEPTH);
45     for (i = 0; i < depth; i++)
46     {
47         /* 得到调用堆栈中的函数的地址,然后将地址发送给 addr2line */
48         fprintf(fp, "%p/n", traceback[i]);
49         /* addr2line 命令在收到地址后,会将函数地址所在的源代码位置打印到标准输出 */
50     }
51     fclose(fp);
52 }

posted @ 2012-04-23 16:34 陈显锋 阅读(2032) | 评论 (0)编辑 收藏

2012年4月20日

今天编写代码,函数声明出现
error:conflicting types

百思不得其解
查阅资料得: 结构体必须放在函数声明之前。。。晕倒!!

posted @ 2012-04-20 17:08 陈显锋 阅读(310) | 评论 (0)编辑 收藏

2012年4月15日

#include <stdio.h>

/*
 *    对齐方式
 *  #pragma pack(push)   //保存当前对齐方式,未保存时,保存的为系统定义的对齐方式
 *  #pragma pack(int n)  //以n字节对齐
 *  #pragma pack(pop)    //恢复保存的对齐方式
 *  #prama pack()        //恢复系统定义的对齐方式  
 *  #pragma pack(int n) 作用范围为到下一个#pragma pack(int n)
 *  良好的编程习惯是先保存当前对齐方式,设定完后,恢复保存的对齐方式
 
*/


#define SIZE_ALINE    4
#define ALINE(size)   (size + (SIZE_ALINE-1))&~(SIZE_ALINE-1)

#pragma pack(
2)
#pragma pack(push)
struct  AA
{
    
int   a;
    
char  b;
    
int   c;
}
;

#pragma pack(
1)
struct  BB
{
    
int   a;
    
char  b;
    
int   c;
}
;

#pragma pack()
struct  CC
{
    
int   a;
    
char  b;
    
int   c;
}
;

int main()
{
    printf(
"%d\n",sizeof(AA));
    printf(
"%d\n",sizeof(BB));
    printf(
"%d\n",sizeof(CC));
    
return 0;
}

posted @ 2012-04-15 14:18 陈显锋 阅读(260) | 评论 (0)编辑 收藏

2012年3月25日

MTD

MTD(memory technology device内存技术设备)是用于访问memory设备(ROM、flash)的Linux的子系统。MTD的主要目的是为了使新的memory设备的驱动更加简单,为此它在硬件和上层之间提供了一个抽象的接口。MTD的所有源代码在/drivers/mtd子目录下。CFI接口的MTD设备分为四层(从设备节点直到底层硬件驱动),这四层从上到下依次是:设备节点、MTD设备层、MTD原始设备层和硬件驱动层。

  MTD原始设备描述
  所有组成MTD原始设备的Flash芯片必须是同类型(无论是interleave还是地址相连),在描述MTD原始设备数据结构中采用同一结构描述组成Flash芯片。每个MTD原始设备有一个mtd_info结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip结构的数组。其中mtd_info、map_info和cfi_private结构用于描述MTD原始设备,因为组成MTD原始设备的NOR型Flash相同,cfi_ident结构用于描述Flash芯片信息;而flchip结构用于描述每个Flash芯片专有信息。
  根文件系统
  文件系统
  字符设备节点
  MTD字符设备
  MTD块设备
  MTD原始设备
  FLASH硬件驱动
  块设备节点
  一、Flash硬件驱动层:硬件驱动层负责在init时驱动Flash硬件,Linux MTD设备的NOR Flash芯片驱动遵循CFI接口标准,其驱动程序位于drivers/mtd/chips子目录下。NAND型Flash的驱动程序则位于/drivers/mtd/nand子目录下
  二、MTD原始设备:原始设备层有两部分组成,一部分是MTD原始设备的通用代码,另一部分是各个特定的Flash的数据,例如分区。
  用于描述MTD原始设备的数据结构是mtd_info,这其中定义了大量的关于MTD的数据和操作函数。mtd_table(mtdcore.c)则是所有MTD原始设备的列表,mtd_part(mtd_part.c)是用于表示MTD原始设备分区的结构,其中包含了mtd_info,因为每一个分区都是被看成一个MTD原始设备加在mtd_table中的,mtd_part.mtd_info中的大部分数据都从该分区的主分区mtd_part->master中获得。
  在drivers/mtd/maps/子目录下存放的是特定的flash的数据,每一个文件都描述了一块板子上的flash。其中调用add_mtd_device()、del_mtd_device()建立/删除 mtd_info结构并将其加入/删除mtd_table(或者调用add_mtd_partition()、del_mtd_partition() (mtdpart.c)建立/删除mtd_part结构并将mtd_part.mtd_info加入/删除mtd_table 中)。
  三、MTD设备层:基于MTD原始设备,linux系统可以定义出MTD的块设备(主设备号31)和字符设备(设备号90)。MTD字符设备的定义在mtdchar.c中实现,通过注册一系列file operation函数(lseek、open、close、read、write)。MTD块设备则是定义了一个描述MTD块设备的结构 mtdblk_dev,并声明了一个名为mtdblks的指针数组,这数组中的每一个mtdblk_dev和mtd_table中的每一个 mtd_info一一对应。
  四、设备节点:通过mknod在/dev子目录下建立MTD字符设备节点(主设备号为90)和MTD块设备节点(主设备号为31),通过访问此设备节点即可访问MTD字符设备和块设备。
  五、根文件系统:在Bootloader中将JFFS(或JFFS2)的文件系统映像jffs.image(或jffs2.img)烧到flash的某一个分区中,在/arch/arm/mach-your/arch.c文件的 your_fixup函数中将该分区作为根文件系统挂载。
  六、文件系统:内核启动后,通过mount 命令可以将flash中的其余分区作为文件系统挂载到mountpoint上。
  设备层和原始设备层的函数调用关系(红色部分需要我们实现):
  一个MTD原始设备可以通过mtd_part分割成数个MTD原始设备注册进 mtd_table,mtd_table中的每个MTD原始设备都可以被注册成一个MTD设备,其中字符设备的主设备号为90,次设备号为0、2、4、 6…(奇数次设备号为只读设备),块设备的主设备号为31,次设备号为0、1、2、3…
  mtd_notifier mtd_notifier
  字符设备 mtd_fops 块设备 mtd_fops
  (mtdchar.c) (mtdblock.c) mtdblks
  设备层
  register_mtd_user()
  get_mtd_device()
  unregister_mtd_user()
  put_mtd_device()
  erase_info
  mtd_notifiers
  mtd_table
  mtd_info
  mtd_part
  (mtdcore.c)
  (mtdpart.c)
  Your Flash
  (your-flash.c)
  add_mtd_partitions()
  del_mtd_partitions()
  原始设备层 add_mtd_device()
  del_mtd_device()
  mtd_partition
  NOR型Flash芯片驱动与MTD原始设备
  所有的NOR型Flash的驱动(探测probe)程序都放在 drivers/mtd/chips下,一个MTD原始设备可以由一块或者数块相同的Flash芯片组成。假设由4块devicetype为x8的 Flash,每块大小为8M,interleave为2,起始地址为0x01000000,地址相连,则构成一个MTD原始设备(0x01000000-0x03000000),其中两块interleave成一个chip,其地址从0x01000000到0x02000000,另两块interleave成一个chip,其地址从0x02000000到0x03000000。
  请注意,所有组成一个MTD原始设备的Flash芯片必须是同类型的(无论是interleave还是地址相连),在描述MTD原始设备的数据结构中也只是采用了同一个结构来描述组成它的Flash芯片。
  0x03000000
  0x02000000
  0x01000000
  每个MTD原始设备都有一个mtd_info 结构,其中的priv指针指向一个map_info结构,map_info结构中的fldrv_priv指向一个cfi_private结构,cfi_private结构的cfiq指针指向一个cfi_ident结构,chips指针指向一个flchip结构的数组。其中mtd_info、 map_info和cfi_private结构用于描述MTD原始设备;因为组成MTD原始设备的NOR型Flash相同,cfi_ident结构用于描述Flash芯片的信息;而flchip结构用于描述每个Flash芯片的专有信息(比如说起始地址)

posted @ 2012-03-25 15:11 陈显锋 阅读(394) | 评论 (0)编辑 收藏

2012年3月23日

vs2005常用快捷键

my performace:
Shift+ALT+ENTER
CTRL+I
CTRL+SHIFT+I
CTRL+L
CTRL+M,M
CTRL+G

posted @ 2012-03-23 21:04 陈显锋| 编辑 收藏

2010年4月21日

 

#ifndef BINARY_TREE
#define BINARY_TREE
#include
<stack>
#include
<iostream>
using namespace std;
template
<class T> class BinaryTree;
template
<class T>
class BTNode
{
public:
    BTNode()
{lChild=rChild=NULL;}
    BTNode(
const T&x)
    
{
        element
=x;
        lChild
=rChild=NULL;
    }

    BTNode(
const T& x,BTNode<T>*l,BTNode<T>* r)
    
{
        element
=x;lChild=l;rChild=r;
    }

    friend 
class BinaryTree<T>;
private:
    T element;
    BTNode
<T>* lChild,*rChild;
    
static stack<BTNode<T>*> s;
}
;
template
<class T>
stack
<BTNode<T>*> BTNode<T>::s;

template
<class T>
class BinaryTree
{
public:
    BinaryTree()
{root=NULL;}
    
bool IsEmpty()const{return root==NULL}
    
void Clear();
    
bool Root(T& x)const;
    
void MakeTree(const T& x,BinaryTree<T>& left,BinaryTree<T>& right);
    
void BreakTree(T& x,BinaryTree<T>& left,BinaryTree<T>& right);
    
void PreOrder();
protected:
    BTNode
<T>* root;
private:
    
void PreOrder(BTNode<T>*current);
}
;

#endif//BINARY_TREE

template
<class T>
bool BinaryTree<T>::Root(T &x)const
{
    
if(root){
        x
=root->element;return true;
    }

    
else return false;
}


template
<class T>
void BinaryTree<T>::MakeTree(const T& x,BinaryTree<T>& left,BinaryTree<T>& right)
{
    
if(root||&left==&right) return;
    root
=new BTNode<T>(x,left.root,right.root);
    left.root
=right.root=NULL;
}


template
<class T>
void BinaryTree<T>::BreakTree(T& x,BinaryTree<T>& left,BinaryTree<T>& right)
{
    
if(root||&left==&right||left.root==right.root) return;
    x
=root->element;
    left.root
=root->lChild;right.root=root->rChild;
    delete root;root
=NULL;//
}


template
<class T>
void BinaryTree<T>::PreOrder(BTNode<T>* current)
{
loop:
    
while(current){
        Visit(current
->element);
        
if(current->rChild)
            BTNode
<T>::s.push(current->rChild);
        current
=current->lChild;
    }

    current
=BTNode<T>::s.top();Visit(current->element);BTNode<T>::s.pop();
    
if(BTNode<T>::s.empty()) return;
    
else goto loop;
}


template
<class T>
void BinaryTree<T>::PreOrder()
{
    
if(root)
        PreOrder(root);
    
if(root->rChild)
        PreOrder(root
->rChild);
}

posted @ 2010-04-21 21:01 陈显锋 阅读(248) | 评论 (0)编辑 收藏

2010年4月17日

     摘要:   #ifndef LINKEDSTACK_CLASS#define LINKEDSTACK_CLASStemplate <class T> class LinkedStack;template <class T>class Node{   &...  阅读全文

posted @ 2010-04-17 12:11 陈显锋 阅读(497) | 评论 (0)编辑 收藏

     摘要: #ifndef STACK_CLASS#define STACK_CLASS#include<iostream>using namespace std;template <class T>class Stack{public:    virtual boo...  阅读全文

posted @ 2010-04-17 10:59 陈显锋 阅读(298) | 评论 (0)编辑 收藏