zz: linux环境下 C++性能测试工具 gprof 和gprof2dot

linux环境下 C++性能测试工具 gprof 和gprof2dot

http://www.4ucode.com/Study/Topic/1909519

http://coolaj86.info/articles/super-simple-gprof.html

http://sam.zoy.org/writings/programming/gprof.html

http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html

 

1 简介

改进应用程序的性能是一项非常耗时耗力的工作,但是究竟程序中是哪些函数消耗掉了大部分执行时间,这通常都不是非常明显的。GNU 编译器工具包所提供了一种剖析工具 GNU profiler(gprof)。gprof 可以为 Linux平台上的程序精确分析性能瓶颈。gprof精确地给出函数被调用的时间和次数,给出函数调用关系。

 

gprof 用户手册网站 http://sourceware.org/binutils/docs-2.17/gprof/index.html

 

2 功能

Gprof GNU gnu binutils工具之一,默认情况下linux系统当中都带有这个工具。

1. 可以显示“flat profile”,包括每个函数的调用次数,每个函数消耗的处理器时间,

2. 可以显示“Call graph”,包括函数的调用关系,每个函数调用花费了多少时间。

3. 可以显示“注释的源代码”--是程序源代码的一个复本,标记有程序中每行代码的执行次数。

 

3 原理

通过在编译和链接程序的时候(使用 -pg 编译和链接选项),gcc 在你应用程序的每个函数中都加入了一个名为mcount ( or “_mcount” , or “__mcount” , 依赖于编译器或操作系统)的函数,也就是说你的应用程序里的每一个函数都会调用mcount, 而mcount 会在内存中保存一张函数调用图,并通过函数调用堆栈的形式查找子函数和父函数的地址。这张调用图也保存了所有与函数相关的调用时间,调用次数等等的所有信息。

 

4 使用流程

1. 在编译和链接时 加上-pg选项。一般我们可以加在 makefile 中。

2. 执行编译的二进制程序。执行参数和方式同以前。

3. 在程序运行目录下 生成 gmon.out 文件。如果原来有gmon.out 文件,将会被重写。

4. 结束进程。这时 gmon.out 会再次被刷新。

5. 用 gprof 工具分析 gmon.out 文件。

 

5 参数说明

l -b 不再输出统计图表中每个字段的详细描述。

l -p 只输出函数的调用图(Call graph的那部分信息)。

l -q 只输出函数的时间消耗列表。

l -e Name 不再输出函数Name 及其子函数的调用图(除非它们有未被限制的其它父函数)。可以给定多个 -e 标志。一个 -e 标志只能指定一个函数。

l -E Name 不再输出函数Name 及其子函数的调用图,此标志类似于 -e 标志,但它在总时间和百分比时间的计算中排除了由函数Name 及其子函数所用的时间。

l -f Name 输出函数Name 及其子函数的调用图。可以指定多个 -f 标志。一个 -f 标志只能指定一个函数。

l -F Name 输出函数Name 及其子函数的调用图,它类似于 -f 标志,但它在总时间和百分比时间计算中仅使用所打印的例程的时间。可以指定多个 -F 标志。一个 -F 标志只能指定一个函数。-F 标志覆盖 -E 标志。

l -z 显示使用次数为零的例程(按照调用计数和累积时间计算)。

 

一般用法: gprof b 二进制程序 gmon.out >report.txt

 

6 报告说明

Gprof 产生的信息解释:

%time

Cumulative

seconds

Self

Seconds

Calls

Self

TS/call

Total

TS/call

name

该函数消耗时间占程序所有时间百分比

程序的累积执行时间

(只是包括gprof能够监控到的函数)

该函数本身执行时间

所有被调用次数的合共时间

函数被调用次数

函数平均执行时间

(不包括被调用时间)

函数的单次执行时间

函数平均执行时间

(包括被调用时间)

 

函数的单次执行时间

函数名

 

Call Graph 的字段含义:

Index

%time

Self

Children

Called

Name

索引值

函数消耗时间占所有时间百分比

函数本身执行时间

执行子函数所用时间

被调用次数

函数名

 

注意:

程序的累积执行时间只是包括gprof能够监控到的函数。工作在内核态的函数和没有加-pg编译的第三方库函数是无法被gprof能够监控到的,(如sleep()等)

Gprof 的具体参数可以 通过 man gprof 查询。

 

7 共享库的支持

对于代码剖析的支持是由编译器增加的,因此如果希望从共享库中获得剖析信息,就需要使用 -pg 来编译这些库提供已经启用代码剖析支持而编译的 C 库版本(libc_p.a)。

如果需要分析系统函数(如libc库),可以用 lc_p替换-lc。这样程序会链接libc_p.so或libc_p.a。这非常重要,因为只有这样才能监控到底层的c库函数的执行时间,(例如memcpy(),memset(),sprintf()等)。

gcc example1.c –pg -lc_p -o example1

注意要用ldd ./example | grep libc来查看程序链接的是libc.so还是libc_p.so

 

8 用户时间与内核时间

gprof 的最大缺陷:它只能分析应用程序在运行过程中所消耗掉的用户时间,无法得到程序内核空间的运行时间。通常来说,应用程序在运行时既要花费一些时间来运行用户代码,也要花费一些时间来运行 “系统代码”,例如内核系统调用sleep()。

有一个方法可以查看应用程序的运行时间组成,在 time 命令下面执行程序。这个命令会显示一个应用程序的实际运行时间、用户空间运行时间、内核空间运行时间

time ./program

输出:

real 2m30.295s

user 0m0.000s

sys 0m0.004s

 

9 注意事项

1. g++在编译和链接两个过程,都要使用-pg选项。

2. 只能使用静态连接libc库,否则在初始化*.so之前就调用profile代码会引起“segmentation fault,解决办法是编译时加上-static-libgcc或-static。

3. 如果不用g++而使用ld直接链接程序,要加上链接文件/lib/gcrt0.o,如ld -o myprog /lib/gcrt0.o myprog.o utils.o -lc_p。也可能是gcrt1.o

4. 要监控到第三方库函数的执行时间,第三方库也必须是添加 pg 选项编译的。

5. gprof只能分析应用程序所消耗掉的用户时间.

6. 程序不能以demon方式运行。否则采集不到时间。(可采集到调用次数)

7. 首先使用 time 来运行程序从而判断 gprof 是否能产生有用信息是个好方法。

8. 如果 gprof 不适合您的剖析需要,那么还有其他一些工具可以克服 gprof 部分缺陷,包括 OProfile 和 Sysprof。

9. gprof对于代码大部分是用户空间的CPU密集型的程序用处明显。对于大部分时间运行在内核空间或者由于外部因素(例如操作系统的 I/O 子系统过载)而运行得非常慢的程序难以进行优化。

10. gprof 不支持多线程应用,多线程下只能采集主线程性能数据。原因是gprof采用ITIMER_PROF信号,在多线程内只有主线程才能响应该信号。但是有一个简单的方法可以解决这一问题:http://sam.zoy.org/writings/programming/gprof.html

11. gprof只能在程序正常结束退出之后才能生成报告(gmon.out)。

a) 原因: gprof通过在atexit()里注册了一个函数来产生结果信息,任何非正常退出都不会执行atexit()的动作,所以不会产生gmon.out文件。

b) 程序可从main函数中正常退出,或者通过系统调用exit()函数退出。

 

 

10 多线程应用

gprof 不支持多线程应用,多线程下只能采集主线程性能数据。原因是gprof采用ITIMER_PROF信号,在多线程内只有主线程才能响应该信号。

采用什么方法才能够分析所有线程呢?关键是能够让各个线程都响应ITIMER_PROF信号。可以通过桩子函数来实现,重写pthread_create函数

 

//////////////////// gprof-helper.c////////////////////////////

#define _GNU_SOURCE

#include <sys/time.h>

#include <stdio.h>

#include <stdlib.h>

#include <dlfcn.h>

#include <pthread.h>

 

static void * wrapper_routine(void *);

 

/* Original pthread function */

static int (*pthread_create_orig)(pthread_t *__restrict,

__const pthread_attr_t *__restrict,

void *(*)(void *),

void *__restrict) = NULL;

 

/* Library initialization function */

void wooinit(void) __attribute__((constructor));

 

void wooinit(void)

{

pthread_create_orig = dlsym(RTLD_NEXT, "pthread_create");

fprintf(stderr, "pthreads: using profiling hooks for gprof\n");

if(pthread_create_orig == NULL)

{

char *error = dlerror();

if(error == NULL)

{

error = "pthread_create is NULL";

}

fprintf(stderr, "%s\n", error);

exit(EXIT_FAILURE);

}

}

 

/* Our data structure passed to the wrapper */

typedef struct wrapper_s

{

void * (*start_routine)(void *);

void * arg;

pthread_mutex_t lock;

pthread_cond_t wait;

struct itimerval itimer;

} wrapper_t;

 

/* The wrapper function in charge for setting the itimer value */

static void * wrapper_routine(void * data)

{

/* Put user data in thread-local variables */

void * (*start_routine)(void *) = ((wrapper_t*)data)->;start_routine;

void * arg = ((wrapper_t*)data)->;arg;

 

/* Set the profile timer value */

setitimer(ITIMER_PROF, &((wrapper_t*)data)->;itimer, NULL);

 

/* Tell the calling thread that we don't need its data anymore */

pthread_mutex_lock(&((wrapper_t*)data)->;lock);

pthread_cond_signal(&((wrapper_t*)data)->;wait);

pthread_mutex_unlock(&((wrapper_t*)data)->;lock);

 

/* Call the real function */

return start_routine(arg);

}

 

/* Our wrapper function for the real pthread_create() */

int pthread_create(pthread_t *__restrict thread,

__const pthread_attr_t *__restrict attr,

void * (*start_routine)(void *),

void *__restrict arg)

{

wrapper_t wrapper_data;

int i_return;

 

/* Initialize the wrapper structure */

wrapper_data.start_routine = start_routine;

wrapper_data.arg = arg;

getitimer(ITIMER_PROF, &wrapper_data.itimer);

pthread_cond_init(&wrapper_data.wait, NULL);

pthread_mutex_init(&wrapper_data.lock, NULL);

pthread_mutex_lock(&wrapper_data.lock);

 

/* The real pthread_create call */

i_return = pthread_create_orig(thread,

attr,

&wrapper_routine,

&wrapper_data);

 

/* If the thread was successfully spawned, wait for the data

* to be released */

if(i_return == 0)

{

pthread_cond_wait(&wrapper_data.wait, &wrapper_data.lock);

}

 

pthread_mutex_unlock(&wrapper_data.lock);

pthread_mutex_destroy(&wrapper_data.lock);

pthread_cond_destroy(&wrapper_data.wait);

 

return i_return;

}

 

///////////////////

然后编译成动态库 gcc -shared -fPIC gprof-helper.c -o gprof-helper.so -lpthread -ldl

 

使用例子:

/////////////////////a.c/////////////////////////////

#include <stdio.h>;

#include <stdlib.h>;

#include <unistd.h>;

#include <pthread.h>;

#include <string.h>;

void fun1();

void fun2();

void* fun(void * argv);

int main()

{

int i =0;

int id;

pthread_t thread[100];

for(i =0 ;i< 100; i++)

{

id = pthread_create(&thread[i], NULL, fun, NULL);

printf("thread =%d\n",i);

}

printf("dsfsd\n");

return 0;

}

void* fun(void * argv)

{

fun1();

fun2();

return NULL;

}

 

void fun1()

{

int i = 0;

while(i<100)

{

i++;

printf("fun1\n");

}

}

 

void fun2()

{

int i = 0;

int b;

while(i<50)

{

i++;

printf("fun2\n");

//b+=i;

}

}

///////////////

 

gcc -pg a.c gprof-helper.so

 

运行程序:

./a.out

 

分析gmon.out:

gprof -b a.out gmon.out

 

 

 

利用 gprof2dot 和graphviz 图形化定位linux c/c++系统性能瓶颈

http://www.cnblogs.com/rocketfan/archive/2009/11/15/1603465.html

http://www.51testing.com/?uid-13997-action-viewspace-itemid-79952

 

gprof 使用说明 zz

一. 概要
1. Gprof 是在运行中收集程序的统计信息。
程序的运行方式会严重影响统计的信息结果。因为不同的执行路径下,程序的行为方式会有很大的差别。

2. Gprof 使用步骤
(1) 编译链接程序的时候,使能Gprof
(2) 执行程序生成profile data file
(3) 使用Gprof分析profile data

3. 输出格式
(1) flat profile
每一个函数花费了多少时间,每一个函数被调用了多少次
(2) call graph
对每一个函数来说,哪个函数调用了它,多少次;它调用了哪些个函数,多少次
这个函数花费了多少时间,它调用的函数花费了多少时间
(3) annotated source
生成一份源代码的拷贝,标注出每一个block被执行了多少次

二. 为 Gprof 编译程序
1. 给GNU Tools 传递PG选项
gcc -g -pg .............
编译器和连接器都需要加上-pg 选项
2. 执行程序
程序执行结束后,会生成gmon.out 统计文件,这个就是profiler data file。
注意:只有在程序正常终止的时候,才会生成这个文件。也就是说,程序必须是从exit或者return终止的。
3. 分析生成结果
gprof [Options] <可执行文件> <profiler data file>

三. Gprof 的输出格式
G平日哦方可生成四种格式的统计结果,其中的line-by-line的输出,已经转移到gconv工具上。
所以目前的Gprof主要是三种格式的输出。

1. Flat Profile:

 

  1. % the percentage of the total running time of the
  2. time program used by this function.(在整个程序运行时间中,
  3. 这个函数运行时间所占的百分比)
  4.  
  5. cumulative a running sum of the number of seconds accounted
  6. seconds for by this function and those listed above it.
  7. (这个函数和列在它之前的函数,以秒为单位运行时间的总和)
  8.  
  9. self the number of seconds accounted for by this
  10. seconds function alone. This is the major sort for this
  11. listing.(这个函数自身运行时间的总和,以秒为单位)
  12.  
  13. calls the number of times this function was invoked, if
  14. this function is profiled, else blank.(这个函数
  15. 被调用的总次数)
  16.  
  17. self the average number of milliseconds spent in this
  18. ms/call function per call, if this function is profiled,
  19. else blank.(这个函数每次被调用的平均时间,以毫秒为单位)
  20.  
  21. total the average number of milliseconds spent in this
  22. ms/call function and its descendents per call, if this
  23. function is profiled, else blank.(这个函数和在它内部调用的
  24. 函数,每次被调用的平均运行时间)
  25.  
  26. name the name of the function. This is the minor sort
  27. for this listing. The index shows the location of
  28. the function in the gprof listing. If the index is
  29. in parenthesis it shows where it would appear in
  30. the gprof listing if it were to be printed.


2. Call Graph
granularity: each sample hit covers 2 byte(s) for 20.00% of 0.05 seconds

index % time self children called name
<spontaneous>
[1] 100.0 0.00 0.05 start [1]
0.00 0.05 1/1 main [2]
0.00 0.00 1/2 on_exit [28]
0.00 0.00 1/1 exit [59]
-----------------------------------------------
0.00 0.05 1/1 start [1]
[2] 100.0 0.00 0.05 1 main [2]
0.00 0.05 1/1 report [3]
-----------------------------------------------
0.00 0.05 1/1 main [2]
[3] 100.0 0.00 0.05 1 report [3]
0.00 0.03 8/8 timelocal [6]
0.00 0.01 1/1 print [9]
0.00 0.01 9/9 fgets [12]
0.00 0.00 12/34 strncmp <cycle 1> [40]
0.00 0.00 8/8 lookup [20]
0.00 0.00 1/1 fopen [21]
0.00 0.00 8/8 chewtime [24]
0.00 0.00 8/16 skipspace [44]
-----------------------------------------------
[4] 59.8 0.01 0.02 8+472 <cycle 2 as a whole> [4]
0.01 0.02 244+260 offtime <cycle 2> [7]
0.00 0.00 236+1 tzset <cycle 2> [26]
-----------------------------------------------


虚线分割的每一个部分叫做一个entity,用来描述一个函数或者一个cycle。
其中每一个以 [数字] 开始的行叫做 primary line,这个entity主要就是描述它的。
在 primary line 之前的行显示的是调用这个函数的函数,也就是caller。在它之后
的是这个函数调用的函数,也就是subroutine。

2.1 Primary Line

index % time self children called name
...
[3] 100.0 0.00 0.05 1 report [3]

+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 

  1. index
  2. Entries are numbered with consecutive integers. Each function therefore has an index number, which appears at the beginning of its primary line.
  3.  
  4. Each cross-reference to a function, as a caller or subroutine of another, gives its index number as well as its name. The index number guides you if you wish to look for the entry for that function.
  5.  
  6. % time
  7. This is the percentage of the total time that was spent in this function, including time spent in subroutines called from this function.(花费在这个函数中的时间在整个程序全部运行时间中的百分比,其中包括花费在这个函数调用的子函数中的时间)
  8.  
  9. The time spent in this function is counted again for the callers of this function. Therefore, adding up these percentages is meaningless.(在这个函数的调用函数caller中,这个函数的运行时间,还会被计算在内,所以将这些百分数加在一起没有意义)
  10.  
  11. self
  12. This is the total amount of time spent in this function. This should be identical to the number printed in the seconds field for this function in the flat profile.(这个函数自身花费的时间。这个数应该和Flat Profile中的记录一样)
  13.  
  14. children
  15. This is the total amount of time spent in the subroutine calls made by this function. This should be equal to the sum of all the self and children entries of the children listed directly below this function. (花费在这个函数调用的所有函数的总时间。这个数应该和列在这个函数下面的子函数的全部 self 和 children之和相等)
  16. called This is the number of times the function was called.(这个函数被调用的次数)
  17.  
  18. If the function called itself recursively, there are two numbers, separated by a `+'. The first number counts non-recursive calls, and the second counts recursive calls.(如果这个函数被循环调用,这里就会有两个被“+”分隔的数字,第一个数字表示非递归调用的次数,第二个表示递归调用的次数)
  19.  
  20. In the example above, the function report was called once from main.
  21.  
  22. name
  23. This is the name of the current function. The index number is repeated after it.
  24.  
  25. If the function is part of a cycle of recursion, the cycle number is printed between the function's name and the index number . For example, if function gnurr is part of cycle number one, and has index number twelve, its primary line would be end like this:
  26.  
  27. gnurr <cycle 1> [12]
  28. 如果这个函数是一个循环递归的一部分,循环编号将会打印在函数名和函数索引号之间。例如:如果函数gnurr是循环一的一部分,并且它的索引号是12,它的primary line就应该如下:
  29. gnurr <cycle 1> [12]



2.2 Lines for a function's caller

index % time self children called name
...
0.00 0.05 1/1 main [2]
[3] 100.0 0.00 0.05 1 report [3]
 

  1. self
  2. An estimate of the amount of time spent in report itself when it was called from main.
  3. (report自己花费的时间)
  4.  
  5. children
  6. An estimate of the amount of time spent in subroutines of report when report was called from main.
  7. (report的子函数花费的总时间)
  8.  
  9. The sum of the self and children fields is an estimate of the amount of time spent within calls to
  10. report from main.
  11.  
  12. called
  13. Two numbers: the number of times report was called from main, followed by the total number of
  14. nonrecursive calls to report from all its callers.(两个数字:第一个,从main函数中调用report的次数,
  15. 第二个,report被非递归形式调用的总次数)
  16.  
  17. name and index number
  18. The name of the caller of report to which this line applies, followed by the caller's index number.
  19.  
  20. Not all functions have entries in the call graph; some options to gprof request the omission of certain
  21. functions. When a caller has no entry of its own, it still has caller-lines in the entries of the
  22. functions it calls.(不是所有的函数在call graph中都有entity)
  23.  
  24. If the caller is part of a recursion cycle, the cycle number is printed between the name and the index
  25. number.(如果caller是一个递归循环的一部分,循环编号会打印在caller的名字和编号之间)

如果caller不能确定,就会出现<spontaneous>作为caller。

GCC 命令行详解 zz

GCC 命令行详解 -L 指定库的路径 -l 指定需连接的库名

1。gcc包含的c/c++编译器
gcc,cc,c++,g++,gcc和cc是一样的,c++和g++是一样的,(没有看太明白前面这半句是什
么意思:))一般c程序就用gcc编译,c++程序就用g++编译

2。gcc的基本用法
gcc test.c这样将编译出一个名为a.out的程序
gcc test.c -o test这样将编译出一个名为test的程序,-o参数用来指定生成程序的名


3。为什么会出现undefined reference to 'xxxxx'错误?
首先这是链接错误,不是编译错误,也就是说如果只有这个错误,说明你的程序源码本
身没有问题,是你用编译器编译时参数用得不对,你没

有指定链接程序要用到得库,比如你的程序里用到了一些数学函数,那么你就要在编译
参数里指定程序要链接数学库,方法是在编译命令行里加入-lm。

4。-l参数和-L参数
-l参数就是用来指定程序要链接的库,-l参数紧接着就是库名,那么库名跟真正的库文
件名有什么关系呢?
就拿数学库来说,他的库名是m,他的库文件名是libm.so,很容易看出,把库文件名的
头lib和尾.so去掉就是库名了。

好了现在我们知道怎么得到库名了,比如我们自已要用到一个第三方提供的库名字叫lib
test.so,那么我们只要把libtest.so拷贝到/usr/lib

里,编译时加上-ltest参数,我们就能用上libtest.so库了(当然要用libtest.so库里
的函数,我们还需要与libtest.so配套的头文件)。

放在/lib/usr/lib/usr/local/lib里的库直接用-l参数就能链接了,但如果库文件
没放在这三个目录里,而是放在其他目录里,这时我们

只用-l参数的话,链接还是会出错,出错信息大概是:“/usr/bin/ld: cannot find
-lxxx”,也就是链接程序ld在那3个目录里找不到

libxxx.so,这时另外一个参数-L就派上用场了,比如常用的X11的库,它放在/usr/X11R
6/lib目录下,我们编译时就要用-L/usr/X11R6/lib -

lX11参数,-L参数跟着的是库文件所在的目录名。再比如我们把libtest.so放在/aaa/bb
b/ccc目录下,那链接参数就是-L/aaa/bbb/ccc -ltest

另外,大部分libxxxx.so只是一个链接,以RH9为例,比如libm.so它链接到/lib/libm.s
o.x,/lib/libm.so.6又链接到/lib/libm-2.3.2.so,

如果没有这样的链接,还是会出错,因为ld只会找libxxxx.so,所以如果你要用到xxxx
库,而只有libxxxx.so.x或者libxxxx-x.x.x.so,做一

个链接就可以了ln -s libxxxx-x.x.x.so libxxxx.so

手工来写链接参数总是很麻烦的,还好很多库开发包提供了生成链接参数的程序,名字
一般叫xxxx-config,一般放在/usr/bin目录下,比如

gtk1.2的链接参数生成程序是gtk-config,执行gtk-config --libs就能得到以下输出"-
L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic

-lgmodule -lglib -ldl -lXi -lXext -lX11 -lm",这就是编译一个gtk1.2程序所需的g
tk链接参数,xxx-config除了--libs参数外还有一个参


数是--cflags用来生成头文
件包含目录的,也就是-I参数,在下面我们将会讲到。你可以试试执行gtk-config
--libs --cflags,看看输出结果。
现在的问题就是怎样用这些输出结果了,最笨的方法就是复制粘贴或者照抄,聪明的办
法是在编译命令行里加入这个`xxxx-config --libs --

cflags`,比如编译一个gtk程序:gcc gtktest.c `gtk-config --libs --cflags`这样
就差
不多了。注意`不是单引号,而是1键左边那个键。

除了xxx-config以外,现在新的开发包一般都用pkg-config来生成链接参数,使用方法
跟xxx-config类似,但xxx-config是针对特定的开发包

,但pkg-config包含很多开发包的链接参数的生成,用pkg-config --list-all命令可以
列出所支持的所有开发包,pkg-config的用法就是pkg

-config pagName --libs --cflags,其中pagName是包名,是pkg-config--list-all里
列出名单中的一个,比如gtk1.2的名字就是gtk+,pkg-

config gtk+ --libs --cflags的作用跟gtk-config --libs --cflags是一样的。比如:
gcc gtktest.c `pkg-config gtk+ --libs --cflags`



5。-include和-I参数
-include用来包含头文件,但一般情况下包含头文件都在源码里用#include xxxxxx实现
,-include参数很少用。-I参数是用来指定头文件目录

,/usr/include目录一般是不用指定的,gcc知道去那里找,但是如果头文件不在/usr/i
nclude里我们就要用-I参数指定了,比如头文件放

在/myinclude目录里,那编译命令行就要加上-I/myinclude参数了,如果不加你会得到
一个"xxxx.h: No such file or directory"的错误。-I

参数可以用相对路径,比如头文件在当前目录,可以用-I.来指定。上面我们提到的--cf
lags参数就是用来生成-I参数的。

6。-O参数
这是一个程序优化参数,一般用-O2就是,用来优化程序用的,比如gcc test.c -O2,优
化得到的程序比没优化的要小,执行速度可能也有所提

高(我没有测试过)。

7。-shared参数
编译动态库时要用到,比如gcc -shared test.c -o libtest.so

8。几个相关的环境变量
PKG_CONFIG_PATH:用来指定pkg-config用到的pc文件的路径,默认是/usr/lib/pkgconf
ig,pc文件是文本文件,扩展名是.pc,里面定义开发

包的安装路径,Libs参数和Cflags参数等等。
CC:用来指定c编译器。
CXX:用来指定cxx编译器。
LIBS:跟上面的--libs作用差不多。
CFLAGS:跟上面的--cflags作用差不多。
CC,CXX,LIBS,CFLAGS手动编译时一般用不上,在做configure时有时用到,一般情况
下不用管。
环境变量设定方法:export ENV_NAME=xxxxxxxxxxxxxxxxx

9。关于交叉编译
交叉编译通俗地讲就是在一种平台上编译出能运行在体系结构不同的另一种平台上,比
如在我们地PC平台(X86 CPU)上编译出能运行在sparc

CPU平台上的程序,编译得到的程序在X86 CPU平台上是不能运行的,必须放到sparc
CPU平台上才能运行。
当然两个平台用的都是linux。

这种方法在异平台移植和嵌入式开发时用得非常普遍。

相对与交叉编译,我们平常做的编译就叫本地编译,也就是在当前平台编译,编译得到
的程序也是在本地执行。

用来编译这种程序的编译器就叫交叉编译器,相对来说,用来做本地编译的就叫本地编
译器,一般用的都是gcc,但这种gcc跟本地的gcc编译器

是不一样的,需要在编译gcc时用特定的configure参数才能得到支持交叉编译的gcc。

为了不跟本地编译器混淆,交叉编译器的名字一般都有前缀,比如sparc-xxxx-linux-gn
u-gcc,sparc-xxxx-linux-gnu-g++ 等等

10。交叉编译器的使用方法
使用方法跟本地的gcc差不多,但有一点特殊的是:必须用-L和-I参数指定编译器用spar
c系统的库和头文件,不能用本地(X86)
的库(头文件有时可以用本地的)。
例子:
sparc-xxxx-linux-gnu-gcc test.c -L/path/to/sparcLib -I/path/to/sparcInclude

 

gprof 看看gobject的效率,API调用。

http://www.ezdoum.com/stories.php?story=07/10/10/5189338

 

1. 重新编译glib

CFLAGS="-pg -g" ./autogen.sh --disable-gtk-doc --enable-debug=minimum \
     --prefix=/your install path --disable-shared

make; make install

2. 写个测试代码

#define REPEATS 100000

#include <glib.h>
#include <glib-object.h>

/*************************/

#define TYPE_TEST_OBJECT    (test_object_get_type ())
#define TEST_OBJECT(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
                             TYPE_TEST_OBJECT, TestObject))
#define IS_TEST_OBJECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
                             TYPE_TEST_OBJECT))

typedef struct _TestObject       TestObject;
typedef struct _TestObjectClass  TestObjectClass;

struct _TestObject
{
    GObject    parent_instance;

    gint       foo;
    gchar     *bar;
};

struct _TestObjectClass
{
    GObjectClass parent_class;
};

GType        test_object_get_type     (void) G_GNUC_CONST;
TestObject  *test_object_new          (void);

enum
{
    PROP_0,
    PROP_FOO,
    PROP_BAR
};

static void   test_object_class_init   (TestObjectClass *klass);
static void   test_object_init         (TestObject      *object);
static void   test_object_finalize     (GObject         *object);
static void   test_object_set_property (GObject         *object,
				        guint            property_id,
					const GValue    *value,
					GParamSpec      *pspec);
static void   test_object_get_property (GObject         *object,
                                        guint            property_id,
                                        GValue          *value,
                                        GParamSpec      *pspec);


static GObjectClass *parent_class = NULL;


GType
test_object_get_type (void)
{
  static GType test_object_type = 0;

  if (!test_object_type)
    {
      static const GTypeInfo test_object_info =
      {
        sizeof (TestObjectClass),
	(GBaseInitFunc) NULL,
	(GBaseFinalizeFunc) NULL,
	(GClassInitFunc) test_object_class_init,
	NULL,		/* class_finalize */
	NULL,		/* class_data     */
	sizeof (TestObject),
	0,              /* n_preallocs    */
	(GInstanceInitFunc) test_object_init,
      };

      test_object_type =
	g_type_register_static (G_TYPE_OBJECT, "TestObject",
				&test_object_info, 0);
    }

  return test_object_type;
}


static void
test_object_class_init (TestObjectClass *klass)
{
  GObjectClass *object_class;
  GParamSpec   *param_spec;

  object_class = G_OBJECT_CLASS (klass);
  parent_class = g_type_class_peek_parent (klass);

  object_class->set_property = test_object_set_property;
  object_class->get_property = test_object_get_property;

  param_spec = g_param_spec_string ("bar", NULL, NULL,
				    NULL,
				    G_PARAM_READABLE | G_PARAM_WRITABLE
#ifdef MAKE_IT_SLOW_2
                                    | G_PARAM_CONSTRUCT_ONLY
#endif
                                    );
  g_object_class_install_property (object_class, PROP_BAR, param_spec);

  param_spec = g_param_spec_int ("foo", NULL, NULL,
				 G_MININT, G_MAXINT, 0,
				 G_PARAM_READABLE | G_PARAM_WRITABLE
#ifdef MAKE_IT_SLOW_2
                                    | G_PARAM_CONSTRUCT_ONLY
#endif
                                 );
  g_object_class_install_property (object_class, PROP_FOO, param_spec);

  object_class->finalize = test_object_finalize;
}

static void
test_object_init (TestObject *object)
{
  object->bar = NULL;
  object->foo = 0;
}

static void
test_object_finalize (GObject *object)
{
  TestObject *obj;

  obj = (TestObject *) (object);

  g_free (obj->bar);

  if (G_OBJECT_CLASS (parent_class)->finalize)
    G_OBJECT_CLASS (parent_class)->finalize (object);
}

static void
test_object_set_property (GObject      *object,
			  guint         property_id,
			  const GValue *value,
			  GParamSpec   *pspec)
{
  TestObject *test_object;

  test_object = TEST_OBJECT (object);

  switch (property_id)
    {
    case PROP_FOO:
      test_object->foo = g_value_get_int (value);
      break;

    case PROP_BAR:
      g_free (test_object->bar);
      test_object->bar = g_value_dup_string (value);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}

static void
test_object_get_property (GObject     *object,
			  guint        property_id,
			  GValue      *value,
			  GParamSpec  *pspec)
{
  TestObject *test_object;

  test_object = TEST_OBJECT (object);

  switch (property_id)
    {
    case PROP_BAR:
      g_value_set_string (value, test_object->bar);
      break;

    case PROP_FOO:
      g_value_set_int (value, test_object->foo);
      break;

    default:
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
      break;
    }
}


TestObject *
test_object_new (void)
{
#ifdef MAKE_IT_SLOW_1
  return TEST_OBJECT (g_object_new (test_object_get_type (),
                                    "foo", 5,
                                    "bar", "blah",
                                    NULL));
#else
  TestObject *obj =
    TEST_OBJECT (g_object_new (test_object_get_type (),
                               NULL));
  obj->foo = 5;
  obj->bar = g_strdup ("blah");
  return obj;
#endif
}


/*************************/


int main (int argc, char *argv[])
{
  GTimeVal start, end;
  int i;
  TestObject *obj[REPEATS];

  g_type_init_with_debug_flags (G_TYPE_DEBUG_NONE);

  test_object_new ();

  g_get_current_time (&start);
  for (i = 0; i < REPEATS; i++)
    obj[i] = test_object_new ();
  g_get_current_time (&end);

  if (end.tv_usec - start.tv_usec < 0)
    end.tv_sec--, end.tv_usec += 1000000;
  g_print ("loop %d, %ld.%06ld sec\n", REPEATS,
           (end.tv_sec - start.tv_sec),
           (end.tv_usec - start.tv_usec));
  return 0;
}

3. 编译测试代码

Then try:
  gcc -Wall -pg -g gobject-test.c -o gobject-test \
    `pkg-config --cflags --libs gobject-2.0` 
vs.
  gcc -DMAKE_IT_SLOW_1 -Wall -pg -g gobject-test.c -o gobject-test \
    `pkg-config --cflags gobject-2.0` `pkg-config --libs gobject-2.0`
or
  gcc -DMAKE_IT_SLOW_2 -Wall -pg -g gobject-test.c -o gobject-test \
    `pkg-config --cflags gobject-2.0` `pkg-config --libs gobject-2.0`

 

4. 用gprof看看:

gprof -b  ./gobject-test
Flat profile:

Each sample counts as 0.01 seconds.
 no time accumulated

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
  0.00      0.00     0.00   200002     0.00     0.00  test_object_get_type
  0.00      0.00     0.00   100001     0.00     0.00  test_object_new


			Call graph


granularity: each sample hit covers 2 byte(s) no time propagated

index % time    self  children    called     name
                0.00    0.00  200002/200002      test_object_new [2]
[1]      0.0    0.00    0.00  200002         test_object_get_type [1]
-----------------------------------------------
                0.00    0.00  100001/100001      main [8]
[2]      0.0    0.00    0.00  100001         test_object_new [2]
                0.00    0.00  200002/200002      test_object_get_type [1]
-----------------------------------------------


Index by function name

   [1] test_object_get_type    [2] test_object_new

 

build again:

gcc -DMAKE_IT_SLOW_1 -Wall -pg -g gobject-test.c -o gobject-test `pkg-config --cflags --libs gobject-2.0`
gprof -b  ./gobject-test
Flat profile:

Each sample counts as 0.01 seconds.
  %   cumulative   self              self     total           
 time   seconds   seconds    calls  ns/call  ns/call  name    
 50.12      0.01     0.01   400004    12.53    12.53  test_object_get_type
 50.12      0.01     0.01                             frame_dummy
  0.00      0.01     0.00   100001     0.00    25.06  test_object_new


			Call graph


granularity: each sample hit covers 2 byte(s) for 99.77% of 0.01 seconds

index % time    self  children    called     name
                0.00    0.00  200002/400004      test_object_set_property [5]
                0.00    0.00  200002/400004      test_object_new [3]
[1]     50.0    0.01    0.00  400004         test_object_get_type [1]
-----------------------------------------------
                                                 <spontaneous>
[2]     50.0    0.01    0.00                 frame_dummy [2]
-----------------------------------------------
                0.00    0.00  100001/100001      main [4]
[3]     25.0    0.00    0.00  100001         test_object_new [3]
                0.00    0.00  200002/400004      test_object_get_type [1]
-----------------------------------------------
                                                 <spontaneous>
[4]     25.0    0.00    0.00                 main [4]
                0.00    0.00  100001/100001      test_object_new [3]
-----------------------------------------------
                                                 <spontaneous>
[5]     25.0    0.00    0.00                 test_object_set_property [5]
                0.00    0.00  200002/400004      test_object_get_type [1]
-----------------------------------------------


Index by function name

   [2] frame_dummy             [1] test_object_get_type    [3] test_object_new

 

build again:

gcc -DMAKE_IT_SLOW_2 -Wall -pg -g gobject-test.c -o gobject-test `pkg-config --cflags --libs gobject-2.0`
gprof -b  ./gobject-test
Flat profile:

Each sample counts as 0.01 seconds.
 no time accumulated

  %   cumulative   self              self     total           
 time   seconds   seconds    calls  Ts/call  Ts/call  name    
  0.00      0.00     0.00   400004     0.00     0.00  test_object_get_type
  0.00      0.00     0.00   100001     0.00     0.00  test_object_new


			Call graph


granularity: each sample hit covers 2 byte(s) no time propagated

index % time    self  children    called     name
                0.00    0.00  200002/400004      test_object_set_property [14]
                0.00    0.00  200002/400004      test_object_new [2]
[1]      0.0    0.00    0.00  400004         test_object_get_type [1]
-----------------------------------------------
                0.00    0.00  100001/100001      main [8]
[2]      0.0    0.00    0.00  100001         test_object_new [2]
                0.00    0.00  200002/400004      test_object_get_type [1]
-----------------------------------------------


Index by function name

   [1] test_object_get_type    [2] test_object_new

gsignal 1.new/2.connect/3.emit

gobjec相关学习文章的list.

 

我们从一个实际的例子看看:

New Signal:

1. New一个signal

在类初始化时,先指定了成员method:

klass->show = gtk_widget_real_show;

 然后定义信号:

  widget_signals[SHOW] =
    g_signal_new (I_("show"),
		  G_TYPE_FROM_CLASS (gobject_class),
		  G_SIGNAL_RUN_FIRST,
		  G_STRUCT_OFFSET (GtkWidgetClass, show),
		  NULL, NULL,
		  _gtk_marshal_VOID__VOID,
		  G_TYPE_NONE, 0);

这里注意:G_STRUCT_OFFSET( GtkWidgetClass, show)

这个是靠地址偏移,找到GtkWidgetClass的成员函数指针的地址

 

2. 往下看:

guint
g_signal_new (const gchar	 *signal_name,
	      GType		  itype,
	      GSignalFlags	  signal_flags,
	      guint               class_offset,
	      GSignalAccumulator  accumulator,
	      gpointer		  accu_data,
	      GSignalCMarshaller  c_marshaller,
	      GType		  return_type,
	      guint		  n_params,
	      ...)
{
  va_list args;
  guint signal_id;

  g_return_val_if_fail (signal_name != NULL, 0);
  
  va_start (args, n_params);

  signal_id = g_signal_new_valist (signal_name, itype, signal_flags,
                                   class_offset ? g_signal_type_cclosure_new (itype, class_offset) : NULL,
				   accumulator, accu_data, c_marshaller,
                                   return_type, n_params, args);

  va_end (args);

一般情况下,class_offset不是0, 所以会调用:g_signal_type_cclosure_new (itype, class_offset)

GClosure*
g_signal_type_cclosure_new (GType    itype,
			    guint    struct_offset)
{
  GClosure *closure;
  
  g_return_val_if_fail (G_TYPE_IS_CLASSED (itype) || G_TYPE_IS_INTERFACE (itype), NULL);
  g_return_val_if_fail (struct_offset >= sizeof (GTypeClass), NULL);
  
  closure = g_closure_new_simple (sizeof (GClosure), (gpointer) itype);
  if (G_TYPE_IS_INTERFACE (itype))
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_iface_meta_marshal);
  else
    g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);
  
  return closure;
}

GUINT_TO_POINTER (struct_offset), 这个就是函数指针:指的就是 GtkWidgetClass的Method:(*show)

这个(*show)作为回调函数的。

往下看:

void
g_closure_set_meta_marshal (GClosure       *closure,
			    gpointer        marshal_data,
			    GClosureMarshal meta_marshal)
{
  GClosureNotifyData *notifiers;

  g_return_if_fail (closure != NULL);
  g_return_if_fail (meta_marshal != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (closure->meta_marshal == 0);

  notifiers = closure->notifiers;
  closure->notifiers = g_renew (GClosureNotifyData, NULL, CLOSURE_N_NOTIFIERS (closure) + 1);
  if (notifiers)
    {
      /* usually the meta marshal will be setup right after creation, so the
       * g_memmove() should be rare-case scenario
       */
      g_memmove (closure->notifiers + 1, notifiers, CLOSURE_N_NOTIFIERS (closure) * sizeof (notifiers[0]));
      g_free (notifiers);
    }
  closure->notifiers[0].data = marshal_data;
  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
  SET (closure, meta_marshal, 1);
}

这里的marshal_data, 就是上面的:GUINT_TO_POINTER (struct_offset), 就是用户指定的callback了。

同时,这个callback作为最后一个参数,传入:g_type_iface_meta_marshal

static void
g_type_class_meta_marshal (GClosure       *closure,
			   GValue /*out*/ *return_value,
			   guint           n_param_values,
			   const GValue   *param_values,
			   gpointer        invocation_hint,
			   gpointer        marshal_data)
{
  GTypeClass *class;
  gpointer callback;
  /* GType itype = (GType) closure->data; */
  guint offset = GPOINTER_TO_UINT (marshal_data);
  
  class = G_TYPE_INSTANCE_GET_CLASS (g_value_peek_pointer (param_values + 0), itype, GTypeClass);
  callback = G_STRUCT_MEMBER (gpointer, class, offset);
  if (callback)
    closure->marshal (closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      callback);
}

在这个函数里面,首先提取callback.

如果有callback的化,后面会把callback做为最后一个参数,传入到marshal里面;

比如:_gtk_marshal_VOID__VOID == g_cclosure_marshal_VOID__VOID

void
g_cclosure_marshal_VOID__VOID (GClosure     *closure,
                               GValue       *return_value G_GNUC_UNUSED,
                               guint         n_param_values,
                               const GValue *param_values,
                               gpointer      invocation_hint G_GNUC_UNUSED,
                               gpointer      marshal_data)
{
  typedef void (*GMarshalFunc_VOID__VOID) (gpointer     data1,
                                           gpointer     data2);
  register GMarshalFunc_VOID__VOID callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 1);

  if (G_CCLOSURE_SWAP_DATA (closure))
    {
      data1 = closure->data;
      data2 = g_value_peek_pointer (param_values + 0);
    }
  else
    {
      data1 = g_value_peek_pointer (param_values + 0);
      data2 = closure->data;
    }
  callback = (GMarshalFunc_VOID__VOID) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            data2);
}

最后真正调用的这是前面指定的callback: klass->show = gtk_widget_real_show;

 

上边把user callback同closure绑定之后,g_signal_new_valist会调用g_signal_newv

guint
g_signal_newv (const gchar       *signal_name,
               GType              itype,
               GSignalFlags       signal_flags,
               GClosure          *class_closure,
               GSignalAccumulator accumulator,
	       gpointer		  accu_data,
               GSignalCMarshaller c_marshaller,
               GType		  return_type,
               guint              n_params,
               GType		 *param_types)
{
  gchar *name;
  guint signal_id, i;
  SignalNode *node;
  
  g_return_val_if_fail (signal_name != NULL, 0);
  g_return_val_if_fail (G_TYPE_IS_INSTANTIATABLE (itype) || G_TYPE_IS_INTERFACE (itype), 0);
  if (n_params)
    g_return_val_if_fail (param_types != NULL, 0);
  g_return_val_if_fail ((return_type & G_SIGNAL_TYPE_STATIC_SCOPE) == 0, 0);
  if (return_type == (G_TYPE_NONE & ~G_SIGNAL_TYPE_STATIC_SCOPE))
    g_return_val_if_fail (accumulator == NULL, 0);
  if (!accumulator)
    g_return_val_if_fail (accu_data == NULL, 0);

  name = g_strdup (signal_name);
  g_strdelimit (name, G_STR_DELIMITERS ":^", '_');  /* FIXME do character checks like for types */
  
  SIGNAL_LOCK ();
  
  signal_id = signal_id_lookup (g_quark_try_string (name), itype);
  node = LOOKUP_SIGNAL_NODE (signal_id);

  /* setup permanent portion of signal node */
  if (!node)
    {
      SignalKey key;
      
      signal_id = g_n_signal_nodes++;
      node = g_new (SignalNode, 1);
      node->signal_id = signal_id;
      g_signal_nodes = g_renew (SignalNode*, g_signal_nodes, g_n_signal_nodes);
      g_signal_nodes[signal_id] = node;
      node->itype = itype;
      node->name = name;
      key.itype = itype;
      key.quark = g_quark_from_string (node->name);
      key.signal_id = signal_id;
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);
      g_strdelimit (name, "_", '-');
      node->name = g_intern_string (name);
      key.quark = g_quark_from_string (name);
      g_signal_key_bsa = g_bsearch_array_insert (g_signal_key_bsa, &g_signal_key_bconfig, &key);

      TRACE(GOBJECT_SIGNAL_NEW(signal_id, name, itype));
    }
  node->destroyed = FALSE;
  node->test_class_offset = 0;

  /* setup reinitializable portion */
  node->flags = signal_flags & G_SIGNAL_FLAGS_MASK;
  node->n_params = n_params;
  node->param_types = g_memdup (param_types, sizeof (GType) * n_params);
  node->return_type = return_type;
  node->class_closure_bsa = NULL;
  if (accumulator)
    {
      node->accumulator = g_new (SignalAccumulator, 1);
      node->accumulator->func = accumulator;
      node->accumulator->data = accu_data;
    }
  else
    node->accumulator = NULL;
  node->c_marshaller = c_marshaller;
  node->emission_hooks = NULL;
  if (class_closure)
    signal_add_class_closure (node, 0, class_closure);
  else if (G_TYPE_IS_INSTANTIATABLE (itype) && return_type == G_TYPE_NONE)
    {
      /* optimize NOP emissions */
      node->test_class_offset = TEST_CLASS_MAGIC;
    }
  SIGNAL_UNLOCK ();

  g_free (name);

  return signal_id;
}

上面这个函数完成:

1. 如果是新建的Signal, 则把它放入SignalNode数组中:g_signal_nodes[signal_id]

2. 并且放入用于查询的二分数组中:g_signal_key_bsa

3. 因为user指定了自己的回调,所以signal_add_class_closure()把class closure也放入

    到相关的二分查找数组中:node->class_closure_bsa

4. 指定c_marshal, 这是:_gtk_marshal_VOID__VOID

 

New signal的小结:

1. Signal Node生成

2. callback指定

3. c Marshal指定

 

Signal Connect:

gsignal.c提供了3个相关的API:

gulong	 g_signal_connect_closure_by_id	      (gpointer		  instance,
					       guint		  signal_id,
					       GQuark		  detail,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_closure	      (gpointer		  instance,
					       const gchar       *detailed_signal,
					       GClosure		 *closure,
					       gboolean		  after);
gulong	 g_signal_connect_data		      (gpointer		  instance,
					       const gchar	 *detailed_signal,
					       GCallback	  c_handler,
					       gpointer		  data,
					       GClosureNotify	  destroy_data,
					       GConnectFlags	  connect_flags);

用的最多的是g_signal_connect_data, 直接用它的也有,不过大多是用经过包装后的:

gtype.h:

#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)
#define g_signal_connect_after(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_AFTER)
#define g_signal_connect_swapped(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, G_CONNECT_SWAPPED)

以及:

gobject.c:

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)

这个函数其实是封装:g_signal_connect_closure/g_signal_connect_data

gulong
g_signal_connect_object (gpointer      instance,
			 const gchar  *detailed_signal,
			 GCallback     c_handler,
			 gpointer      gobject,
			 GConnectFlags connect_flags)
{
  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (instance), 0);
  g_return_val_if_fail (detailed_signal != NULL, 0);
  g_return_val_if_fail (c_handler != NULL, 0);

  if (gobject)
    {
      GClosure *closure;

      g_return_val_if_fail (G_IS_OBJECT (gobject), 0);

      closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, gobject);

      return g_signal_connect_closure (instance, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
    }
  else
    return g_signal_connect_data (instance, detailed_signal, c_handler, NULL, NULL, connect_flags);
}

 

 

 Signal Emit: 

void
gtk_widget_show (GtkWidget *widget)
{
  g_return_if_fail (GTK_IS_WIDGET (widget));

  if (!gtk_widget_get_visible (widget))
    {
      g_object_ref (widget);
      gtk_widget_push_verify_invariants (widget);

      if (!gtk_widget_is_toplevel (widget))
        gtk_widget_queue_resize (widget);

      /* see comment in set_parent() for why this should and can be
       * conditional
       */
      if (widget->priv->need_compute_expand ||
          widget->priv->computed_hexpand ||
          widget->priv->computed_vexpand)
        {
          if (widget->priv->parent != NULL)
            gtk_widget_queue_compute_expand (widget->priv->parent);
        }

      g_signal_emit (widget, widget_signals[SHOW], 0);
      g_object_notify (G_OBJECT (widget), "visible");

      gtk_widget_pop_verify_invariants (widget);
      g_object_unref (widget);
    }
}

 

void
g_signal_emit (gpointer instance,
	       guint    signal_id,
	       GQuark   detail,
	       ...)
{
  va_list var_args;

  va_start (var_args, detail);
  g_signal_emit_valist (instance, signal_id, detail, var_args);
  va_end (var_args);
}

 

static gboolean
signal_emit_unlocked_R (SignalNode   *node,
			GQuark	      detail,
			gpointer      instance,
			GValue	     *emission_return,
			const GValue *instance_and_params)
{
  SignalAccumulator *accumulator;
  Emission emission;
  GClosure *class_closure;
  HandlerList *hlist;
  Handler *handler_list = NULL;
  GValue *return_accu, accu = { 0, };
  guint signal_id;
  gulong max_sequential_handler_number;
  gboolean return_value_altered = FALSE;

  emission.instance = instance;
  emission.ihint.signal_id = node->signal_id;
  emission.ihint.detail = detail;
  emission.ihint.run_type = 0;
  emission.state = 0;
  emission.chain_type = G_TYPE_NONE;
  emission_push ((node->flags & G_SIGNAL_NO_RECURSE) ? &g_restart_emissions : &g_recursive_emissions, &emission);
  class_closure = signal_lookup_closure (node, instance);
  
 EMIT_RESTART:
  
  if (handler_list)
    handler_unref_R (signal_id, instance, handler_list);
  max_sequential_handler_number = g_handler_sequential_number;
  hlist = handler_list_lookup (signal_id, instance);
  handler_list = hlist ? hlist->handlers : NULL;
  if (handler_list)
    handler_ref (handler_list);
  
  emission.ihint.run_type = G_SIGNAL_RUN_FIRST;
  
  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);
      if (!accumulate (&emission.ihint, emission_return, &accu, accumulator) &&
	  emission.state == EMISSION_RUN)
	emission.state = EMISSION_STOP;
      SIGNAL_LOCK ();
      emission.chain_type = G_TYPE_NONE;
      return_value_altered = TRUE;
      
      if (emission.state == EMISSION_STOP)
	goto EMIT_CLEANUP;
      else if (emission.state == EMISSION_RESTART)
	goto EMIT_RESTART;
    }
  

上面做如下的动作:

1)做一个emmission, 收集好signal的相关信息,并且压入list中,临时保存信息

2)找到和这个signal相关联的closure (callback)

3)  根据signal id, 找到hander list

4)  如果有closure, 则调用g_closure_invoke(), 启动callback流程

  if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)
    {
      emission.state = EMISSION_RUN;

      emission.chain_type = G_TYPE_FROM_INSTANCE (instance);
      SIGNAL_UNLOCK ();
      g_closure_invoke (class_closure,
			return_accu,
			node->n_params + 1,
			instance_and_params,
			&emission.ihint);

 

   4.1) 调用closure_invoke_notifier() //保护参数

   4.2) 执行marshal(), 其中在marshal中调用 user callback

          因为参数传入时,用GValue形式传入,在marshal中:

          (1)需要把参数由Gvalue --> C 参数形式

          (2)调用user callback

          (3)把返回的值再包装成GValue形式带回

5. 如果通过g_signal_connect_xxx() 注册了自己的回调,会调用handler_list

  if (handler_list)
    {
      Handler *handler = handler_list;
     
      emission.state = EMISSION_RUN;
      handler_ref (handler);
      do
 {
   Handler *tmp;
  
   if (handler->after)
     {
       handler_unref_R (signal_id, instance, handler_list);
       handler_list = handler;
       break;
     }
   else if (!handler->block_count && (!handler->detail || handler->detail == detail) &&
     handler->sequential_number < max_sequential_handler_number)
     {
       SIGNAL_UNLOCK ();
       g_closure_invoke (handler->closure,
    return_accu,
    node->n_params + 1,
    instance_and_params,
    &emission.ihint);

 

gobjec相关学习文章的list.

 

 

 

内核的likely()/unlikely和glib的G_LIKELY/G_UNLIKELY zz

内核中的 likely() 与 unlikely()


在 2.6 内核中,随处可以见到 likely() 和 unlikely() 的身影,那么为什么要用它们?它们之间有什么区别?

 

首先要明确:

if(likely(value)) 等价于 if(value)

if(unlikely(value)) 也等价于 if(value)

也就是说 likely() 和 unlikely() 从阅读和理解代码的角度来看,是一样的!!!


这两个宏在内核中定义如下:

 

#define likely(x) __builtin_expect((x),1)
#define unlikely(x) __builtin_expect((x),0)



__builtin_expect() 是 GCC (version >= 2.96)提供给程序员使用的,目的是将“分支转移”的信息提供给编译器,这样编译器可以对代码进行优化,以减少指令跳转带来的性能下降。

__builtin_expect((x),1) 表示 x 的值为真的可能性更大;
__builtin_expect((x),0) 表示 x 的值为假的可能性更大。

也就是说,使用 likely() ,执行 if 后面的语句 的机会更大,使用unlikely(),执行else 后面的语句的机会更大。
例如下面这段代码,作者就认为 prev 不等于 next 的可能性更大,

 

if (likely(prev != next)) {
next->timestamp = now;
...
} else {
...;
}


通过这种方式,编译器在编译过程中,会将可能性更大的代码紧跟着起面的代码,从而减少指令跳转带来的性能上的下降。


下面以两个例子来加深这种理解:

第一个例子: example1.c

 

int testfun(int x)
{
if(__builtin_expect(x, 0)) {
^^^--- We instruct the compiler, "else" block is more probable
x = 5;
x = x * x;
} else {
x = 6;
}
return x;
}


在这个例子中,我们认为 x 为0的可能性更大

编译以后,通过 objdump 来观察汇编指令,在我的 2.4 内核机器上,结果如下:

# gcc -O2 -c example1.c
# objdump -d example1.o

 

Disassembly of section .text:

00000000 <testfun>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 75 07 jne 11 <testfun+0x11>
a: b8 06 00 00 00 mov $0x6,%eax
f: c9 leave
10: c3 ret
11: b8 19 00 00 00 mov $0x19,%eax
16: eb f7 jmp f <testfun+0xf>



可以看到,编译器使用的是 jne (不相等跳转)指令,并且 else block 中的代码紧跟在后面。

8: 75 07 jne 11 <testfun+0x11>
a: b8 06 00 00 00 mov $0x6,%eax


第二个例子: example2.c


 

int testfun(int x)
{
if(__builtin_expect(x, 1)) {
^^^ --- We instruct the compiler, "if" block is more probable
x = 5;
x = x * x;
} else {
x = 6;
}
return x;
}


在这个例子中,我们认为 x 不为 0 的可能性更大

编译以后,通过 objdump 来观察汇编指令,在我的 2.4 内核机器上,结果如下:

# gcc -O2 -c example2.c
# objdump -d example2.o


 

Disassembly of section .text:

00000000 <testfun>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 8b 45 08 mov 0x8(%ebp),%eax
6: 85 c0 test %eax,%eax
8: 74 07 je 11 <testfun+0x11>
a: b8 19 00 00 00 mov $0x19,%eax
f: c9 leave
10: c3 ret
11: b8 06 00 00 00 mov $0x6,%eax
16: eb f7 jmp f <testfun+0xf>



这次编译器使用的是 je (相等跳转)指令,并且 if block 中的代码紧跟在后面。

8: 74 07 je 11 <testfun+0x11>
a: b8 19 00 00 00 mov $0x19,%eax

 

G_LIKELY和G_UNLIKELY

在GTK+2.0源码中有很多这样的宏:G_LIKELY和G_UNLIKELY。比如下面这段代码:

if (G_LIKELY (acat == 1))       /* allocate through magazine layer */
      {
        ThreadMemory *tmem = thread_memory_from_self();
        guint ix = SLAB_INDEX (allocator, chunk_size);
        if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
          {
            thread_memory_swap_magazines (tmem, ix);
            if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))
              thread_memory_magazine1_reload (tmem, ix);
          }
        mem = thread_memory_magazine1_alloc (tmem, ix);
      }

在源码中,宏G_LIKELY和G_UNLIKELY 是这么定义的:

#define G_LIKELY(expr) (__builtin_expect (_G_BOOLEAN_EXPR(expr), 1))
  #define G_UNLIKELY(expr) (__builtin_expect (_G_BOOLEAN_EXPR(expr), 0))

宏_G_BOOLEAN_EXPR的作用是把expr转换为0和1,即真假两种。要理解宏G_LIKELY和G_UNLIKELY ,很明显必须理解__builtin_expect。__builtin_expect是GCC(version>=2.9)引进的宏,其作用就是帮助编译器判断条件跳转的预期值,避免跳转造成时间乱费。拿上面的代码来说:

if (G_LIKELY (acat == 1)) //表示大多数情况下if里面是真,程序大多数直接执行if里面的程序

if (G_UNLIKELY (thread_memory_magazine1_is_empty (tmem, ix)))//表示大多数情况if里面为假,程序大多数直接执行else里面的程序

可能大家看到还是一头雾水,看下面一段就会明白其中的乐趣啦;

//test_builtin_expect.c 
#define LIKELY(x) __builtin_expect(!!(x), 1)
#define UNLIKELY(x) __builtin_expect(!!(x), 0)
int test_likely(int x)
{
 if(LIKELY(x))
 {
    x = 5;
 }
 else
 {
    x = 6;
 }
  
 return x;
}
int test_unlikely(int x)
{
 if(UNLIKELY(x))
 {
    x = 5;
 }
 else
 {
    x = 6;
 }
  
 return x;
}
[lammy@localhost test_builtin_expect]$ gcc -fprofile-arcs -O2 -c test_builtin_expect.c 
[lammy@localhost test_builtin_expect]$ objdump -d test_builtin_expect.o
test_builtin_expect.o:       file format elf32-i386
Disassembly of section .text:
00000000 <test_likely>:
     0: 55                      push     %ebp
     1: 89 e5                   mov      %esp,%ebp
     3: 8b 45 08                mov      0x8(%ebp),%eax
     6: 83 05 38 00 00 00 01  addl     $0x1,0x38
     d: 83 15 3c 00 00 00 00  adcl     $0x0,0x3c
  14: 85 c0                   test     %eax,%eax
  16: 74 15                   je       2d <test_likely+0x2d>//主要看这里
  18: 83 05 40 00 00 00 01  addl     $0x1,0x40
  1f: b8 05 00 00 00          mov      $0x5,%eax
  24: 83 15 44 00 00 00 00  adcl     $0x0,0x44
  2b: 5d                      pop      %ebp
  2c: c3                      ret      
  2d: 83 05 48 00 00 00 01  addl     $0x1,0x48
  34: b8 06 00 00 00          mov      $0x6,%eax
  39: 83 15 4c 00 00 00 00  adcl     $0x0,0x4c
  40: 5d                      pop      %ebp
  41: c3                      ret      
  42: 8d b4 26 00 00 00 00  lea      0x0(%esi,%eiz,1),%esi
  49: 8d bc 27 00 00 00 00  lea      0x0(%edi,%eiz,1),%edi
00000050 <test_unlikely>:
  50: 55                      push     %ebp
  51: 89 e5                   mov      %esp,%ebp
  53: 8b 55 08                mov      0x8(%ebp),%edx
  56: 83 05 20 00 00 00 01  addl     $0x1,0x20
  5d: 83 15 24 00 00 00 00  adcl     $0x0,0x24
  64: 85 d2                   test     %edx,%edx
  66: 75 15                   jne      7d <test_unlikely+0x2d>//主要看这里
  68: 83 05 30 00 00 00 01  addl     $0x1,0x30
  6f: b8 06 00 00 00          mov      $0x6,%eax
  74: 83 15 34 00 00 00 00  adcl     $0x0,0x34
  7b: 5d                      pop      %ebp
  7c: c3                      ret      
  7d: 83 05 28 00 00 00 01  addl     $0x1,0x28
  84: b8 05 00 00 00          mov      $0x5,%eax
  89: 83 15 2c 00 00 00 00  adcl     $0x0,0x2c
  90: 5d                      pop      %ebp
  91: c3                      ret      
  92: 8d b4 26 00 00 00 00  lea      0x0(%esi,%eiz,1),%esi
  99: 8d bc 27 00 00 00 00  lea      0x0(%edi,%eiz,1),%edi
000000a0 <_GLOBAL__I_65535_0_test_likely>:
  a0: 55                      push     %ebp
  a1: 89 e5                   mov      %esp,%ebp
  a3: 83 ec 08                sub 

C中的volatile用法 zz

C中的volatile用法

volatile 影响编译器编译的结果,指出,volatile 变量是随时可能发生变化的,与volatile变量有关的运算,不要进行编译优化,以免出错,(VC++ 在产生release版可执行码时会进行编译优化,加volatile关键字的变量有关的运算,将不进行编译优化。)。
例如:
volatile int i=10;
int j = i;
...
int k = i;
volatile 告诉编译器i是随时可能发生变化的,每次使用它的时候必须从i的地址中读取,因而编译器生成的可执行码会重新从i的地址读取数据放在k中。
而优化做法是,由于编译器发现两次从i读数据的代码之间的代码没有对i进行过操作,它会自动把上次读的数据放在k中。而不是重新从i里面读。这样以来,如果i是一个寄存器变量或者表示一个端口数据就容易出错,所以说volatile可以保证对特殊地址的稳定访问,不会出错。

/**********************

一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量
回答不出这个问题的人是不会被雇佣的。我认为这是区分C程序员和嵌入式系统程序员的最基本的问题。搞嵌入式的家伙们经常同硬件、中断、RTOS等等打交道,所有这些都要求用到volatile变量。不懂得volatile的内容将会带来灾难。假设被面试者正确地回答了这是问题(嗯,怀疑是否会是这样),我将稍微深究一下,看一下这家伙是不是直正懂得volatile完全的重要性。
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2); 一个指针可以是volatile 吗?解释为什么。
3); 下面的函数有什么错误:
int square(volatile int *ptr)
{
return *ptr * *ptr;
}
下面是答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是const因为程序不应该试图去修改它。
2); 是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指针时。
3) 这段代码有点变态。这段代码的目的是用来返指针*ptr指向值的平方,但是,由于*ptr指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int *ptr)
{
int a,b;
a = *ptr;
b = *ptr;
return a * b;
}
由于*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:
long square(volatile int *ptr)
{
int a;
a = *ptr;
return a * a;
}
位操作(Bit manipulation)

//*********************

嵌入式编程中经常用到 volatile这个关键字,在网上查了下他的用法可以归结为以下两点:

一:告诉compiler不能做任何优化

比如要往某一地址送两指令:
int *ip =...; //设备地址
*ip = 1; //第一个指令
*ip = 2; //第二个指令
以上程序compiler可能做优化而成:
int *ip = ...;
*ip = 2;
结果第一个指令丢失。如果用volatile, compiler就不允许做任何的优化,从而保证程序的原意:
volatile int *ip = ...;
*ip = 1;
*ip = 2;
即使你要compiler做优化,它也不会把两次付值语句间化为一。它只能做其它的优化。这对device driver程序员很有用。

二:表示用volatile定义的变量会在程序外被改变,每次都必须从内存中读取,而不能把他放在cache或寄存器中重复使用。

如 volatile char a;
a=0;
while(!a){
//do some things;
}
doother();
如果没有 volatile doother()不会被执行

 

__asm__ __volatile__内嵌汇编用法简述 zz

__asm__ __volatile__内嵌汇编用法简述 在阅读C/C++原码时经常会遇到内联汇编的情况,下面简要介绍下__asm__ __volatile__内嵌汇编用法。因为我们华清远见教学平台是ARM体系结构的,所以下面的示例都是用ARM汇编。

带有C/C++表达式的内联汇编格式为:

__asm__ __volatile__("Instruction List" : Output : Input : Clobber/Modify);

其中每项的概念及功能用法描述如下:

1、 __asm__

__asm__是GCC 关键字asm 的宏定义:

#define __asm__ asm

__asm__或asm 用来声明一个内联汇编表达式,所以任何一个内联汇编表达式都是以它开头的,是必不可少的。

2、Instruction List

Instruction List 是汇编指令序列。它可以是空的,比如:__asm__ __volatile__(""); 或 __asm__ ("");都是完全合法的内联汇编表达式,只不过这两条语句没有什么意义。但并非所有Instruction List 为空的内联汇编表达式都是没有意义的,比如:__asm__ ("":::"memory");

就非常有意义,它向GCC 声明:“内存作了改动”,GCC 在编译的时候,会将此因素考虑进去。 当在"Instruction List"中有多条指令的时候,可以在一对引号中列出全部指令,也可以将一条 或几条指令放在一对引号中,所有指令放在多对引号中。如果是前者,可以将每一条指令放在一行,如果要将多条指令放在一行,则必须用分号(;)或换行符(/n)将它们分开. 综上述:(1)每条指令都必须被双引号括起来 (2)两条指令必须用换行或分号分开。

例如: 在ARM系统结构上关闭中断的操作

int disable_interrupts (void)
{
unsigned long old,temp;
__asm__ __volatile__("mrs %0, cpsr/n"
"orr %1, %0, #0x80/n"
"msr cpsr_c, %1"
: "=r" (old), "=r" (temp)
:
: "memory");
return (old & 0x80) == 0;
}

3. __volatile__

__volatile__是GCC 关键字volatile 的宏定义

#define __volatile__ volatile

__volatile__或volatile 是可选的。如果用了它,则是向GCC 声明不允许对该内联汇编优化,否则当 使用了优化选项(-O)进行编译时,GCC 将会根据自己的判断决定是否将这个内联汇编表达式中的指令优化掉。

4、 Output

Output 用来指定当前内联汇编语句的输出

例如:从arm协处理器p15中读出C1值

static unsigned long read_p15_c1 (void)
{
unsigned long value;
__asm__ __volatile__(
"mrc p15, 0, %0, c1, c0, 0 @ read control reg/n"
: "=r" (value) @编译器选择一个R*寄存器
:
: "memory");
#ifdef MMU_DEBUG
printf ("p15/c1 is = %08lx/n", value);
#endif
return value;
}

5、 Input

Input 域的内容用来指定当前内联汇编语句的输入Output和Input中,格式为形如“constraint”(variable)的列表(逗号分隔)

例如:向arm协处理器p15中写入C1值

static void write_p15_c1 (unsigned long value)
{
#ifdef MMU_DEBUG
printf ("write %08lx to p15/c1/n", value);
#endif
__asm__ __volatile__(
"mcr p15, 0, %0, c1, c0, 0 @ write it back/n"
:
: "r" (value) @编译器选择一个R*寄存器
: "memory");
read_p15_c1 ();
}

6.、Clobber/Modify

有时候,你想通知GCC当前内联汇编语句可能会对某些寄存器或内存进行修改,希望GCC在编译时能够将这一点考虑进去。那么你就可以在Clobber/Modify域声明这些寄存器或内存。这种情况一般发生在一个寄存器出现在"Instruction List",但却不是由Input/Output操作表达式所指定的,也不是在一些Input/Output操作表达式使用"r"约束时由GCC 为其选择的,同时此寄存器被"Instruction List"中的指令修改,而这个寄存器只是供当前内联汇编临时使用的情况。

例如:

__asm__ ("mov R0, #0x34" : : : "R0");

寄存器R0出现在"Instruction List中",并且被mov指令修改,但却未被任何Input/Output操作表达式指定,所以你需要在Clobber/Modify域指定"R0",以让GCC知道这一点。

因为你在Input/Output操作表达式所指定的寄存器,或当你为一些Input/Output操作表达式使用"r"约束,让GCC为你选择一个寄存器时,GCC对这些寄存器是非常清楚的——它知道这些寄存器是被修改的,你根本不需要在Clobber/Modify域再声明它们。但除此之外, GCC对剩下的寄存器中哪些会被当前的内联汇编修改一无所知。所以如果你真的在当前内联汇编指令中修改了它们,那么就最好在Clobber/Modify 中声明它们,让GCC针对这些寄存器做相应的处理。否则有可能会造成寄存器的不一致,从而造成程序执行错误。

如果一个内联汇编语句的Clobber/Modify域存在"memory",那么GCC会保证在此内联汇编之前,如果某个内存的内容被装入了寄存器,那么在这个内联汇编之后,如果需要使用这个内存处的内容,就会直接到这个内存处重新读取,而不是使用被存放在寄存器中的拷贝。因为这个 时候寄存器中的拷贝已经很可能和内存处的内容不一致了。

这只是使用"memory"时,GCC会保证做到的一点,但这并不是全部。因为使用"memory"是向GCC声明内存发生了变化,而内存发生变化带来的影响并不止这一点。

例如:

int main(int __argc, char* __argv[])
{
int* __p = (int*)__argc;
(*__p) = 9999;
__asm__("":::"memory");
if((*__p) == 9999)
return 5;
return (*__p);
}

本例中,如果没有那条内联汇编语句,那个if语句的判断条件就完全是一句废话。GCC在优化时会意识到这一点,而直接只生成return 5的汇编代码,而不会再生成if语句的相关代码,而不会生成return (*__p)的相关代码。但你加上了这条内联汇编语句,它除了声明内存变化之外,什么都没有做。但GCC此时就不能简单的认为它不需要判断都知道 (*__p)一定与9999相等,它只有老老实实生成这条if语句的汇编代码,一起相关的两个return语句相关代码。

另外在linux内核中内存屏障也是基于它实现的include/asm/system.h中

# define barrier() _asm__volatile_("": : :"memory")

主要是保证程序的执行遵循顺序一致性。呵呵,有的时候你写代码的顺序,不一定是最终执行的顺序,这个是处理器有关的。

gobject的调试。

gobjec相关学习文章的list.

gobject中,基本的gtype, 以及继承,派生关系;

gobject 类初始化,对象初始化,接口初始化,方法调用,信号发射等;需要仔细的观察;

所以我们需要函数完成2个功能:

1. 函数调用层级关系显示(类初始化,对象初始化,接口初始化...)

2. gtype/gobject父子关系,显示

 

参考gobject-query.c, 修改一下:

gtype.h:

void g_type_get_node_tree(void);


void g_type_enter_func(const char *name);
void g_type_leave_func(const char *name);

#define G_ENTER_FUNC g_type_enter_func(__FUNCTION__)
#define G_LEAVE_FUNC g_type_leave_func(__FUNCTION__)

gtype.c:

static GType static_gtype_root = G_TYPE_OBJECT;

static void
_g_type_show_nodes (GType        type,
	    GType        sibling,
	    const gchar *indent)
{
	static gchar *indent_inc = NULL;
	static guint spacing = 1;
	static gboolean recursion = TRUE;

#define	O_SPACE	" "
#define	O_ESPACE ""
#define	O_BRANCH "+"
#define	O_VLINE "|"
#define	O_LLEAF	"`"
#define	O_KEY_FILL "_"

	GType   *children;
	guint i;

	if (!type)
		return;

	if (!indent_inc)
	{
		indent_inc = g_new (gchar, strlen (O_SPACE) + 1);
		*indent_inc = 0;
		strcpy (indent_inc, O_SPACE);
	}  
  
	children = g_type_children (type, NULL);

	if (type != static_gtype_root)
		for (i = 0; i < spacing; i++)
			g_printf("%s%s\n", indent, O_VLINE);
  
	g_printf("%s%s%s%s",
				indent,
				sibling ? O_BRANCH : (type != static_gtype_root ? O_LLEAF : O_SPACE),
				O_ESPACE,
				g_type_name (type));

	for (i = strlen (g_type_name (type)); i <= strlen (indent_inc); i++)
		g_printf("%s", O_KEY_FILL);
	g_printf("\n");

	if (children && recursion)
	{
		gchar *new_indent;
		GType   *child;

		if (sibling)
			new_indent = g_strconcat (indent, O_VLINE, indent_inc, NULL);
		else
			new_indent = g_strconcat (indent, O_SPACE, indent_inc, NULL);

		for (child = children; *child; child++)
			_g_type_show_nodes (child[0], child[1], new_indent);

		g_free (new_indent);
	}

	g_free (children);
}

void g_type_get_node_tree(void)
{
	gint i;
	
  	static_gtype_root = ~0;
	for (i = 0; i <= G_TYPE_FUNDAMENTAL_MAX; i += G_TYPE_MAKE_FUNDAMENTAL (1))
	{
		const gchar *name = g_type_name (i);
		if (name)
			_g_type_show_nodes (i, 0, "");
	}

}


static guint g_static_depth = 0;

void g_type_enter_func(const char *name)
{
	guint i;
	const char *s;
	if (g_static_depth == 1)	
		g_printf(" \n");
	for (i = 0; i < g_static_depth; i++)
		g_printf("+");
	g_printf("Entering %s\n", name);
	g_static_depth++;
}

void g_type_leave_func(const char *name)
{
	const char *s;
	guint i;
	if (g_static_depth != 0) 	
        	g_static_depth--;
	if (g_static_depth < 0)  
		g_static_depth = 0;

	for (i = 0; i < g_static_depth; i++) 
		g_printf("-");
	g_printf("Leaving  %s\n", name);
}

 

修改gobject/testgobject.c

可以看到的结果:

Entering main

+Entering test_object_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_get_type

+Entering iface_base_init
-Leaving  iface_base_init

+Entering iface_base_init
-Leaving  iface_base_init

+Entering test_object_class_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_class_init

+Entering test_object_test_iface_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_iface_init

+Entering derived_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  derived_object_get_type

+Entering iface_base_init
-Leaving  iface_base_init

+Entering derived_object_class_init
-Leaving  derived_object_class_init

+Entering derived_object_test_iface_init
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  derived_object_test_iface_init

+Entering test_object_init
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  test_object_init

+Entering derived_object_init
++Entering derived_object_get_type
--Leaving  derived_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  derived_object_init

+Entering test_object_check_private_init
++Entering test_object_get_type
--Leaving  test_object_get_type
private data during initialization: 54321 == 54321
-Leaving  test_object_check_private_init
|
`void
|
`GInterface
  |
  +GTypePlugin
  |
  `TestIface
|
`gchar
|
`guchar
|
`gboolean
|
`gint
|
`guint
|
`glong
|
`gulong
|
`gint64
|
`guint64
|
`GEnum
|
`GFlags
|
`gfloat
|
`gdouble
|
`gchararray
|
`gpointer
  |
  `GType
|
`GBoxed
  |
  `GValueArray
|
`GParam
  |
  +GParamChar
  |
  +GParamUChar
  |
  +GParamBoolean
  |
  +GParamInt
  |
  +GParamUInt
  |
  +GParamLong
  |
  +GParamULong
  |
  +GParamInt64
  |
  +GParamUInt64
  |
  +GParamUnichar
  |
  +GParamEnum
  |
  +GParamFlags
  |
  +GParamFloat
  |
  +GParamDouble
  |
  +GParamString
  |
  +GParamParam
  |
  +GParamBoxed
  |
  +GParamPointer
  |
  +GParamValueArray
  |
  +GParamObject
  |
  +GParamOverride
  |
  +GParamGType
  |
  `GParamVariant
|
`GObject
  |
  `TestObject
    |
    `DerivedObject
|
`GVariant
|
`FooShadow1
|
`FooShadow2

+Entering test_object_get_type
-Leaving  test_object_get_type

+Entering test_object_init
++Entering test_object_get_type
--Leaving  test_object_get_type
-Leaving  test_object_init
MAIN: emit test-signal:
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal

+Entering test_signal_accumulator
-Leaving  test_signal_accumulator
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal

+Entering test_signal_accumulator
-Leaving  test_signal_accumulator
TestObject-Message: ::test_signal default_handler called

+Entering test_object_test_signal
++Entering test_iface_get_type
--Leaving  test_iface_get_type
-Leaving  test_object_test_signal
TestObject-Message: signal return: "<default_handler><default_handler>"
MAIN: call iface print-string on test and derived object:

+Entering iface_print_string
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering print_foo
Iface-FOO: "iface-string-from-test-type" from 0x9370a00
--Leaving  print_foo
-Leaving  iface_print_string

+Entering iface_print_string
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering test_iface_get_type
--Leaving  test_iface_get_type
++Entering print_bar
+++Entering test_iface_get_type
---Leaving  test_iface_get_type
Iface-BAR: "iface-string-from-derived-type" from 0x936aaf0
chaining: +++Entering test_iface_get_type
---Leaving  test_iface_get_type
+++Entering print_foo
Iface-FOO: "iface-string-from-derived-type" from 0x936aaf0
---Leaving  print_foo
--Leaving  print_bar
-Leaving  iface_print_string

+Entering test_object_get_type
-Leaving  test_object_get_type
private data after initialization: 54321 == 54321

Leaving  main

gobjec相关学习文章的list.

 

 

 

GTYPE类型系统分析 zz

转载时请注明出处和作者联系方式

作者联系方式:会飞的鱼 <parker30_liu at hotmail dot com>

刚开始接触GLib库时,对GLib传统的习惯和特有的概念不熟悉,在编写基于GLib的程序时总是很不顺手,心中很是不爽。后来随着写的代码多了,渐渐熟悉了GLib的这些术语、概念、编码风格,情况好多了。但对GLib的类型系统还是不大清楚,只不过是照葫芦画瓢学的不错而已,对基于GType的使用在较深入时还是一知半解。最近有了些空余时间,于是去仔细看了GType相关的代码,总算弄得比较清楚了。记录在此,以便查阅,有不对之处,请高手指正。

Gtype提供了GLib的数据类型系统和数据类型定义,是GObject对象系统的基础。GLib定义了一些基本数据类型,并允许用户定义一些基本类型的派生类型,还可以定义动态类型,如GObject的对象系统的各种类型的对象都是GObject的派生类型。GType只支持单继承,但允许通过接口继承的方式实现变形的多继承,避免了类似C++的多继承的复杂性和多继承导致的各种问题,规范了程序员对多继承的使用。GType提供了一个基于C语言的具有完备的多层抽象结构的继承体系的良好实现,使得我们可以使用C语言来进行面向对象的程序设计和实现。

首先,从GType这个数据类型的定义开始,一步一步地进入这个复杂的GType类型系统,让我们来解开这个迷宫吧。下面的分析是基于GLib的2.12.6版本的源代码来进行的,可能和较早的版本有些出入,请大家注意。

在最新的GLib中,GType是一个ulong类型,即无符号长整型数。GType的意义是用来指向整个GLib类型系统的一个类型节点。对于基本类型,GType是该类型在基本类型中的序号乘以4(乘以4是用来和非基本类型兼容,内存地址低二位为0)。对于非基本类型,GType是该类型的类型节点的类型信息的内存地址。

基本类型最多有256个,现在GLib只使用了49个,包括GLib已经定义的基本类型、GLib保留的基本类型、BSE保留的基本类型,其余的基本类型给用户使用。下面是GLib中已经定义的基本类型。

 

 

#define G_TYPE_INVALID G_TYPE_MAKE_FUNDAMENTAL (0)

#define G_TYPE_NONE G_TYPE_MAKE_FUNDAMENTAL (1)

#define G_TYPE_INTERFACE G_TYPE_MAKE_FUNDAMENTAL (2)

#define G_TYPE_CHAR G_TYPE_MAKE_FUNDAMENTAL (3)

#define G_TYPE_UCHAR G_TYPE_MAKE_FUNDAMENTAL (4)

#define G_TYPE_BOOLEAN G_TYPE_MAKE_FUNDAMENTAL (5)

#define G_TYPE_INT G_TYPE_MAKE_FUNDAMENTAL (6)

#define G_TYPE_UINT G_TYPE_MAKE_FUNDAMENTAL (7)

#define G_TYPE_LONG G_TYPE_MAKE_FUNDAMENTAL (8)

#define G_TYPE_ULONG G_TYPE_MAKE_FUNDAMENTAL (9)

#define G_TYPE_INT64 G_TYPE_MAKE_FUNDAMENTAL (10)

#define G_TYPE_UINT64 G_TYPE_MAKE_FUNDAMENTAL (11)

#define G_TYPE_ENUM G_TYPE_MAKE_FUNDAMENTAL (12)

#define G_TYPE_FLAGS G_TYPE_MAKE_FUNDAMENTAL (13)

#define G_TYPE_FLOAT G_TYPE_MAKE_FUNDAMENTAL (14)

#define G_TYPE_DOUBLE G_TYPE_MAKE_FUNDAMENTAL (15)

#define G_TYPE_STRING G_TYPE_MAKE_FUNDAMENTAL (16)

#define G_TYPE_POINTER G_TYPE_MAKE_FUNDAMENTAL (17)

#define G_TYPE_BOXED G_TYPE_MAKE_FUNDAMENTAL (18)

#define G_TYPE_PARAM G_TYPE_MAKE_FUNDAMENTAL (19)

#define G_TYPE_OBJECT G_TYPE_MAKE_FUNDAMENTAL (20)

 

宏G_TYPE_MAKE_FUNDAMENTAL是将类型序号乘以4以得到基本类型的值。除了基本数据使用的类型如G_TYPE_CHAR等外,还有如下几个基本类型。

  • G_TYPE_INVALID

无效的GType类型,此种类型不是GType类型。

  • G_TYPE_NONE

空的GType类型,和C语言的void相当。

  • G_TYPE_BOXED

多个GType类型的数据组合在一起的得到的GType类型,可以是单一GType类型数据组成的数组,也可以是不同GType类型数据组成的异质数组。如字符串数组(G_TYPE_STRV)、GValue数组(G_TYPE_VALUE_ARRAY)、日期数据类型(G_TYPE_DATE)。此类型只能单层继承。

  • G_TYPE_PARAM

参数数据类型,用于进行函数、信号等的参数传递的GType类型。

  • G_TYPE_OBJECT

GObject对象的GType类型,是GLib对象系统的根对象类型,是一个可以实例化的类类型,可以多层继承,但只能单根继承。G_TYPE_OBJECT类型的派生类型的实例化对象在不混淆的情况下也可以称为GObject对象。

  • G_TYPE_INTERFACE

接口类型,定义了对数据或对象进行操作的接口方法,凡实现了某一个接口的所有方法的GObject对象可以称其继承了该接口。

Gtype的基本类型是可以继承的,其继承关系由类型节点确定,类型节点保存了GType类型的类型信息。所有基本类型的类型节点的指针保存在一个基本类型节点数组中,基本类型的值除以4就是该基本类型节点在基本类型节点数组中的序号。

类型节点的数据结构如下所示。

 

struct _TypeNode

{

GTypePlugin *plugin;

guint n_children : 12;

guint n_supers : 8;

guint _prot_n_ifaces_prerequisites : 9;

guint is_classed : 1;

guint is_instantiatable : 1;

guint mutatable_check_cache : 1; /* combines some common path checks */

GType *children;

TypeData * volatile data;

GQuark qname;

GData *global_gdata;

union {

IFaceEntry *iface_entries; /* for !iface types */

GType *prerequisistes;

} _prot;

GType supers[1]; /* flexible array */

};

类型节点数据结构中的supers和children确定了各个GType类型的继承关系。n_supers是所继承的所有祖先类型的个数,数组supers保存了该类型继承的所有祖先类型,n_children是所有子类型的个数,数组children保存了该类型的所有子类型。所有GType类型组成了下图所示的关系。


 

图中箭头由派生类指向基类,虚线框的节点类型是和我们的分析无关的继承关系。该图描述了无继承,单层继承,多层继承这几种继承关系。

基本类型的类型节点除了普通类型的信息外,还有基本类型的类型信息。基本类型的信息决定了该基本类型及其派生类型的如下几个特性。

  • G_TYPE_FLAG_CLASSED

该基本类型是一个类类型。

  • G_TYPE_FLAG_INSTANTIABLE

该基本类型是可以实例化的类型,即是否可以创建该类型的对象。

  • G_TYPE_DERIVABLE

该基本类型是可以单层继承的类型,只允许一层的平坦的继承。

  • G_TYPE_DEEP_DERIVABLE

该基本类型是可以多层继承的类型,可以有子类型、孙子类型,子子孙孙无穷尽也。

普通类型的信息有如下几个类型特性。

  • G_TYPE_FLAG_ABSTRACT

该类型是一个抽象类型。

  • G_TYPE_FLAG_VALUE_ABSTRACT

该类型是一个值抽象类型,是GValue用的,表明该类型的实例是一个GValue值

 

每个类型节点的祖先类型数组supers是按如下方式排列,祖先类型数组的大小是1(self) + n_supers + 1(0),n_supers最大值是255,即最大继承层次是255层。

 

自身类型

父类型

祖父类型

...

基本类型

0

 

每个类型节点的子类型按创建的次序增加到子类型数组children中,n_children最大值是4095,即最多有4095个子类型。

类型节点数据结构的qname保存了该GType类型的名字,这是一个夸克(夸克是GLib的特有术语,相当于字符串的ID)。plugin是用于动态类型的类型数据入口。

is_classed指明该类型是否是一个类,is_instantiatable指明该类型是否可以实例化。这两个成员的值从基本类型继承而来。

global_gdata是GType的一个特色,用于保存GType类型相关的附属信息,其本身是一个由QData组成的动态数组GData。每个QData是个夸克指针对,夸克保存该信息的名字,指针指向该信息的内容。可以通过函数g_type_set_qdata()设置该类型相关的附属信息,通过函数g_type_get_qdata()获取该类型相关的附属信息。这是一个十分方便和好用的功能。

 

_prot是一个联合,当该类型是一个接口时,是一个由GType类型组成的动态数组,实现这个接口的GType必须要实现该GType类型数组中的所有GType类型,该数组中一般都是接口类型,最多只有一个可实例化的类型。若不是接口,是一般的类,则是一个由接口入口IFaceEntry组成的动态数组,这个数组保存了这个类继承的所有接口。_prot_n_ifaces_prerequisites是动态数组元素的个数。

data是该GType类型的类型数据,保存了和该类型相关的接口、类或实例对象的数据。类型数据的数据结构如下所示。

 

union _TypeData

{

CommonData common;

IFaceData iface;

ClassData class;

InstanceData instance;

};

 

 

 

这是一个联合,当类型是一个接口时,为接口数据IFaceData。当类型是一个类时,为类数据ClassData。当类型是一个对象实例时,为对象数据InstanceData。当类型是其他一般的类型时,为类型公共数据CommonData。接口数据IFaceData、类数据ClassData、对象数据InstanceData都包含有公共数据CommonData。

 

 

CommonData的数据结构如下所示。

 

struct _CommonData

{

guint ref_count;

GTypeValueTable *value_table;

};

ref_count是引用计数,是大部分面向对象系统用来保证对象能够在复杂的环境下正确的创建、引用、销毁的一种技术。

value_table是该gtype类型的值处理函数表,如值的初始化函数、值的释放函数、值拷贝函数等,这是同类型的gtype实例之间实现值拷贝的基础。下面是GTypeValueTable的数据结构。

 

struct _GTypeValueTable

{

void (*value_init) (GValue *value);

void (*value_free) (GValue *value);

void (*value_copy) (const GValue *src_value,

GValue *dest_value);

/* varargs functionality (optional) */

gpointer (*value_peek_pointer) (const GValue *value);

gchar *collect_format;

gchar* (*collect_value) (GValue *value,

guint n_collect_values,

GTypeCValue *collect_values,

guint collect_flags);

gchar *lcopy_format;

gchar* (*lcopy_value) (const GValue *value,

guint n_collect_values,

GTypeCValue *collect_values,

guint collect_flags);

};