gcc常见选项(zz)

前三章:
要点
 
1。强烈推荐使用-Wall选项;
注:Warning 的显示格式: file:line-number:message
 
2。编译选项
-o 文件名:直接产生可执行文件
-c 文件名:只编译为.o的库文件,不链接 (.c => .o )
 
在将多个.o连接为一个可执行文件时,仅使用-o指定可执行文件名即可,不需要使用
-Wall选项,因为链接是一个明确的过程(unambiguous),只有successed或者fail
两种结果。
 
incidentally [InsI5dentElI] adv. 附带地, 顺便提及(学单词一个:)
 
分两步处理:
每个.c文件编译为.o,然后通过linker连接为一个可执行文件;
这样每次只编译修改过的.c文件,可以缩短整体编译时间:
An object file contains machine code where any references to the memory addresses of functions(orvariables)in other files are left undefined.This allows source files to be compiled without direct reference to each other.The linker fills in these missing addresses when it produces the executable.
 
3。连接解析顺序:
在类unix的系统中,编译器和链接器是通过在命令行中从左向右的顺序检索各个文件。这就意味着:
含有函数定义的.o文件要放在那些调用该函数的.o文件的右边。
This means that the object file which contains the definition of a function should appear after any files which call that function.
 
4。库的连接
A library is a collection of precompiled object files which can be linked into programs. Libraries are typically stored in special archive files with the extension‘.a’, referred to as static libraries.They are created from object files with a separate tool, the GNU archiver ar, and used by the linker to resolve references to functions at compile-time.
 
-l参数:指定欲链接的库名称
比如我们相连接标准库目录(/usr/lib, /lib)下的数学库libm.a,那么我们可以这样指定:
-lm (具体见下面的解释)
 
In general, the compiler option ‘-lNAME’ will attempt to link object
files with a library file ‘libNAME.a’ in the standard library directories.
Additional directories can specified with command-line options and environment
variables, to be discussed shortly. A large program will typically
use many ‘-l’ options to link libraries such as the math library, graphics
libraries and networking libraries.
 
5。其他的一些编译选项
5.1 -I,-L
假如程序中使用的头文件路经和链接时使用的.a路径不是缺省的系统路径(如下)
By default, gcc searches the following directories for header files:
/usr/local/include/
/usr/include/
and the following directories for libraries:
/usr/local/lib/
/usr/lib/
 
则,需要利用 -I 来指定头文件的include路径
需要利用 -L 来指定.a文件的库文件路径
(注:不要在文件include中使用绝对路径来避免这个问题,因为这样对移植后的编译不利)
 
同时,也可以利用环境变量来解决搜索路径的问题(Environment variables)
对于include 问题:
Additional directories can be added to the include path using the environment
variable C_INCLUDE_PATH (for C header files) or CPLUS_INCLUDE_PATH (for C++ header files).
举例:
$ C_INCLUDE_PATH=/opt/gdbm-1.8.3/include
$ export C_INCLUDE_PATH
这样这个头文件路经就被添加到了环境变量中了(利用export使得其他模块也可见该变量)
 
同样的添加库文件路经:
additional directories can be added to the link path using the environment variable LIBRARY_PATH.
举例:
$ LIBRARY_PATH=/opt/gdbm-1.8.3/lib
$ export LIBRARY_PATH
 
那么,想指定多个路径可以采用下面的格式:
dir1:dir2:dir3:......
其中每一个单独的路径用 : 隔开;
一个点 . 表示当前路径
举例:
$ C_INCLUDE_PATH=.:/opt/gdbm-1.8.3/include:/net/include (含三个路径)
$ LIBRARY_PATH=.:/opt/gdbm-1.8.3/lib:/net/lib (含三个路径)
5.2 静态库和共享库(static libraries and shared libraries.)
库有两种方式存在,
Static libraries are the ‘.a’ files seen earlier.
Shared libraries are handled with a more advanced form of linking, which makes the executable file smaller. They use the extension ‘.so’,which stands for shared object.
 
[ 短语:in preference to 优先于... ]
 
.so的文件是动态链接并执行的。
当我们指定一个路径下的库文件名时,假如此时同时存在xxx.a和xxx.so的两个库形式,那么
优先选择.so链接。(共享库优先
 
当执行函数动态链接.so时,如果此文件不在缺省目录下‘/usr/local/lib’ and ‘/usr/lib’.
那么就需要指定环境变量LD_LIBRARY_PATH (方式和上面include,lib path相同)
 
假如现在需要在已有的环境变量上添加新的路径名,则采用如下方式:
LD_LIBRARY_PATH=NEWDIRS:$LD_LIBRARY_PATH.(newdirs是新的路径串)
 
(注:gnu系统可以自动添加在 /etc/ld.so.conf文件中来实现环境变量的设置)
 
-static
使用该选项可以强制编译器生成静态库文件
 
6. C语言标准
-ansi
the compiler option‘-ansi’ disables those GNU extensions which conflict with the ANSI/ISO standard.
 
-pedantic
The command-line option ‘-pedantic’ in combination with ‘-ansi’ will
cause gcc to reject all GNU C extensions, not just those that are incompatible
with the ANSI/ISO standard. This helps you to write portable
programs which follow the ANSI/ISO standard.
 
-std
The specific language standard used by GCC can be controlled with the‘-std’ option.
包含有:
‘-std=c89’ or ‘-std=iso9899:1990’-- The original ANSI/ISO C language standard
‘-std=iso9899:199409’-- The ISO C language standard with ISO Amendment 1, published
in 1994.
‘-std=c99’ or ‘-std=iso9899:1999’-- The revised ISO C language standard,
published in 1999 (ISO/IEC9899:1999).
‘-std=gnu89’ and ‘-std=gnu99’. -- for GNU extensions
 
 
7.关于warning -Wall的具体选项
以下的一些编译选项都是包含在Wall中的一些具体项目,可以单独使用。
‘-Wcomment’ This option warns about nested comments.
‘-Wformat’ This option warns about the incorrect use of format strings in functions
such as printf and scanf, where the format specifier does not agree with the
type of the corresponding function argument.
‘-Wunused’ This option warns about unused variables.
‘-Wimplicit’ This option warns about any functions that are used without being declared.
‘-Wreturn-type’ This option warns about functions that are defined without a return
type but not declared void. It also catches empty return statements in
functions that are not declared void.
 
8. 其他的一些warn编译选项(不包含在Wall中的)
‘-W’ This is a general option similar to ‘-Wall’ which warns about a selection of
common programming errors, such as functions which can return without a value
(also known as “falling off the end of the function body”), and comparisons
一般情况下,-W 和 -Wall同时使用
‘-Wconversion’ This option warns about implicit type conversions that could cause
unexpected results.
‘-Wshadow’ This option warns about the redeclaration of a variable name in a scope where
it has already been declared.
‘-Wcast-qual’ This option warns about pointers that are cast to remove a type qualifier,
such as const.
‘-Wwrite-strings’ This option implicitly gives all string constants defined in the program
a const qualifier, causing a compile-time warning if there is an attempt
to overwrite them.
‘-Wtraditional’ This option warns about parts of the code which would be interpreted
differently by an ANSI/ISO compiler and a “traditional” pre-ANSI
compiler.(5)
上面的这些warn选项都仅仅产生warn,而不会停止编译过程,下面的选项可以将warn视为error并停止编译
‘-Werror’ changes the default behavior by converting warnings into errors, stopping
the compilation whenever a warning occurs.
between signed and unsigned values.


一般情况下,-W 和 -Wall同时使用
‘-Wconversion’ This option warns about implicit type conversions that could cause
unexpected results.
‘-Wshadow’ This option warns about the redeclaration of a variable name in a scope where
it has already been declared.
‘-Wcast-qual’ This option warns about pointers that are cast to remove a type qualifier,
such as const.
‘-Wwrite-strings’ This option implicitly gives all string constants defined in the program
a const qualifier, causing a compile-time warning if there is an attempt
to overwrite them.
‘-Wtraditional’ This option warns about parts of the code which would be interpreted
differently by an ANSI/ISO compiler and a “traditional” pre-ANSI
compiler.(5)
上面的这些warn选项都仅仅产生warn,而不会停止编译过程,下面的选项可以将warn视为error并停止编译
‘-Werror’ changes the default behavior by converting warnings into errors, stopping
the compilation whenever a warning occurs.
 
 
第四章:使用预处理器 ( Using the preprocessor )
GCC的预处理器 cpp属于GCC包的一部分;在原文件被编译之前,GCC会使用cpp将所有的宏扩展。
 
4.1定义宏(macro)
有两种定义的宏的方式:1:其他原文件中;2:在GCC的命令行中使用 -Dxxx
当这些宏被定义后(#define xxx)
在系统中使用时:#ifdef xxx 就会被预处理器扩展为有效代码;
 
在系统中已经定义了一些系统命名空间内的宏,都是以 __ 开头的(两条下划线)
使用命令:$ cpp -dM /dev/null 可以查看到所有的预定义宏
 
(注:在这些宏中,有一些是GCC系统级的宏,它们都不是以__开头,这些非标准的宏可以使用
GCC的编译选项 -ansi 使其无效)
(其实,利用-Dxxx来定义xxx,就是将xxx赋值为1 )
 
4.2 定义有值宏
也是利用 -Dxxx = value的方式来定义
 
假如利用-DNAME="" (空)来定义一个宏,则这个宏也被视为被定义的,但是如果按值展开的话,
则为空;
 
4.3 预处理源文件
 
The ‘-E’ option causes gcc to run the preprocessor, display the expanded
output, and then exit without compiling the resulting source code.
可以利用 -E选项来查看预处理器的工作过程
 
可以利用 -save-temps选项来保存预处理器的处理过程日志到临时文件xxxx.i;
 
The preprocessed system header files usually generate a lot of output.
This can be redirected to a file, or saved more conveniently using the gcc
‘-save-temps’ option:
$ gcc -c -save-temps hello.c
After running this command, the preprocessed output will be available
in the file ‘hello.i’. The ‘-save-temps’ option also saves ‘.s’ assembly
files and ‘.o’ object files in addition to preprocessed ‘.i’ files.
 
第五章:Compiling for debugging
一个可执行文件中并不保存任何与其源文件中相关的函数以及变量信息在其中,可执行文件
只是机器码的集合。
这样的话,如果系统运行过程中出现错误,我们也不能定位错误。
利用 -g 编译选项,可以将这些信息保存在执行文件中,使得我们可以利用gdb工具调试;
保存的信息有:函数名,变量名(及其所有引用)以及它们的相应行号,这些信息都保存
在叫做符号表的东东中;
 
5.1 Core文件
When a program exits abnormally the operating system can write out a core file,
usually named ‘core’, which contains the in-memory state of the program at the
time it crashed.
 
当我们运行程序发生错误时,假如提示信息中包含‘core dumped’错误信息时,操作系统
已经产生了一个叫做core的文件到当前目录(或者/var/coredumps/下)
 
(注:In the GNU Bash shell the command ulimit -c controls the maximum
size of core files. If the size limit is zero, no core files are produced.
命令$ ulimit -c unlimited 使得任何大小的core文件都可以生成,但是这些设置只在
当前shell有效)
 
使用命令 $ gdb EXECUTABLE-FILE CORE-FILE 来分析
 
 
第六章:优化编译
 
6.1 Source-level optimization 代码级优化
在此只介绍两种:common subexpression eliminationfunction inlining.
 
Common subexpression elimination:(CSE)
只要优化开关打开,这种优化都会进行,进行这种优化既可以提高速度,又可以减少
代码大小,例如:
x = cos(v)*(1+sin(u/2)) + sin(w)*(1-sin(u/2))
可以写成:
t = sin(u/2)
x = cos(v)*(1+t) + sin(w)*(1-t)
(利用中间变量减少运算次数)
 
function inlining:
 
6.2 Speed-space tradeoffs 速度-空间权衡
 
6.2.1 Loop unrolling
例如:
for (i = 0; i < 8; i++)
{
y[i] = i;
}
很多的时间花费在判断上,可以改写为:
y[0] = 0;
y[1] = 1;
y[2] = 2;
y[3] = 3;
y[4] = 4;
y[5] = 5;
y[6] = 6;
y[7] = 7;
这样可以达到最快的执行速度(没有判断语句,同时可以利用流水线),但是空间也增大了
另外一例:
for (i = 0; i < n; i++)
{
y[i] = i;
}
可以重新写成:
for (i = 0; i < (n % 2); i++)
{
y[i] = i;
}
for ( ; i + 1 < n; i += 2) /* no initializer */
{
y[i] = i;
y[i+1] = i+1;
}
The first loop handles the case i = 0 when n is odd, and the second loop
handles all the remaining iterations. Note that the second loop does
not use an initializer in the first argument of the for statement, since
it continues where the first loop finishes. The assignments in the second
loop can be parallelized, and the overall number of tests is reduced by a
factor of 2 (approximately). Higher factors can be achieved by unrolling
more assignments inside the loop, at the cost of greater code size.
 
6.3 scheduling
When scheduling is enabled, instructions must be arranged so that
their results become available to later instructions at the right time, and to
allow for maximum parallel execution. Scheduling improves the speed of
an executable without increasing its size, but requires additional memory
and time in the compilation process itself (due to its complexity).
 
6.4 优化等级
优化等级分为:0 - 3
编译选项为: -Olevel
各等级含义如下:
 
‘-O0’ or no ‘-O’ option (default)不优化
At this optimization level GCC does not perform any optimization
and compiles the source code in the most straightforward way
possible. Each command in the source code is converted directly
to the corresponding instructions in the executable file, without
rearrangement. This is the best option to use when debugging a
program
.
The option ‘-O0’ is equivalent to not specifying a ‘-O’ option.
‘-O1’ or ‘-O’
This level turns on the most common forms of optimization that
do not require any speed-space tradeoffs. With this option the
resulting executables should be smaller and faster than with ‘-O0’.
The more expensive optimizations, such as instruction scheduling,
are not used at this level.
Compiling with the option ‘-O1’ can often take less time than compiling
with ‘-O0’, due to the reduced amounts of data that need to
be processed after simple optimizations.
‘-O2’ This option turns on further optimizations, in addition to those
used by ‘-O1’. These additional optimizations include instruction
scheduling
. Only optimizations that do not require any speed-space
tradeoffs are used, so the executable should not increase in size. The
compiler will take longer to compile programs and require more
memory than with ‘-O1’. This option is generally the best choice
for deployment of a program, because it provides maximum optimization
without increasing the executable size. It is the default
optimization level for releases of GNU packages.
‘-O3’ This option turns on more expensive optimizations, such as function
inlining, in addition to all the optimizations of the lower levels
‘-O2’ and ‘-O1’. The ‘-O3’ optimization level may increase the speed
of the resulting executable, but can also increase its size. Under
some circumstances where these optimizations are not favorable,
this option might actually make a program slower.
‘-funroll-loops’
This option turns on loop-unrolling, and is independent of the other
optimization options. It will increase the size of an executable.
Whether or not this option produces a beneficial result has to be
examined on a case-by-case basis.
‘-Os’ This option selects optimizations which reduce the size of an executable.
The aim of this option is to produce the smallest possible
executable, for systems constrained by memory or disk space. In
some cases a smaller executable will also run faster, due to better
cache usage.
 
注:For most purposes it is satisfactory to use ‘-O0’ for debugging, and
‘-O2’ for development and deployment.
 
“optimizations may not necessarily make a program faster in every case.”
 
第七章:编译C++程序
GCC是一个真正的C++编译器,其直接将C++代码编译为汇编代码。
 
1。利用g++而不是gcc命令;
(注:C++ source code should be given one of the valid C++ file extensions ‘.cc’,
‘.cpp’, ‘.cxx’ or ‘.C’ rather than the ‘.c’ extension used for C programs.)
 
2。C++标准库模板
The C++ standard library ‘libstdc++’ supplied with GCC provides a wide
range of generic container classes such as lists and queues, in addition to
generic algorithms such as sorting。
 
3。Explicit template instantiation
To achieve complete control over the compilation of templates with g++
it is possible to require explicit instantiation of each occurrence of a template,
using the option ‘-fno-implicit-templates’.
 
第八章:平台相关的选项
利用 -m 选项来确定不同的开发平台
 
 
获得GCC的帮助文档:
$ gcc -v --help
$ gcc -v --help 2>&1 | more
获得GCC的版本号:
$ gcc --version
(注:The ‘-v’ option can also be used to display detailed information about the
exact sequence of commands used to compile and link a program)
 
第十章:编译器相关的工具
 
1。 ar工具 Creating a library with the GNU archiver
The GNU archiver ar combines a collection of object files into a single
archive file, also known as a library.
ar工具可以将要发布的多个.o文件组装成一个.a的库文件
例如:将hello_fn.o和bye_fn.o生成 libhello.a的命令行:
$ ar cr libhello.a hello_fn.o bye_fn.o
其中The option ‘cr’ stands for “create and replace”.
 
利用 选项 t 可以查看.a文件中所包含的所有.o的信息
$ ar t libhello.a
hello_fn.o
bye_fn.o
 
2。程序性能测试工具-gprof
To use profiling, the program must be compiled and linked with the ‘-pg’
profiling option:
$ gcc -Wall -c -pg collatz.c
$ gcc -Wall -pg collatz.o
编译和链接的时候添加选项 -pg ,才能使用gprof测试程序性能
 
然后运行编译通过的程序,才能产生gprof需要的文件 gmon.out(在当前目录下)
然后执行:
$ gprof a.out (假设可执行文件是缺省生成的)
 
3。程序覆盖测试工具- gcov
The GNU coverage testing tool gcov analyses the number of times each
line of a program is executed during a run. This makes it possible to
find areas of the code which are not used, or which are not exercised
in testing.
When combined with profiling information from gprof the
information from coverage testing allows efforts to speed up a program to
be concentrated on specific lines of the source code.
 
编译和链接必须使用相关选项,才可以使用gcov工具。
例如:
$ gcc -Wall -fprofile-arcs -ftest-coverage cov.c
其中
‘-ftest-coverage’ adds instructions for counting the number of times
individual lines are executed,
‘-fprofile-arcs’ incorporates instrumentation code for each branch of
the program. Branch instrumentation records how frequently
different paths are taken through‘if’ statements and
other conditionals.
运行通过编译的程序,产生供gcov使用的文件:
分别带有后缀:‘.bb’‘.bbg’ and ‘.da’在当前目录下
然后运行
$ gcov cov.c (注意:是源代码文件
这样会产生一个带有标注的源文件的副本文件,后缀为 .gcov
在该文件中,标注了每一行代码的执行次数,标注为‘######’的语句为
未被执行到的语句。
可以通过命令:
grep ’######’ *.gcov 查找到所有未被执行到的语句
 
第十一章:编译器工作过程
 
Compilation is a multi-stage process involving several
tools, including the GNU Compiler itself (through the gcc or g++ frontends),
the GNU Assembler as, and the GNU Linker ld. The complete
set of tools used in the compilation process is referred to as a toolchain.
 
toolchain的定义见此:)
 
调用GCC的内部过程如下:
• preprocessing (to expand macros) 预处理
• compilation (from source code to assembly language) 编译
• assembly (from assembly language to machine code) 汇编
• linking (to create the final executable) 链接
 
详细描述如下:
1。preprocessing:
举例:第一步GCC会调用如下命令
 
$ cpp hello.c > hello.i
 
生成中间文件 hello.i(如果是C++文件的话,就是 hello.ii)
这种中间文件不会保存到磁盘上的,除非添加了编译选项-save-temps
 
2。compilation:
第二步:调用gcc(或者g++)生成汇编文件而不是.o文件(使用了-S选项)
 
$ gcc -Wall -S hello.i
 
这样就生成了基于x86 CPU的汇编文件 hello.s
 
3.assembly:
The purpose of the assembler is to convert assembly language into machine
code and generate an object file. When there are calls to external
functions in the assembly source file, the assembler leaves the addresses
of the external functions undefined, to be filled in later by the linker.
第三步调用
 
$ as hello.s -o hello.o
 
4。linking:
The final stage of compilation is the linking of object files to create an
executable. In practice, an executable requires many external functions
from system and C run-time (crt) libraries. Consequently, the actual link
commands used internally by GCC are complicated.
 
第四步调用
$ gcc hello.o
 
其实在GCC内部的这一步调用如下:
$ ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o
/usr/lib/crti.o /usr/lib/gcc-lib/i686/3.3.1/crtbegin.o
-L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lgcc -lgcc_eh
-lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i686/3.3.1/crtend.o
/usr/lib/crtn.o(非常复杂)
 
第十二章:测试编译后文件的工具
 
1。Identifying files 鉴别文件 - file 命令
举例:
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386,
version 1 (SYSV), dynamically linked (uses shared
libs), not stripped
 
最后一个not stripped表示该文件含符号表(可以利用命令strip去除符号表
 
2。查看符号表工具 - nm
举例:

vides maximum optimization
without increasing the executable size. It is the default
optimization level for releases of GNU packages.

‘-O3’ This option turns on more expensive optimizations, such as function
inlining, in addition to all the optimizations of the lower levels
‘-O2’ and ‘-O1’. The ‘-O3’ optimization level may increase the speed
of the resulting executable, but can also increase its size. Under
some circumstances where these optimizations are not favorable,
this option might actually make a program slower.
‘-funroll-loops’
This option turns on loop-unrolling, and is independent of the other
optimization options. It will increase the size of an executable.
Whether or not this option produces a beneficial result has to be
examined on a case-by-case basis.
‘-Os’ This option selects optimizations which reduce the size of an executable.
The aim of this option is to produce the smallest possible
executable, for systems constrained by memory or disk space. In
some cases a smaller executable will also run faster, due to better
cache usage.
 
注:For most purposes it is satisfactory to use ‘-O0’ for debugging, and
‘-O2’ for development and deployment.
 
“optimizations may not necessarily make a program faster in every case.”
 
第七章:编译C++程序
GCC是一个真正的C++编译器,其直接将C++代码编译为汇编代码。
 
1。利用g++而不是gcc命令;
(注:C++ source code should be given one of the valid C++ file extensions ‘.cc’,
‘.cpp’, ‘.cxx’ or ‘.C’ rather than the ‘.c’ extension used for C programs.)
 
2。C++标准库模板
The C++ standard library ‘libstdc++’ supplied with GCC provides a wide
range of generic container classes such as lists and queues, in addition to
generic algorithms such as sorting。
 
3。Explicit template instantiation
To achieve complete control over the compilation of templates with g++
it is possible to require explicit instantiation of each occurrence of a template,
using the option ‘-fno-implicit-templates’.
 
第八章:平台相关的选项
利用 -m 选项来确定不同的开发平台
 
 
获得GCC的帮助文档:
$ gcc -v --help
$ gcc -v --help 2>&1 | more
获得GCC的版本号:
$ gcc --version
(注:The ‘-v’ option can also be used to display detailed information about the
exact sequence of commands used to compile and link a program)
 
第十章:编译器相关的工具
 
1。 ar工具 Creating a library with the GNU archiver
The GNU archiver ar combines a collection of object files into a single
archive file, also known as a library.
ar工具可以将要发布的多个.o文件组装成一个.a的库文件
例如:将hello_fn.o和bye_fn.o生成 libhello.a的命令行:
$ ar cr libhello.a hello_fn.o bye_fn.o
其中The option ‘cr’ stands for “create and replace”.
 
利用 选项 t 可以查看.a文件中所包含的所有.o的信息
$ ar t libhello.a
hello_fn.o
bye_fn.o
 
2。程序性能测试工具-gprof
To use profiling, the program must be compiled and linked with the ‘-pg’
profiling option:
$ gcc -Wall -c -pg collatz.c
$ gcc -Wall -pg collatz.o
编译和链接的时候添加选项 -pg ,才能使用gprof测试程序性能
 
然后运行编译通过的程序,才能产生gprof需要的文件 gmon.out(在当前目录下)
然后执行:
$ gprof a.out (假设可执行文件是缺省生成的)
 
3。程序覆盖测试工具- gcov
The GNU coverage testing tool gcov analyses the number of times each
line of a program is executed during a run. This makes it possible to
find areas of the code which are not used, or which are not exercised
in testing.
When combined with profiling information from gprof the
information from coverage testing allows efforts to speed up a program to
be concentrated on specific lines of the source code.
 
编译和链接必须使用相关选项,才可以使用gcov工具。
例如:
$ gcc -Wall -fprofile-arcs -ftest-coverage cov.c
其中
‘-ftest-coverage’ adds instructions for counting the number of times
individual lines are executed,
‘-fprofile-arcs’ incorporates instrumentation code for each branch of
the program. Branch instrumentation records how frequently
different paths are taken through‘if’ statements and
other conditionals.
运行通过编译的程序,产生供gcov使用的文件:
分别带有后缀:‘.bb’‘.bbg’ and ‘.da’在当前目录下
然后运行
$ gcov cov.c (注意:是源代码文件
这样会产生一个带有标注的源文件的副本文件,后缀为 .gcov
在该文件中,标注了每一行代码的执行次数,标注为‘######’的语句为
未被执行到的语句。
可以通过命令:
grep ’######’ *.gcov 查找到所有未被执行到的语句
 
第十一章:编译器工作过程
 
Compilation is a multi-stage process involving several
tools, including the GNU Compiler itself (through the gcc or g++ frontends),
the GNU Assembler as, and the GNU Linker ld. The complete
set of tools used in the compilation process is referred to as a toolchain.
 
toolchain的定义见此:)
 
调用GCC的内部过程如下:
• preprocessing (to expand macros) 预处理
• compilation (from source code to assembly language) 编译
• assembly (from assembly language to machine code) 汇编
• linking (to create the final executable) 链接
 
详细描述如下:
1。preprocessing:
举例:第一步GCC会调用如下命令
 
$ cpp hello.c > hello.i
 
生成中间文件 hello.i(如果是C++文件的话,就是 hello.ii)
这种中间文件不会保存到磁盘上的,除非添加了编译选项-save-temps
 
2。compilation:
第二步:调用gcc(或者g++)生成汇编文件而不是.o文件(使用了-S选项)
 
$ gcc -Wall -S hello.i
 
这样就生成了基于x86 CPU的汇编文件 hello.s
 
3.assembly:
The purpose of the assembler is to convert assembly language into machine
code and generate an object file. When there are calls to external
functions in the assembly source file, the assembler leaves the addresses
of the external functions undefined, to be filled in later by the linker.
第三步调用
 
$ as hello.s -o hello.o
 
4。linking:
The final stage of compilation is the linking of object files to create an
executable. In practice, an executable requires many external functions
from system and C run-time (crt) libraries. Consequently, the actual link
commands used internally by GCC are complicated.
 
第四步调用
$ gcc hello.o
 
其实在GCC内部的这一步调用如下:
$ ld -dynamic-linker /lib/ld-linux.so.2 /usr/lib/crt1.o
/usr/lib/crti.o /usr/lib/gcc-lib/i686/3.3.1/crtbegin.o
-L/usr/lib/gcc-lib/i686/3.3.1 hello.o -lgcc -lgcc_eh
-lc -lgcc -lgcc_eh /usr/lib/gcc-lib/i686/3.3.1/crtend.o
/usr/lib/crtn.o(非常复杂)
 
第十二章:测试编译后文件的工具
 
1。Identifying files 鉴别文件 - file 命令
举例:
$ file a.out
a.out: ELF 32-bit LSB executable, Intel 80386,
version 1 (SYSV), dynamically linked (uses shared
libs), not stripped
 
最后一个not stripped表示该文件含符号表(可以利用命令strip去除符号表
 
2。查看符号表工具 - nm
举例:
$ nm a.out
08048334 t Letext
08049498 ? _DYNAMIC
08049570 ? _GLOBAL_OFFSET_TABLE_
........
080483f0 T main
08049590 b object.11
0804948c d p.3
U printf@GLIBC_2.0
 
其中: T表示该函数在此文件中有定义
U表示未定义的函数(需要link的外部函数)
所以,nm最常用的地方在于,查看这个文件中是否包含某函数的定义
 
3。寻找所需的动态链接库工具 - ldd
 
例如:
$ gcc -Wall hello.c
$ ldd a.out
libc.so.6 => /lib/libc.so.6 (0x40020000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
 
给命令同样可以用于动态链接库本身,来查找其所需要的链接库
 

 

使用Gnu gprof进行Linux平台下的程序分析 (转)

Gprof 简介:

Gprof功能:打印出程序运行中各个函数消耗的时间,可以帮助程序员找出众多函数中耗时最多的函数。产生程序运行时候的函数调用关系,包括调用次数,可以帮助程序员分析程序的运行流程。

有了函数的调用关系,这会让开发人员大大提高工作效率,不用费心地去一点点找出程序的运行流程,这对小程序来说可能效果不是很明显,但对于有几万,几十万代码量的工程来说,效率是毋庸置疑的!而且这个功能对于维护旧代码或者是分析Open Source来说那是相当诱人的,有了调用图,对程序的运行框架也就有了一个大体了解,知道了程序的“骨架“,分析它也就不会再那么茫然,尤其是对自己不熟悉的代码和Open Source。费话不多说了,让我们开始我们的分析之旅吧!

Gprof 实现原理:

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

Gprof基本用法:

1. 使用 -pg 编译和链接你的应用程序。

2. 执行你的应用程序使之生成供gprof 分析的数据。

3. 使用gprof 程序分析你的应用程序生成的数据。

Gprof 简单使用:

让我们简单的举个例子来看看Gprof是如何使用的。

1.打开linux终端。新建一个test.c文件,并生用-pg 编译和链接该文件。 test.c 文件内容如下:

 

#include "stdio.h"

#include "stdlib.h"

void a(){

  printf("\t\t+---call a() function\n");

}

void c(){

  printf("\t\t+---call c() function\n");

}

int b(){

printf("\t+--- call b() function\n");

a();

c();

return 0;

}

int main(){

printf(" main() function()\n");

b();

}



命令行里面输入下面命令,没加-c选项,gcc 会默认进行编译并链接生成a.out:

[linux /home/test]$gcc -pg test.c



如果没有编译错误,gcc会在当前目录下生成一个a.out文件,当然你也可以使用 –o 选项给生成的文件起一个别的名字,像 gcc –pg test.c –o test , 则gcc会生成一个名为test的可执行文件,在命令行下输入[linux /home/test]$./test , 就可以执行该程序了,记住一定要加上 ./ 否则程序看上去可能是执行,可是什么输出都没有。

 

2.执行你的应用程序使之生成供gprof 分析的数据。 命令行里面输入:

 

[linux /home/test]$a.out

main() function()

+--- call b() function

+---call a() function

+---call c() function

[linux /home/test]$

你会在当前目录下看到一个gmon.out 文件, 这个文件就是供gprof 分析使用的。

 

3.使用gprof 程序分析你的应用程序生成的数据。

命令行里面输入:

 

[linux /home/test]$ gprof -b a.out gmon.out | less

由于gprof输出的信息比较多,这里使用了 less 命令,该命令可以让我们通过上下方向建查看gprof产生的输出,| 表示gprof -b a.out gmon.out 的输出作为 less的输入。下面是我从gprof输出中摘抄出的与我们有关的一些详细信息。


				

% cumulative self self total

time seconds seconds calls Ts/call Ts/call name

0.00 0.00 0.00 1 0.00 0.00 a

0.00 0.00 0.00 1 0.00 0.00 b

0.00 0.00 0.00 1 0.00 0.00 c

Call graph

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

index % time self children called name

0.00 0.00 1/1 b [2]

[1] 0.0 0.00 0.00 1 a [1]

-----------------------------------------------

0.00 0.00 1/1 main [10]

[2] 0.0 0.00 0.00 1 b [2]

0.00 0.00 1/1 a [1]

0.00 0.00 1/1 c [3]

-----------------------------------------------

0.00 0.00 1/1 b [2]

[3] 0.0 0.00 0.00 1 c [3]

 

从上面的输出我们能明显的看出来,main 调用了 b 函数, 而b 函数分别调用了a 和 c 函数。由于我们的函数只是简单的输出了一个字串,故每个函数的消耗时间都是0 秒。

gprof产生的信息解释如下:

 

 

使用Gnu gprof进行Linux平台下的程序分析(图一)
gprof产生的信息解释

 

常用的Gprof 命令选项解释:

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

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

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

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

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

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

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

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

到这为止你可能对gprof 有了一个比较感性的认识了,你可能会问如何用它去分析一个真正的Open Source 呢!下面就让我们去用gprof去分析一个Open Source,看看如何去在真实的环境中使用它。

使用Gprof 分析 Cflow开源项目

CFlow 是程序流程分析工具,该工具可以通过分析C源代码,产生程序调用图!有点跟Gprof差不多,不过CFlow是通过源代码进行的静态分析并且 不能分析C++ 程序,你可以到http://www.gnu.org/software/cflow/去下载源代码。

假设你已经下载了该源代码(cflow-1.1.tar.gz),并把它放置在/home目录下,让我们看看如何在这个应用上使用gprof。

1. 使用 -pg 编译和链接该应用程序,请输入下列命令。

 

  [linux /home/]tar zxvf cflow-1.1.tar.gz

[linux /home/cflow-1.1/src]$./configure

[linux /home]$make CFLAGS=-pg LDFLAGS=-pg 

如果没有出错你会在/home/cflow-1.1/src 目录下发行一个名为cflow的可执行文件,这就是我们加入-pg编译选项后编译出来的可以产生供gprof提取信息的可执行文件。记住一定要在编译和链接的时候都使用-pg选项,否则可能不会产生gmon.out文件。对于cflow项目,CFLAGS=-pg 是设置它的编译选项,LDFLAGS=-pg是设置它的链接选项。当然你也可以直接修改它的Makefile来达到上述相同的目的,不过一定要记住编译和链接都要使用-pg选项。

 

2. 运行cflow 程序使之生成gmon.out 文件供gprof使用。

 

[linux /home/cflow-1.1/src]$cflow parser.c

查看/home/cflow-1.1/src目录下有没有产生gmon.out文件,如果没有请重复第一步,并确认你已经在编译和链接程序的时候使用了-pg 选项。Cflow的使用请参考http://www.gnu.org/software/cflow/manual/cflow.html。

 

3. 使用gprof分析程序

[linux /home/cflow-1.1/src]$gprof -b cflow gmon.out | less

恭喜你,不出意外你会在屏幕上看到gprof的输出,函数消耗时间和函数调用图,下面是我从我的输出中摘抄出来的一小段。

 


				

% cumulative self self total

time seconds seconds calls Ts/call Ts/call name

0.00 0.00 0.00 118262 0.00 0.00 include_symbol

0.00 0.00 0.00 92896 0.00 0.00 is_printable

0.00 0.00 0.00 28704 0.00 0.00 set_level_mark

0.00 0.00 0.00 28703 0.00 0.00 is_last

0.00 0.00 0.00 19615 0.00 0.00 auto_processor

0.00 0.00 0.00 15494 0.00 0.00 gnu_output_handler

0.00 0.00 0.00 12286 0.00 0.00 delete_parm_processor

0.00 0.00 0.00 7728 0.00 0.00 newline

0.00 0.00 0.00 7728 0.00 0.00 print_function_name

0.00 0.00 0.00 7728 0.00 0.00 print_level

。。。。。。

。。。。。。

Call graph

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

index % time self children called name

[1] 0.0 0.00 0.00 79+855 [1]

0.00 0.00 166 dcl [52]

0.00 0.00 163 parse_dcl [53]

0.00 0.00 150 dirdcl [56]

0.00 0.00 129 parse_declaration [63]

0.00 0.00 98 parse_variable_declaration [66]

0.00 0.00 63 maybe_parm_list [69]

0.00 0.00 63 parse_function_declaration [70]

0.00 0.00 39 func_body [74]

。。。。。。

。。。。。。

 

通过分析%time你就知道了那个函数消耗的时间最多,你可以根据这个输出信息做有目的的优化,不过cflow执行的速度是在是太快了,以至%time都是0 (消耗时间是以秒为单位进行统计的)。

生成图形化的函数调用图

1.Graphviz 工具

看到这里你也可能觉得上面的函数调用图实在是不方便察看,也看不出来一个程序调用的整体框架。没有关系,我再介绍一个有用的工具给你,使用 Graphviz,Graphviz or Graph Visualization 是由 AT&T 开发的一个开源的图形可视化工具。它提供了多种画图能力,但是我们重点关注的是它使用 Dot 语言直连图的能力。在这里,将简单介绍如何使用 Dot 来创建一个图形,并展示如何将分析数据转换成 Graphviz 可以使用的规范, Dot 使用的图形规范。

使用 Dot 语言,你可以指定三种对象:图、节点和边。为了让你理解这些对象的含义,我将构建一个例子来展示这些元素的用法。

下图给出了一个简单的定向图(directed graph),其中包含 3 个节点。第一行声明这个图为 G,并且声明了该图的类型(digraph)。接下来的三行代码用于创建该图的节点,这些节点分别名为 node1、node2 和 node3。节点是在它们的名字出现在图规范中时创建的。边是在在两个节点使用边操作(->)连接在一起时创建的,如第 6 行到第 8 行所示。我还对边使用了一个可选的属性 label,用它来表示边在图中的名称。最后,在第 9 行完成对该图规范的定义。

使用 Dot 符号表示的示例图(test.dot)

 

 1: digraph G {

2:   node1;

3:   node2;

4:   node3;

5:

6:   node1 -> node2 [label="edge_1_2"];

7:   node1 -> node3 [label="edge_1_3"];

8:   node2 -> node3 [label="edge_2_3"];

9: }

要将这个 .dot 文件转换成一个图形映像,则需要使用 Dot 工具,这个工具是在 Graphviz 包中提供的。清单 6 介绍了这种转换。

清单 6. 使用 Dot 来创建 JPG 映像

[linux /home]$ dot -Tjpg test.dot -o test.jpg

在这段代码中,我告诉 Dot 使用 test.dot 图形规范,并生成一个 JPG 图像,将其保存在文件 test.jpg 中。所生成的图像如图1所示。在此处,我使用了 JPG 格式,但是 Dot 工具也可以支持其他格式,其中包括 GIF、PNG 和 postscript等等。

 

使用Gnu gprof进行Linux平台下的程序分析(图二)
图 1. Dot 创建的示例图



Dot 语言还可以支持其他一些选项,包括外形、颜色和很多属性。有兴趣可以查看graphviz相关文档。

2.从gprof的输出中提取调用图信息,产生可供Graphviz使用的dot文件。

这样的脚本有人已经实现了,我们只要下载一个现成的就可以了,首先从http://www.ioplex.com/~miallen/ 网站下载一个mkgraph脚本。解压该脚本到包含gmon.out文件的目录下。使用mkgraph0.sh产生调用的jpg图像文件。例如:使用上面的例子,生成cflow的调用图。

[linux /home/cflow-1.1/src]$ mkgraph0.sh cflow gmon.out

部分调用图如下,有了这个图是不是对程序整体框架有了个清晰地了解,如果你对生成的调用图效果不满意,你还可以通过修改mkgraph0脚本使之产生合适的dot文件即可:

 

 

 

使用Gnu gprof进行Linux平台下的程序分析(图三)
部分调用图如下



总结:

使用gprof , Graphviz , mkgraph 生成函数调用图

1. 使用 -pg 编译和链接你的应用程序。

2. 执行你的应用程序使之生成供gprof 分析的数据。

3. 使用mkgraph脚本生成图形化的函数调用图。

 

相关资料:

文档:用 Graphviz 可视化函数调用

文档:Speed your code with the GNU profiler

文档:gropf 帮助文件

Mkgraph 脚本:http://www.ioplex.com/~miallen/

Graphviz 工具:http://www.graphviz.org

Cflow :http://www.gnu.org/software/cflow/

gobject/gstobject的初始化(包括类型,类,实例等的初始化)。

gobjec相关学习文章的list.

 

Type,Class, instance 初始化,包括其中函数调用的过程总结:

阶段 相应API 作用 GObject 调用顺序 GstObject GstElement
1 xxx_yyy_get_type()

创建TypeNode, 并且赋值一些APIs:

base_class_init()

class_init()

instance_init(),

等等

     
2 g_type_class_ref()

调用base_class_init()

从父-->子,

g_object_base_class_init()

1. 父类:g_object_base_class_init()

2. 自己没有base_init()

1. 爷爷:g_object_base_class_init()

2. 父亲没有base_init()

3. gst_element_base_class_init

调用class_init(), 指定各种APIs

包括class->constructor(),

set_property()/get_property()

dispose()/finalize()

g_object_do_class_init()

gst_object_class_init()

1. 首选取得GObjectClass

2. 重载一些函数

3. 装载一些自己的signal

gst_element_class_init()

1. 首选取得GObjectClass

2. 把GObjectClass的一些函数换成自己的(重载)

3. 装载自己的信号

3 g_object_new()

调用class->constructor() --> g_type_create_instance() -->

父类的instance(), 自己的instance()

g_object_constructor()->g_type_create_instance() ->

1. g_object_init()

g_object_constructor()->g_type_create_instance() ->

1. 父亲:g_object_init()

2.自己:gst_object_init()

g_object_constructor()->g_type_create_instance() ->

1. 爷爷:g_object_init()

2.父亲:gst_object_init()

3. 自己:gst_element_init()

 

set properties      

 

下面是具体的分析:

g_object_type_init()

1. 这个API完成GObject类型的注册

2. GObject 作为一个基础类型,放入全局数组以及Hash Table中

3. 分配GObject Node的数据,并且把用户指定的一系列函数指针赋值过去

4. 此时还没有真正的GObject对象实例。

 

void
g_type_init (void)
{
  g_type_init_with_debug_flags (0);
}
  /* G_TYPE_OBJECT
   */
  g_object_type_init ();
void
g_object_type_init (void)
{
  static gboolean initialized = FALSE;
  static const GTypeFundamentalInfo finfo = {
    G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE,
  };
  static GTypeInfo info = {
    sizeof (GObjectClass),
    (GBaseInitFunc) g_object_base_class_init,
    (GBaseFinalizeFunc) g_object_base_class_finalize,
    (GClassInitFunc) g_object_do_class_init,
    NULL	/* class_destroy */,
    NULL	/* class_data */,
    sizeof (GObject),
    0		/* n_preallocs */,
    (GInstanceInitFunc) g_object_init,
    NULL,	/* value_table */
  };
  static const GTypeValueTable value_table = {
    g_value_object_init,	  /* value_init */
    g_value_object_free_value,	  /* value_free */
    g_value_object_copy_value,	  /* value_copy */
    g_value_object_peek_pointer,  /* value_peek_pointer */
    "p",			  /* collect_format */
    g_value_object_collect_value, /* collect_value */
    "p",			  /* lcopy_format */
    g_value_object_lcopy_value,	  /* lcopy_value */
  };
  GType type;
  
  g_return_if_fail (initialized == FALSE);
  initialized = TRUE;
  
  /* G_TYPE_OBJECT
   */
  info.value_table = &value_table;
  type = g_type_register_fundamental (G_TYPE_OBJECT, g_intern_static_string ("GObject"), &info, &finfo, 0);
  g_assert (type == G_TYPE_OBJECT);
  g_value_register_transform_func (G_TYPE_OBJECT, G_TYPE_OBJECT, g_value_object_transform_value);
  

}

这里注册GObject类型:

1. 指定这个类型的标记flag

  static const GTypeFundamentalInfo finfo = {
    G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE,
  };

2. 注册Class的GTypeInfo. 相关的几个函数指针

3. 注册GObject type的函数表Value Table

 

问题来了:

g_object_base_class_init、g_object_do_class_init、g_object_init 何时被调用?

有什么严格的顺序吗?

 

我们先看看GObject的内部注册过程中,干了些什么?然后再看看基于Gobject生成新的object实例时

做了些什么?

GType
g_type_register_fundamental (GType                       type_id,
			     const gchar                *type_name,
			     const GTypeInfo            *info,
			     const GTypeFundamentalInfo *finfo,
			     GTypeFlags			 flags)
{
  TypeNode *node;
 
  
  G_WRITE_LOCK (&type_rw_lock);
  node = type_node_fundamental_new_W (type_id, type_name, finfo->type_flags);
  type_add_flags_W (node, flags);
  
  if (check_type_info_I (NULL, NODE_FUNDAMENTAL_TYPE (node), type_name, info))
    type_data_make_W (node, info,
		      check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
  G_WRITE_UNLOCK (&type_rw_lock);
  
  return NODE_TYPE (node);
}

其中关系不大的代码去掉了。

因为GObject是基础类型,没有父节点,所以代码中,去掉了部分。

static TypeNode*
type_node_any_new_W (TypeNode             *pnode,
		     GType                 ftype,
		     const gchar          *name,
		     GTypePlugin          *plugin,
		     GTypeFundamentalFlags type_flags)
{
  guint n_supers;
  GType type;
  TypeNode *node;
  guint i, node_size = 0;

  n_supers = pnode ? pnode->n_supers + 1 : 0;
  
  if (!pnode)
    node_size += SIZEOF_FUNDAMENTAL_INFO;	      /* fundamental type info */
  node_size += SIZEOF_BASE_TYPE_NODE ();	      /* TypeNode structure */
  node_size += (sizeof (GType) * (1 + n_supers + 1)); /* self + ancestors + (0) for ->supers[] */
  node = g_malloc0 (node_size);
  if (!pnode)					      /* offset fundamental types */
    {
      node = G_STRUCT_MEMBER_P (node, SIZEOF_FUNDAMENTAL_INFO);
      static_fundamental_type_nodes[ftype >> G_TYPE_FUNDAMENTAL_SHIFT] = node;
      type = ftype;
    }

  
 
 
  node->n_supers = n_supers;
  if (!pnode)
    {
      node->supers[0] = type;
      node->supers[1] = 0;
      
      node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0;
      node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0;
      
      if (NODE_IS_IFACE (node))
	{
          IFACE_NODE_N_PREREQUISITES (node) = 0;
	  IFACE_NODE_PREREQUISITES (node) = NULL;
	}
      else
	_g_atomic_array_init (CLASSED_NODE_IFACES_ENTRIES (node));
    }
 
  TRACE(GOBJECT_TYPE_NEW(name, node->supers[1], type));

  node->plugin = plugin;
  node->n_children = 0;
  node->children = NULL;
  node->data = NULL;
  node->qname = g_quark_from_string (name);
  node->global_gdata = NULL;
  
  g_hash_table_insert (static_type_nodes_ht,
		       GUINT_TO_POINTER (node->qname),
		       (gpointer) type);
  return node;
}

上面重要的地方:

static_fundamental_type_nodes[ftype >> G_TYPE_FUNDAMENTAL_SHIFT] = node; //把G_OBJECT_TYPE放入基础类型数组。


      node->supers[0] = type; //没有父亲节点
      node->supers[1] = 0;
     
      node->is_classed = (type_flags & G_TYPE_FLAG_CLASSED) != 0; //可类化
      node->is_instantiatable = (type_flags & G_TYPE_FLAG_INSTANTIATABLE) != 0;  //可实例化
 

下面是把前面设置的GObject的标识:存入node的qdata中,用quark:static_quark_type_flags 来标识。

即:

static void
type_add_flags_W (TypeNode  *node,
		  GTypeFlags flags)
{
  guint dflags;
  
  g_return_if_fail ((flags & ~TYPE_FLAG_MASK) == 0);
  g_return_if_fail (node != NULL);
  
  if ((flags & TYPE_FLAG_MASK) && node->is_classed && node->data && node->data->class.class)
    g_warning ("tagging type `%s' as abstract after class initialization", NODE_NAME (node));
  dflags = GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags));
  dflags |= flags;
  type_set_qdata_W (node, static_quark_type_flags, GUINT_TO_POINTER (dflags));
}

 

重点是下面:

/* --- type info (type node data) --- */
static void
type_data_make_W (TypeNode              *node,
		  const GTypeInfo       *info,
		  const GTypeValueTable *value_table)
{
  TypeData *data;
  GTypeValueTable *vtable = NULL;
  guint vtable_size = 0;
  
  g_assert (node->data == NULL && info != NULL);
  
  if (!value_table)
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));
      
      if (pnode)
	vtable = pnode->data->common.value_table;
      else
	{
	  static const GTypeValueTable zero_vtable = { NULL, };
	  
	  value_table = &zero_vtable;
	}
    }
  if (value_table)
    {
      /* need to setup vtable_size since we have to allocate it with data in one chunk */
      vtable_size = sizeof (GTypeValueTable);
      if (value_table->collect_format)
	vtable_size += strlen (value_table->collect_format);
      if (value_table->lcopy_format)
	vtable_size += strlen (value_table->lcopy_format);
      vtable_size += 2;
    }
   
  if (node->is_instantiatable) /* carefull, is_instantiatable is also is_classed */
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));

      data = g_malloc0 (sizeof (InstanceData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (InstanceData));
      data->instance.class_size = info->class_size;
      data->instance.class_init_base = info->base_init;
      data->instance.class_finalize_base = info->base_finalize;
      data->instance.class_init = info->class_init;
      data->instance.class_finalize = info->class_finalize;
      data->instance.class_data = info->class_data;
      data->instance.class = NULL;
      data->instance.init_state = UNINITIALIZED;
      data->instance.instance_size = info->instance_size;
      /* We'll set the final value for data->instance.private size
       * after the parent class has been initialized
       */
      data->instance.private_size = 0;
      data->instance.class_private_size = 0;
      if (pnode)
        data->instance.class_private_size = pnode->data->instance.class_private_size;
#ifdef	DISABLE_MEM_POOLS
      data->instance.n_preallocs = 0;
#else	/* !DISABLE_MEM_POOLS */
      data->instance.n_preallocs = MIN (info->n_preallocs, 1024);
#endif	/* !DISABLE_MEM_POOLS */
      data->instance.instance_init = info->instance_init;
    }
  else if (node->is_classed) /* only classed */
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));

      data = g_malloc0 (sizeof (ClassData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (ClassData));
      data->class.class_size = info->class_size;
      data->class.class_init_base = info->base_init;
      data->class.class_finalize_base = info->base_finalize;
      data->class.class_init = info->class_init;
      data->class.class_finalize = info->class_finalize;
      data->class.class_data = info->class_data;
      data->class.class = NULL;
      data->class.class_private_size = 0;
      if (pnode)
        data->class.class_private_size = pnode->data->class.class_private_size;
      data->class.init_state = UNINITIALIZED;
    }
  else if (NODE_IS_IFACE (node))
    {
      data = g_malloc0 (sizeof (IFaceData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (IFaceData));
      data->iface.vtable_size = info->class_size;
      data->iface.vtable_init_base = info->base_init;
      data->iface.vtable_finalize_base = info->base_finalize;
      data->iface.dflt_init = info->class_init;
      data->iface.dflt_finalize = info->class_finalize;
      data->iface.dflt_data = info->class_data;
      data->iface.dflt_vtable = NULL;
    }
  else if (NODE_IS_BOXED (node))
    {
      data = g_malloc0 (sizeof (BoxedData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (BoxedData));
    }
  else
    {
      data = g_malloc0 (sizeof (CommonData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (CommonData));
    }
  
  node->data = data;
  
  if (vtable_size)
    {
      gchar *p;
      
      /* we allocate the vtable and its strings together with the type data, so
       * children can take over their parent's vtable pointer, and we don't
       * need to worry freeing it or not when the child data is destroyed
       */
      *vtable = *value_table;
      p = G_STRUCT_MEMBER_P (vtable, sizeof (*vtable));
      p[0] = 0;
      vtable->collect_format = p;
      if (value_table->collect_format)
	{
	  strcat (p, value_table->collect_format);
	  p += strlen (value_table->collect_format);
	}
      p++;
      p[0] = 0;
      vtable->lcopy_format = p;
      if (value_table->lcopy_format)
	strcat  (p, value_table->lcopy_format);
    }
  node->data->common.value_table = vtable;
  node->mutatable_check_cache = (node->data->common.value_table->value_init != NULL &&
				 !((G_TYPE_FLAG_VALUE_ABSTRACT | G_TYPE_FLAG_ABSTRACT) &
				   GPOINTER_TO_UINT (type_get_qdata_L (node, static_quark_type_flags))));
  
  g_assert (node->data->common.value_table != NULL); /* paranoid */

  g_atomic_int_set ((int *) &node->ref_count, 1);
}

这里有个union的变量:node->data: 可选4个类型:BoxedData/IFaceData/ClassData/InsanceData

node->is_instantiatable = 1

所以选择InstanceData

  if (node->is_instantiatable) /* carefull, is_instantiatable is also is_classed */
    {
      TypeNode *pnode = lookup_type_node_I (NODE_PARENT_TYPE (node));

      data = g_malloc0 (sizeof (InstanceData) + vtable_size);
      if (vtable_size)
	vtable = G_STRUCT_MEMBER_P (data, sizeof (InstanceData));
      data->instance.class_size = info->class_size;
      data->instance.class_init_base = info->base_init;
      data->instance.class_finalize_base = info->base_finalize;
      data->instance.class_init = info->class_init;
      data->instance.class_finalize = info->class_finalize;
      data->instance.class_data = info->class_data;
      data->instance.class = NULL;
      data->instance.init_state = UNINITIALIZED;
      data->instance.instance_size = info->instance_size;
      /* We'll set the final value for data->instance.private size
       * after the parent class has been initialized
       */
      data->instance.private_size = 0;
      data->instance.class_private_size = 0;
      if (pnode)
        data->instance.class_private_size = pnode->data->instance.class_private_size;
#ifdef	DISABLE_MEM_POOLS
      data->instance.n_preallocs = 0;
#else	/* !DISABLE_MEM_POOLS */
      data->instance.n_preallocs = MIN (info->n_preallocs, 1024);
#endif	/* !DISABLE_MEM_POOLS */
      data->instance.instance_init = info->instance_init;
    }

node->data = data;

把前面class的base/class/instance的APIs, Value Table APIs, 赋值给node->data

 

g_type_init():

1. ....

2. g_object_type_init()

3. ...

 

用户如何用呢?

之后应该就是g_object_new?

我们找个例子看看:

GObject

`|_____ GstObject

 

在gstobject.c:

G_DEFINE_ABSTRACT_TYPE (GstObject, gst_object, G_TYPE_OBJECT);

 

先比较一下gobject.c/gstobject.c提供的一些API:

Me (当前) 儿子 孙子 重孙子
GObject GstObject GstElement GstBin GstPipeline
g_object_base_class_init()   gst_element_base_class_init gst_bin_base_init gst_pipeline_base_init
g_object_do_class_init() gst_object_class_init() gst_element_class_init gst_bin_class_init gst_pipeline_class_init
g_object_init() gst_object_init() gst_element_init gst_bin_init gst_pipeline_init

 

把宏展开,看到上面gstobject提供的两个API,绑定了:

#define _G_DEFINE_TYPE_EXTENDED_BEGIN(GstObject, gst_object, G_TYPE_OBJECT, flags) \
\
static void     gst_object_init              (GstObject       *self); \
static void     gst_object_class_init        (GstObjectClass *klass); \
static gpointer gst_object_parent_class = NULL; \
static void     gst_object_class_intern_init (gpointer klass) \
{ \
  gst_object_parent_class = g_type_class_peek_parent (klass); \
  gst_object_class_init ((GstObjectlass*) klass); \
} \
\
GType \
gst_object_get_type (void) \
{ \
  static volatile gsize g_define_type_id__volatile = 0; \
  if (g_once_init_enter (&g_define_type_id__volatile))  \
    { \
      GType g_define_type_id = \
        g_type_register_static_simple (G_TYPE_OBJECT, \
                                       g_intern_static_string ("GstObject"), \
                                       sizeof (CGstObjectClass), \
                                       (GClassInitFunc) gst_object_class_intern_init, \
                                       sizeof (GstObject), \
                                       (GInstanceInitFunc) gst_object_init, \
                                       (GTypeFlags) flags); \
      { /* custom code follows */
#define _G_DEFINE_TYPE_EXTENDED_END()	\
        /* following custom code */	\
      }					\
      g_once_init_leave (&g_define_type_id__volatile, g_define_type_id); \
    }					\
  return g_define_type_id__volatile;	\
} /* closes gst_object_get_type() */

 

首先取得父类的class数据:

gpointer
g_type_class_peek_parent (gpointer g_class)
{
  TypeNode *node;
  gpointer class = NULL;
  
  g_return_val_if_fail (g_class != NULL, NULL);
  
  node = lookup_type_node_I (G_TYPE_FROM_CLASS (g_class));
  /* We used to acquire a read lock here. That is not necessary, since 
   * parent->data->class.class is constant as long as the derived class
   * exists. 
   */
  if (node && node->is_classed && node->data && NODE_PARENT_TYPE (node))
    {
      node = lookup_type_node_I (NODE_PARENT_TYPE (node));
      class = node->data->class.class;
    }
  else if (NODE_PARENT_TYPE (node))
    g_warning (G_STRLOC ": invalid class pointer `%p'", g_class);
  
  return class;
}

class = node->data->class.class;

 

然后真正开始Init:

gst.c中:

g_type_class_ref (gst_object_get_type ());

 

首先会调用:

GType
g_type_register_static_simple (GType             parent_type,
			       const gchar      *type_name,
			       guint             class_size,
			       GClassInitFunc    class_init,
			       guint             instance_size,
			       GInstanceInitFunc instance_init,
			       GTypeFlags	 flags)
{
  GTypeInfo info;

  info.class_size = class_size;
  info.base_init = NULL;
  info.base_finalize = NULL;
  info.class_init = class_init;
  info.class_finalize = NULL;
  info.class_data = NULL;
  info.instance_size = instance_size;
  info.n_preallocs = 0;
  info.instance_init = instance_init;
  info.value_table = NULL;

  return g_type_register_static (parent_type, type_name, &info, flags);
}

然后指定自己的class_init/Instance_init函数,就开始创建这个TypeNode.

GType
g_type_register_static (GType            parent_type,
			const gchar     *type_name,
			const GTypeInfo *info,
			GTypeFlags	 flags)
{
  TypeNode *pnode, *node;
  GType type = 0;
  
  g_return_val_if_type_system_uninitialized (0);
  g_return_val_if_fail (parent_type > 0, 0);
  g_return_val_if_fail (type_name != NULL, 0);
  g_return_val_if_fail (info != NULL, 0);
  
  if (!check_type_name_I (type_name) ||
      !check_derivation_I (parent_type, type_name))
    return 0;
  if (info->class_finalize)
    {
      g_warning ("class finalizer specified for static type `%s'",
		 type_name);
      return 0;
    }
  
  pnode = lookup_type_node_I (parent_type);
  G_WRITE_LOCK (&type_rw_lock);
  type_data_ref_Wm (pnode);
  if (check_type_info_I (pnode, NODE_FUNDAMENTAL_TYPE (pnode), type_name, info))
    {
      node = type_node_new_W (pnode, type_name, NULL);
      type_add_flags_W (node, flags);
      type = NODE_TYPE (node);
      type_data_make_W (node, info,
			check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
    }
  G_WRITE_UNLOCK (&type_rw_lock);
  
  return type;
}

1. type_data_ref_Wm (pnode); //给父节点增加一个引用计数

2. node = type_node_new_W (pnode, type_name, NULL); //从父节点拷贝出一份内存,并且建立父子关系;

   并且把新创建的“子节点” 保存在父节点的数组中:pnode->children[i] = type;

3. type_data_make_W, 就是用户指定的各种APIs, Value Table APIs, 指定给这个新建的Type Node, 这里是给GstObject

 

g_type_class_ref(GstObject);


好戏都在g_type_class_ref

这个函数是个循环嵌套函数!

gpointer
g_type_class_ref (GType type)
{
  TypeNode *node;
  GType ptype;
  gboolean holds_ref;
  GTypeClass *pclass;
  /* optimize for common code path */
  node = lookup_type_node_I (type);

  /* we need an initialized parent class for initializing derived classes */
  ptype = NODE_PARENT_TYPE (node);
  pclass = ptype ? g_type_class_ref (ptype) : NULL;

它会顺藤摸瓜!找到最上面的祖先:

这里它会找到GObject (GstObject的父类),GObject已经是最顶层了。

好,接着往下:

  if (!holds_ref)
    type_data_ref_Wm (node);

  if (!node->data->class.class) /* class uninitialized */
    type_class_init_Wm (node, pclass);

最关键的地方:如果当前class并没有被初始化,则进行初始化。

先初始化GObject, 然后轮到其儿子:GstObject.

 

对某个typeclass进行下面的顺序进行初始化:

BASE_CLASS_INIT

BASE_IFACE_INIT

CLASS_INIT

IFACE_INIT

INITIALIZED

 

对GObject的初始化:

1. BASE_CLASS_INIT

  /* stack all base class initialization functions, so we
   * call them in ascending order.
   */
  for (bnode = node; bnode; bnode = lookup_type_node_I (NODE_PARENT_TYPE (bnode))) 
  {
    if (bnode->data->class.class_init_base)
      init_slist = g_slist_prepend (init_slist, (gpointer) bnode->data->class.class_init_base);
  }

  for (slist = init_slist; slist; slist = slist->next)
    {
      GBaseInitFunc class_init_base = (GBaseInitFunc) slist->data;
      class_init_base (class);
    }

 

for (bnode = node; bnode; bnode = lookup_type_nodeI (NODE_PARENT_TYPE (bnode)))

是从最底层往上层循环(孙子 --> 儿子--> 爷爷)

 

init_slist, 是反过来弄的:

爷爷->儿子-->孙子,这么一个顺序,进行class_init_base()

后面会印证。

 

2. CLASS_INIT

  g_atomic_int_set (&node->data->class.init_state, CLASS_INIT);
  
  G_WRITE_UNLOCK (&type_rw_lock);

  if (node->data->class.class_init)
    node->data->class.class_init (class, (gpointer) node->data->class.class_data);

此时执行:g_object_do_class_init()

 

父类初始化完成后,嵌套退回一层,开始自行GstObjec的初始化:

也是按照那个顺序,有的成员是NULL, 就跳过去:

3. GstObject的CLASS_INIT

  g_atomic_int_set (&node->data->class.init_state, CLASS_INIT);
  
  G_WRITE_UNLOCK (&type_rw_lock);

  if (node->data->class.class_init)
    node->data->class.class_init (class, (gpointer) node->data->class.class_data);

此时执行:gst_object_class_init()


注意,此时:gobject/gstobject的实例instance对象还没有建立!

 

g_object_base_class_init

static void
g_object_base_class_init (GObjectClass *class)
{
  GObjectClass *pclass = g_type_class_peek_parent (class);

  /* Don't inherit HAS_DERIVED_CLASS flag from parent class */
  class->flags &= ~CLASS_HAS_DERIVED_CLASS_FLAG;

  if (pclass)
    pclass->flags |= CLASS_HAS_DERIVED_CLASS_FLAG;

  /* reset instance specific fields and methods that don't get inherited */
  class->construct_properties = pclass ? g_slist_copy (pclass->construct_properties) : NULL;
  class->get_property = NULL;
  class->set_property = NULL;
}

g_object_do_class_init

static void
g_object_do_class_init (GObjectClass *class)
{
  /* read the comment about typedef struct CArray; on why not to change this quark */
  quark_closure_array = g_quark_from_static_string ("GObject-closure-array");

  quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
  quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
  pspec_pool = g_param_spec_pool_new (TRUE);
  property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue");
  property_notify_context.dispatcher = g_object_notify_dispatcher;

  class->constructor = g_object_constructor;
  class->constructed = g_object_constructed;
  class->set_property = g_object_do_set_property;
  class->get_property = g_object_do_get_property;
  class->dispose = g_object_real_dispose;
  class->finalize = g_object_finalize;
  class->dispatch_properties_changed = g_object_dispatch_properties_changed;
  class->notify = NULL;

  /**
   * GObject::notify:
   * @gobject: the object which received the signal.
   * @pspec: the #GParamSpec of the property which changed.
   *
   * The notify signal is emitted on an object when one of its
   * properties has been changed. Note that getting this signal
   * doesn't guarantee that the value of the property has actually
   * changed, it may also be emitted when the setter for the property
   * is called to reinstate the previous value.
   *
   * This signal is typically used to obtain change notification for a
   * single property, by specifying the property name as a detail in the
   * g_signal_connect() call, like this:
   * |[
   * g_signal_connect (text_view->buffer, "notify::paste-target-list",
   *                   G_CALLBACK (gtk_text_view_target_list_notify),
   *                   text_view)
   * ]|
   * It is important to note that you must use
   * <link linkend="canonical-parameter-name">canonical</link> parameter names as
   * detail strings for the notify signal.
   */
  gobject_signals[NOTIFY] =
    g_signal_new (g_intern_static_string ("notify"),
		  G_TYPE_FROM_CLASS (class),
		  G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION,
		  G_STRUCT_OFFSET (GObjectClass, notify),
		  NULL, NULL,
		  g_cclosure_marshal_VOID__PARAM,
		  G_TYPE_NONE,
		  1, G_TYPE_PARAM);

  /* Install a check function that we'll use to verify that classes that
   * implement an interface implement all properties for that interface
   */
  g_type_add_interface_check (NULL, object_interface_check_properties);
}

gst_object_class_init

static void
gst_object_class_init (GstObjectClass * klass)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);

  parent_class = g_type_class_peek_parent (klass);

#ifndef GST_DISABLE_TRACE
  _gst_object_trace = gst_alloc_trace_register (g_type_name (GST_TYPE_OBJECT));
#endif

  gobject_class->set_property = gst_object_set_property;
  gobject_class->get_property = gst_object_get_property;

  g_object_class_install_property (gobject_class, ARG_NAME,
      g_param_spec_string ("name", "Name", "The name of the object",
          NULL,
          G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS));

  /**
   * GstObject::parent-set:
   * @gstobject: a #GstObject
   * @parent: the new parent
   *
   * Emitted when the parent of an object is set.
   */
  gst_object_signals[PARENT_SET] =
      g_signal_new ("parent-set", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstObjectClass, parent_set), NULL, NULL,
      g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_OBJECT);

  /**
   * GstObject::parent-unset:
   * @gstobject: a #GstObject
   * @parent: the old parent
   *
   * Emitted when the parent of an object is unset.
   */
  gst_object_signals[PARENT_UNSET] =
      g_signal_new ("parent-unset", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstObjectClass, parent_unset), NULL,
      NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_OBJECT);

#if !defined(GST_DISABLE_LOADSAVE) && !defined(GST_REMOVE_DEPRECATED)
  /**
   * GstObject::object-saved:
   * @gstobject: a #GstObject
   * @xml_node: the xmlNodePtr of the parent node
   *
   * Trigered whenever a new object is saved to XML. You can connect to this
   * signal to insert custom XML tags into the core XML.
   */
  /* FIXME This should be the GType of xmlNodePtr instead of G_TYPE_POINTER
   *       (if libxml would use GObject)
   */
  gst_object_signals[OBJECT_SAVED] =
      g_signal_new ("object-saved", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstObjectClass, object_saved), NULL,
      NULL, g_cclosure_marshal_VOID__POINTER, G_TYPE_NONE, 1, G_TYPE_POINTER);

  klass->restore_thyself =
      ((void (*)(GstObject * object,
              gpointer self)) *gst_object_real_restore_thyself);
#endif

  /**
   * GstObject::deep-notify:
   * @gstobject: a #GstObject
   * @prop_object: the object that originated the signal
   * @prop: the property that changed
   *
   * The deep notify signal is used to be notified of property changes. It is
   * typically attached to the toplevel bin to receive notifications from all
   * the elements contained in that bin.
   */
  gst_object_signals[DEEP_NOTIFY] =
      g_signal_new ("deep-notify", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED |
      G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (GstObjectClass, deep_notify), NULL,
      NULL, gst_marshal_VOID__OBJECT_PARAM, G_TYPE_NONE, 2, GST_TYPE_OBJECT,
      G_TYPE_PARAM);

  klass->path_string_separator = "/";
  /* FIXME 0.11: Store this directly in the class struct */
  klass->lock = g_slice_new (GStaticRecMutex);
  g_static_rec_mutex_init (klass->lock);

  klass->signal_object = g_object_newv (gst_signal_object_get_type (), 0, NULL);

  /* see the comments at gst_object_dispatch_properties_changed */
  gobject_class->dispatch_properties_changed
      = GST_DEBUG_FUNCPTR (gst_object_dispatch_properties_changed);

  gobject_class->dispose = gst_object_dispose;
  gobject_class->finalize = gst_object_finalize;
}

 

Testing:

1. g_type_class_ref(G_TYPE_OBJECT)

g_type_init();
g_type_class_ref(G_TYPE_OBJECT);

result:

Entering g_type_class_ref
Type:GObject
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

 

2. 

g_type_init();
g_type_class_ref(G_TYPE_OBJECT);
g_type_class_ref(GST_TYPE_OBJECT);
Entering g_type_class_ref
Type:GObject
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref
Entering g_type_class_ref
Type:GstObject
 
+Entering g_type_class_ref
Type:GObject
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
++Entering g_type_class_ref
Type:GParamString
+++Entering g_type_class_ref
Type:GParam
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
+++Entering g_type_class_ref
Type:GObject
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
--Leaving  g_type_class_ref
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

 

 

3.

g_type_init();
g_type_class_ref(GST_TYPE_OBJECT);
Entering g_type_class_ref
Type:GstObject
 
+Entering g_type_class_ref
Type:GObject
++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
--Leaving  type_class_init_Wm
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
++Entering g_type_class_ref
Type:GParamString
+++Entering g_type_class_ref
Type:GParam
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
+++Entering g_type_class_ref
Type:GObject
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
--Leaving  g_type_class_ref
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

4.

g_type_init();
g_type_class_ref(G_TYPE_OBJECT);
g_type_class_ref(GST_TYPE_OBJECT);
g_type_class_ref(GST_TYPE_ELEMENT);

 

=============================

Entering g_type_class_ref
Type:GObject
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref


=============================

Entering g_type_class_ref
Type:GstObject
 
+Entering g_type_class_ref
Type:GObject
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
++Entering g_type_class_ref
Type:GParamString
+++Entering g_type_class_ref
Type:GParam
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
+++Entering g_type_class_ref
Type:GObject
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
--Leaving  g_type_class_ref
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref


=============================

Entering g_type_class_ref
Type:GstElement
 
+Entering g_type_class_ref
Type:GstObject
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GstElement.class_init_base()
Call GObject.class_init_base()
Call GstElement.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

5.

g_type_init();
g_type_class_ref(GST_TYPE_ELEMENT);
Entering g_type_class_ref
Type:GstElement
 
+Entering g_type_class_ref
Type:GstObject
++Entering g_type_class_ref
Type:GObject
+++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
+++Entering g_type_class_ref
Type:GParamString
++++Entering g_type_class_ref
Type:GParam
+++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
-----Leaving  type_class_init_Wm
----Leaving  g_type_class_ref
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering g_type_class_ref
Type:GstSignalObject
++++Entering g_type_class_ref
Type:GObject
----Leaving  g_type_class_ref
++++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering g_type_class_ref
Type:GstSignalObject
---Leaving  g_type_class_ref
--Leaving  type_class_init_Wm
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GstElement.class_init_base()
Call GObject.class_init_base()
Call GstElement.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

 

6.

g_type_init();
g_type_class_ref(GST_TYPE_PIPELINE);
Entering g_type_class_ref
Type:GstPipeline
 
+Entering g_type_class_ref
Type:GstBin
++Entering g_type_class_ref
Type:GstElement
+++Entering g_type_class_ref
Type:GstObject
++++Entering g_type_class_ref
Type:GObject
+++++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
-----Leaving  type_class_init_Wm
----Leaving  g_type_class_ref
++++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
+++++Entering g_type_class_ref
Type:GParamString
++++++Entering g_type_class_ref
Type:GParam
+++++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
-------Leaving  type_class_init_Wm
------Leaving  g_type_class_ref
++++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
------Leaving  type_class_init_Wm
-----Leaving  g_type_class_ref
+++++Entering g_type_class_ref
Type:GstSignalObject
++++++Entering g_type_class_ref
Type:GObject
------Leaving  g_type_class_ref
++++++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
------Leaving  type_class_init_Wm
-----Leaving  g_type_class_ref
+++++Entering g_type_class_ref
Type:GstSignalObject
-----Leaving  g_type_class_ref
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GstElement.class_init_base()
Call GObject.class_init_base()
Call GstElement.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering type_class_init_Wm
Call GstBin.class_init_base()
Call GstElement.class_init_base()
Call GObject.class_init_base()
Call GstBin.class_init()
+++Entering g_type_class_ref
Type:GParamBoolean
++++Entering g_type_class_ref
Type:GParam
----Leaving  g_type_class_ref
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamBoolean.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering g_type_class_ref
Type:GParamBoolean
---Leaving  g_type_class_ref
+++Entering g_type_class_ref
Type:GstBin
++++Entering g_type_class_ref
Type:GstElement
----Leaving  g_type_class_ref
---Leaving  g_type_class_ref
--Leaving  type_class_init_Wm
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GstPipeline.class_init_base()
Call GstBin.class_init_base()
Call GstElement.class_init_base()
Call GObject.class_init_base()
Call GstPipeline.class_init()
++Entering g_type_class_ref
Type:GParamUInt64
+++Entering g_type_class_ref
Type:GParam
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamUInt64.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GParamBoolean
--Leaving  g_type_class_ref
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

 

上面的base调用打印应该是反的

Call GstPipeline.class_init_base()
Call GstBin.class_init_base()
Call GstElement.class_init_base()
Call GObject.class_init_base()
 

实际是:

GObject.class_init_base()

GstElement.class_init_base()

GstBin.class_init_base()

GstPipeline.class_init_base()

先父亲后儿子的顺序,调用base_init():

Me (当前) 儿子 孙子 重孙子
GObject GstObject GstElement GstBin GstPipeline
g_object_base_class_init()   gst_element_base_class_init gst_bin_base_init gst_pipeline_base_init
g_object_do_class_init() gst_object_class_init() gst_element_class_init gst_bin_class_init gst_pipeline_class_init
g_object_init() gst_object_init() gst_element_init gst_bin_init gst_pipeline_init

 

小结一下:

xxx_yyy_get_type(), 是创建type node, 并且,绑定自己定义的一些APIs;

g_type_class_ref(), 是调用用户的一些class: APIs,

                                先调用base_init() [老子--> 儿子-->当前],

                                在调用class_init()

 

这个时候,对象实例依然没有创建。

 

下面就要看g_object_newv的实力了!

 

examples:

1.

g_object_new(G_TYPE_OBJECT, NULL);
GEnum->is_classed = 1
GFlags->is_classed = 1
GParam->is_instantiatable = 1
GObject->is_instantiatable = 1
GParamChar->is_instantiatable = 1
GParamUChar->is_instantiatable = 1
GParamBoolean->is_instantiatable = 1
GParamInt->is_instantiatable = 1
GParamUInt->is_instantiatable = 1
GParamLong->is_instantiatable = 1
GParamULong->is_instantiatable = 1
GParamInt64->is_instantiatable = 1
GParamUInt64->is_instantiatable = 1
GParamUnichar->is_instantiatable = 1
GParamEnum->is_instantiatable = 1
GParamFlags->is_instantiatable = 1
GParamFloat->is_instantiatable = 1
GParamDouble->is_instantiatable = 1
GParamString->is_instantiatable = 1
GParamParam->is_instantiatable = 1
GParamBoxed->is_instantiatable = 1
GParamPointer->is_instantiatable = 1
GParamValueArray->is_instantiatable = 1
GParamObject->is_instantiatable = 1
GParamOverride->is_instantiatable = 1
GParamGType->is_instantiatable = 1
GParamVariant->is_instantiatable = 1


=============================

Entering g_type_class_ref
Type:GObject
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref
Entering g_type_class_ref
Type:GObject
Leaving  g_type_class_ref
Call GObject.instance_init()

 

2.

g_object_new(GST_TYPE_OBJECT,NULL);
GEnum->is_classed = 1
GFlags->is_classed = 1
GParam->is_instantiatable = 1
GObject->is_instantiatable = 1
GParamChar->is_instantiatable = 1
GParamUChar->is_instantiatable = 1
GParamBoolean->is_instantiatable = 1
GParamInt->is_instantiatable = 1
GParamUInt->is_instantiatable = 1
GParamLong->is_instantiatable = 1
GParamULong->is_instantiatable = 1
GParamInt64->is_instantiatable = 1
GParamUInt64->is_instantiatable = 1
GParamUnichar->is_instantiatable = 1
GParamEnum->is_instantiatable = 1
GParamFlags->is_instantiatable = 1
GParamFloat->is_instantiatable = 1
GParamDouble->is_instantiatable = 1
GParamString->is_instantiatable = 1
GParamParam->is_instantiatable = 1
GParamBoxed->is_instantiatable = 1
GParamPointer->is_instantiatable = 1
GParamValueArray->is_instantiatable = 1
GParamObject->is_instantiatable = 1
GParamOverride->is_instantiatable = 1
GParamGType->is_instantiatable = 1
GParamVariant->is_instantiatable = 1


=============================

GstObject->is_instantiatable = 1
Entering g_type_class_ref
Type:GstObject
 
+Entering g_type_class_ref
Type:GObject
++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GObject.class_init()
--Leaving  type_class_init_Wm
-Leaving  g_type_class_ref
 
+Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstObject.class_init()
++Entering g_type_class_ref
Type:GParamString
+++Entering g_type_class_ref
Type:GParam
++++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParam.class_init()
----Leaving  type_class_init_Wm
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GParam.class_init_base()
Call GParamString.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
Call GParam.instance_init()
Call GParamString.instance_init()
GstSignalObject->is_instantiatable = 1
++Entering g_type_class_ref
Type:GstSignalObject
+++Entering g_type_class_ref
Type:GObject
---Leaving  g_type_class_ref
+++Entering type_class_init_Wm
Call GObject.class_init_base()
Call GstSignalObject.class_init()
---Leaving  type_class_init_Wm
--Leaving  g_type_class_ref
++Entering g_type_class_ref
Type:GstSignalObject
--Leaving  g_type_class_ref
Call GObject.instance_init()
Call GstSignalObject.instance_init()
-Leaving  type_class_init_Wm
Leaving  g_type_class_ref

GLib-GObject-WARNING **: cannot create instance of abstract (non-instantiatable) type `GstObject'
aborting...

 

小结:

 

阶段 相应API 作用 GObject 调用顺序 GstObject GstElement
1 xxx_yyy_get_type()

创建TypeNode, 并且赋值一些APIs:

base_class_init()

class_init()

instance_init(),

等等

     
2 g_type_class_ref()

调用base_class_init()

从父-->子,

g_object_base_class_init()

1. 父类:g_object_base_class_init()

2. 自己没有base_init()

1. 爷爷:g_object_base_class_init()

2. 父亲没有base_init()

3. gst_element_base_class_init

调用class_init(), 指定各种APIs

包括class->constructor(),

set_property()/get_property()

dispose()/finalize()

g_object_do_class_init()

gst_object_class_init()

1. 首选取得GObjectClass

2. 重载一些函数

3. 装载一些自己的signal

gst_element_class_init()

1. 首选取得GObjectClass

2. 把GObjectClass的一些函数换成自己的(重载)

3. 装载自己的信号

3 g_object_new()

调用class->constructor() -->   g_type_create_instance() -->

父类的instance(), 自己的instance()

g_object_constructor()->g_type_create_instance() ->

1. g_object_init()

g_object_constructor()->g_type_create_instance() ->

1. 父亲:g_object_init()

2.自己:gst_object_init()

g_object_constructor()->g_type_create_instance() ->

1. 爷爷:g_object_init()

2.父亲:gst_object_init()

3. 自己:gst_element_init()

 

set properties      

 

疑问:在同一进程内,如果同时注册N个话,如果管理?

“用时”注册,会浪费时间吗?

 

 

gobject中比较常用,但是又比较生疏的数据类型quark/dataset。

gobjec相关学习文章的list.

glib中除了提供

1. array/byte array/pointer array

2. SList/List

3. Hash Table

4. Queue

5. String

6. Tree

之外,还有quark/dataset, 这是gobject中用的非常多的。要理解它。

 

GQuark:

quark其实就是个数字,用来和字符串进行匹配。

 

只有一个gquark.h, 没有gquark.c

typedef guint32 GQuark;

实现文件在gdataset.c

static GHashTable   *g_quark_ht = NULL;
static gchar       **g_quarks = NULL;
static GQuark        g_quark_seq_id = 0;

上面定义了一个hash table:g_quark_ht和一个全局的指针数组:g_quarks。

 

从下面的new中,我们可以很清楚的看到,string如何和数字对应的:

/* HOLDS: g_quark_global_lock */
static inline GQuark
g_quark_new (gchar *string)
{
  GQuark quark;
  
  if (g_quark_seq_id % G_QUARK_BLOCK_SIZE == 0)
    g_quarks = g_renew (gchar*, g_quarks, g_quark_seq_id + G_QUARK_BLOCK_SIZE);
  if (!g_quark_ht)
    {
      g_assert (g_quark_seq_id == 0);
      g_quark_ht = g_hash_table_new (g_str_hash, g_str_equal);
      g_quarks[g_quark_seq_id++] = NULL;
    }

  quark = g_quark_seq_id++;
  g_quarks[quark] = string;
  g_hash_table_insert (g_quark_ht, string, GUINT_TO_POINTER (quark));
  
  return quark;
}

会创建一个全局的Hash Table: g_quark_ht, 用来保持string/quark

同时把string放入数组中:g_quarks[quark] = string.

如果一个进程中有很多string, 那个这个指针数组是否很大?占用的内存如何?能否优化一些不用的?

 

GData

有了gquark后,就可以有GData了,这是和

gquark关联的一个数据元链表。

struct _GData
{
  GData *next;
  GQuark id;
  gpointer data;
  GDestroyNotify destroy_func;
};

这个data list, 并没有一个明显的全局变量来保持它。

1. 用时分配内存

2. 存放在一个全局的Hash Table中

3. 用完释放掉

 

GDataset:

 

struct _GDataset
{
  gconstpointer location;
  GData        *datalist;
};

Datalist的作用主要是:

给GObject关联任意数据。

Data lists are used for associating arbitrary data with #GObjects

 

定义了一个全局的Hash Table来保存,新生成的datalist

static GHashTable   *g_dataset_location_ht = NULL;
static GDataset     *g_dataset_cached = NULL; /* should this be
						 threadspecific? */
static void
g_data_initialize (void)
{
  g_return_if_fail (g_dataset_location_ht == NULL);

  g_dataset_location_ht = g_hash_table_new (g_direct_hash, NULL);
  g_dataset_cached = NULL;
}

下面2个函数会把new的数据,放到hash table中:

void
g_dataset_id_set_data_full (gconstpointer  dataset_location,
			    GQuark         key_id,
			    gpointer       data,
			    GDestroyNotify destroy_func)
void
g_datalist_id_set_data_full (GData	  **datalist,
			     GQuark         key_id,
			     gpointer       data,
			     GDestroyNotify destroy_func)

再仔细看下这个函数:

void
g_dataset_id_set_data_full (gconstpointer  dataset_location,
			    GQuark         key_id,
			    gpointer       data,
			    GDestroyNotify destroy_func)
{
  register GDataset *dataset;
  
  g_return_if_fail (dataset_location != NULL);
  if (!data)
    g_return_if_fail (destroy_func == NULL);
  if (!key_id)
    {
      if (data)
	g_return_if_fail (key_id > 0);
      else
	return;
    }
  
  G_LOCK (g_dataset_global);
  if (!g_dataset_location_ht)
    g_data_initialize ();
 
  dataset = g_dataset_lookup (dataset_location);
  if (!dataset)
    {
      dataset = g_slice_new (GDataset);
      dataset->location = dataset_location;
      g_datalist_init (&dataset->datalist);
      g_hash_table_insert (g_dataset_location_ht, 
			   (gpointer) dataset->location,
			   dataset);
    }
  
  g_data_set_internal (&dataset->datalist, key_id, data, destroy_func, dataset);
  G_UNLOCK (g_dataset_global);
}

0. 先根据hash table的key查询一下,是否已经存在与之关联的dataset

    如果没有,则新分配;如果有,则把相关的数据(data)放到这个data_list中

1. 上面是给GDataset分配一块内存

2. 把新的dataset插入到hash table中

3. 把用户想携带的data/destroy_func放到data_list中

 

实际使用:

在实际使用中g_dataset_xxx用的并不多,

而是g_datalist_xxx用的多:

#define   g_datalist_id_set_data(dl, q, d)      \
     g_datalist_id_set_data_full ((dl), (q), (d), NULL)

实际使用中,GObject主要用了这个函数:g_datalist_id_set_data

void
g_datalist_id_set_data_full (GData	  **datalist,
			     GQuark         key_id,
			     gpointer       data,
			     GDestroyNotify destroy_func)
{
  g_return_if_fail (datalist != NULL);
  if (!data)
    g_return_if_fail (destroy_func == NULL);
  if (!key_id)
    {
      if (data)
	g_return_if_fail (key_id > 0);
      else
	return;
    }

  G_LOCK (g_dataset_global);
  if (!g_dataset_location_ht)
    g_data_initialize ();
  
  g_data_set_internal (datalist, key_id, data, destroy_func, NULL);
  G_UNLOCK (g_dataset_global);
}

 

查询一下:

Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
Gobject.c (\glib-2.28.6\gobject):      g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
Gobject.c (\glib-2.28.6\gobject):      g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, quark, data);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, g_quark_from_string (key), data);
Gobjectnotifyqueue.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, context->quark_notify_queue, NULL);
Gparam.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&pspec->qdata, quark, data);
Gstobject.c (\gstreamer-0.10.36\gst):  g_datalist_id_set_data (&object_name_counts, q, GINT_TO_POINTER (count + 1));

 

GObject中的qdata:

1. 内部使用的

2. 进一步封装,提供给调用者使用的APIs

 

在GObject自己内部用了3个quark来标识与之关联的data(任何数据) 

static GQuark	            quark_closure_array = 0;
static GQuark	            quark_weak_refs = 0;
static GQuark	            quark_toggle_refs = 0;

再复杂的data, 用这个quark/datalist, 都可以绑定/提取,这样看来datalist还是很强大滴~

Gobject.c (\glib-2.28.6\gobject):static GQuark	            quark_closure_array = 0;
Gobject.c (\glib-2.28.6\gobject):  quark_closure_array = g_quark_from_static_string ("GObject-closure-array");
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
Gobject.c (\glib-2.28.6\gobject):      g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
Gobject.c (\glib-2.28.6\gobject):  carray = g_object_get_qdata (object, quark_closure_array);
Gobject.c (\glib-2.28.6\gobject):       * letting it fiddle with quark_closure_array which is empty anyways
Gobject.c (\glib-2.28.6\gobject):  carray = g_datalist_id_remove_no_notify (&object->qdata, quark_closure_array);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array);
---- quark_weak_refs Matches (7 in 1 files) ----
Gobject.c (\glib-2.28.6\gobject):static GQuark	            quark_weak_refs = 0;
Gobject.c (\glib-2.28.6\gobject):  quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
Gobject.c (\glib-2.28.6\gobject):  wstack = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_refs);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data_full (&object->qdata, quark_weak_refs, wstack, weak_refs_notify);
Gobject.c (\glib-2.28.6\gobject):  wstack = g_datalist_id_get_data (&object->qdata, quark_weak_refs);
Gobject.c (\glib-2.28.6\gobject):      g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
---- quark_toggle_refs Matches (6 in 1 files) ----
Gobject.c (\glib-2.28.6\gobject):static GQuark	            quark_toggle_refs = 0;
Gobject.c (\glib-2.28.6\gobject):  quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");
Gobject.c (\glib-2.28.6\gobject):  tstackptr = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);
Gobject.c (\glib-2.28.6\gobject):  tstack = g_datalist_id_remove_no_notify (&object->qdata, quark_toggle_refs);
Gobject.c (\glib-2.28.6\gobject):  g_datalist_id_set_data_full (&object->qdata, quark_toggle_refs, tstack,
Gobject.c (\glib-2.28.6\gobject):  tstack = g_datalist_id_get_data (&object->qdata, quark_toggle_refs);

 

static void
g_object_do_class_init (GObjectClass *class)
{
  /* read the comment about typedef struct CArray; on why not to change this quark */
  quark_closure_array = g_quark_from_static_string ("GObject-closure-array");

  quark_weak_refs = g_quark_from_static_string ("GObject-weak-references");
  quark_toggle_refs = g_quark_from_static_string ("GObject-toggle-references");

上面这3个quark来标识GObject的3种重要数据:

1)GObject的Closure Array

2)  Weak Reference

3) Toggle reference

 Closure Array用quark来存取:

void
g_object_watch_closure (GObject  *object,
			GClosure *closure)
{
  CArray *carray;
  guint i;
  
  g_return_if_fail (G_IS_OBJECT (object));
  g_return_if_fail (closure != NULL);
  g_return_if_fail (closure->is_invalid == FALSE);
  g_return_if_fail (closure->in_marshal == FALSE);
  g_return_if_fail (object->ref_count > 0);	/* this doesn't work on finalizing objects */
  
  g_closure_add_invalidate_notifier (closure, object, object_remove_closure);
  g_closure_add_marshal_guards (closure,
				object, (GClosureNotify) g_object_ref,
				object, (GClosureNotify) g_object_unref);
  G_LOCK (closure_array_mutex);
  carray = g_datalist_id_remove_no_notify (&object->qdata, quark_closure_array);
  if (!carray)
    {
      carray = g_renew (CArray, NULL, 1);
      carray->object = object;
      carray->n_closures = 1;
      i = 0;
    }
  else
    {
      i = carray->n_closures++;
      carray = g_realloc (carray, sizeof (*carray) + sizeof (carray->closures[0]) * i);
    }
  carray->closures[i] = closure;
  g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array);
  G_UNLOCK (closure_array_mutex);
}

g_datalist_id_set_data_full (&object->qdata, quark_closure_array, carray, destroy_closure_array);
这是把Closure Array用quark标识后,并且放入data list中

 

在g_object_unref()中:

      g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
      g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);

在dispose时也搞:

static void
g_object_real_dispose (GObject *object)
{
  g_signal_handlers_destroy (object);
  g_datalist_id_set_data (&object->qdata, quark_closure_array, NULL);
  g_datalist_id_set_data (&object->qdata, quark_weak_refs, NULL);
}

 

Weak Reference用quark来存取:

void
g_object_weak_ref (GObject    *object,
		   GWeakNotify notify,
		   gpointer    data)
{
  WeakRefStack *wstack;
  guint i;
  
  g_return_if_fail (G_IS_OBJECT (object));
  g_return_if_fail (notify != NULL);
  g_return_if_fail (object->ref_count >= 1);

  G_LOCK (weak_refs_mutex);
  wstack = g_datalist_id_remove_no_notify (&object->qdata, quark_weak_refs);
  if (wstack)
    {
      i = wstack->n_weak_refs++;
      wstack = g_realloc (wstack, sizeof (*wstack) + sizeof (wstack->weak_refs[0]) * i);
    }
  else
    {
      wstack = g_renew (WeakRefStack, NULL, 1);
      wstack->object = object;
      wstack->n_weak_refs = 1;
      i = 0;
    }
  wstack->weak_refs[i].notify = notify;
  wstack->weak_refs[i].data = data;
  g_datalist_id_set_data_full (&object->qdata, quark_weak_refs, wstack, weak_refs_notify);
  G_UNLOCK (weak_refs_mutex);
}

Toggle Reference用quark来存取:

void
g_object_add_toggle_ref (GObject       *object,
			 GToggleNotify  notify,
			 gpointer       data)
{
  ToggleRefStack *tstack;
  guint i;
  
  g_return_if_fail (G_IS_OBJECT (object));
  g_return_if_fail (notify != NULL);
  g_return_if_fail (object->ref_count >= 1);

  g_object_ref (object);

  G_LOCK (toggle_refs_mutex);
  tstack = g_datalist_id_remove_no_notify (&object->qdata, quark_toggle_refs);
  if (tstack)
    {
      i = tstack->n_toggle_refs++;
      /* allocate i = tstate->n_toggle_refs - 1 positions beyond the 1 declared
       * in tstate->toggle_refs */
      tstack = g_realloc (tstack, sizeof (*tstack) + sizeof (tstack->toggle_refs[0]) * i);
    }
  else
    {
      tstack = g_renew (ToggleRefStack, NULL, 1);
      tstack->object = object;
      tstack->n_toggle_refs = 1;
      i = 0;
    }

  /* Set a flag for fast lookup after adding the first toggle reference */
  if (tstack->n_toggle_refs == 1)
    g_datalist_set_flags (&object->qdata, OBJECT_HAS_TOGGLE_REF_FLAG);
  
  tstack->toggle_refs[i].notify = notify;
  tstack->toggle_refs[i].data = data;
  g_datalist_id_set_data_full (&object->qdata, quark_toggle_refs, tstack,
			       (GDestroyNotify)g_free);
  G_UNLOCK (toggle_refs_mutex);
}

 

Gobject除了自己内部用的3个quark外,

还封装了一组API给外部调用:调用者可以自己定义自己的quark, 并且可以给object绑定/提取自己关系的数据:

gpointer    g_object_get_qdata                (GObject        *object,
					       GQuark          quark);
void        g_object_set_qdata                (GObject        *object,
					       GQuark          quark,
					       gpointer        data);
void        g_object_set_qdata_full           (GObject        *object,
					       GQuark          quark,
					       gpointer        data,
					       GDestroyNotify  destroy);
gpointer    g_object_steal_qdata              (GObject        *object,
					       GQuark          quark);


gpointer    g_object_get_data                 (GObject        *object,
					       const gchar    *key);
void        g_object_set_data                 (GObject        *object,
					       const gchar    *key,
					       gpointer        data);
void        g_object_set_data_full            (GObject        *object,
					       const gchar    *key,
					       gpointer        data,
					       GDestroyNotify  destroy);
gpointer    g_object_steal_data               (GObject        *object,
					       const gchar    *key);

 

这一组首先是定义一个quark,

然后用quark来标识data, 并且进行绑定/提取操作:

gpointer    g_object_get_qdata                (GObject        *object,
       GQuark          quark);
void        g_object_set_qdata                (GObject        *object,
       GQuark          quark,
       gpointer        data);
void        g_object_set_qdata_full           (GObject        *object,
       GQuark          quark,
       gpointer        data,
       GDestroyNotify  destroy);
gpointer    g_object_steal_qdata              (GObject        *object,
       GQuark          quark);
 

下面这一组,不仅仅是quark, 可以用字符串来标识。

gpointer    g_object_get_data                 (GObject        *object,
       const gchar    *key);
void        g_object_set_data                 (GObject        *object,
       const gchar    *key,
       gpointer        data);
void        g_object_set_data_full            (GObject        *object,
       const gchar    *key,
       gpointer        data,
       GDestroyNotify  destroy);
gpointer    g_object_steal_data               (GObject        *object,
       const gchar    *key);

 

用的地方很多,非常多~

 

gobjec相关学习文章的list.

gobject相关学习文章的list.

Index:

 

 ID Title 简单描述
19 GObject 设计思想  Gobject Tutorial
18 C++ VS Gobject  
17 gobject->qdata的使用(1: property notify and getter/setter)。

 弄明白gobject property内部机制,

以及user如何使用它;更多的可以

参考gstreamer 如何使用它。

16 g_object_new()的理解。  
15 GObject中的Override(父类和子类的方法重写).  
14 g_new_object(GST_TYPE_PIPELINE) instance建立过程。 log
13 g_object_new(G_TYPE_OBJECT) instance的建立。 log
12 gobject/gstobject的初始化(包括类型,类,实例等的初始化)。

 基本上弄清base_class_init();class_init()

instance_init()的过程。

11 gobject中比较常用,但是又比较生疏的数据类型quark/dataset。  gobject中用的非常多的quark/qdata
10 小结一下:gsignal的new/connect/emit. 整体明白后,小结
9 从gst的have-type信号,看signal new/connect/emit. gst的例子
8 gsignal 1.new/2.connect/3.emit gtk的例子
7 gobject的调试。 主要查看继承关系
6 GTYPE类型系统分析 zz  
5 gobject运行时重要的数据结构。 分析gobject跑起来后,有哪些重要的数据结构
4 GObject Reference Manual 官方参考文档
3 type.h  type的定义等,如何保存
2 如何查看GType节点,以及父子关系。 一个调试方法
1 GObject-Glib Type, Object, Parameter and Signal 这是一个扫盲的总结

 

1. Type System : ok

2. Parameter/ParamSpec: ok

3. Closure/Marshal/Signal: ok

4. GObject

5. GModule

6. GPlugin

GObject消息系统:Closure (转)

Closures

Closures是异步信号传递概念的中心,它被广泛的应用到了GTK+和GNOME应用程序中。一个Closure是一个抽象的、通用表示的回调(callback)。它是一个包含三个对象的简单结构:

  • 一个函数指针(回调本身) ,原型类似于:return_type function_callback (... , gpointer user_data);
  • user_data指针用来传递到callback在调用Closure时。
  • 一个函数指针,代表Closure的销毁:当Closure的引用数达到0时,这个函数将被调用来释放Closure的结构。

GClosure结构代表所有Closure实现的共同基础:现在存在着很多各自不同的使用GObject类型系统的运行环境。GObject库提供一个简单的GCClosure类型,来明确使用C/C++的回调来实现。

一个GClosure提供以下简单的服务:

  • 调用(g_closure_invoke):这就是Closure创建的目的: 它们隐藏了回调者的回调细节。
  • 通知:相关事件的Closure通知监听者如Closure调用,Closure无效和Clsoure终结。监听者可以用注册g_closure_add_finalize_notifier(终结通知),g_closure_add_invalidate_notifier(无效通知)和g_closure_add_marshal_guards(调用通知)。对于终结和无效事件来说,这是对等的函数(g_closure_remove_finalize_notifier和g_closure_remove_invalidate_notifier,但调用过程不是。

 

C Closures

如果你用C或C++来通过一个给定的事件来连接一个回调,你可以选择使用一个简单的GCClosures来拥有一个完美又小的API或者使用更简单的g_signal_connect函数(后面将讲这个):

 

GClosure* g_cclosure_new (GCallback        callback_func,

 

gpointer user_data,

GClosureNotify destroy_data);

GClosure* g_cclosure_new_swap (GCallback callback_func,

gpointer user_data,

GClosureNotify destroy_data);

GClosure* g_signal_type_cclosure_new (GType itype,

guint struct_offset);

 

g_cclosure_new将创建一个新的closure将创建一个调用用户提供的回调函数,用户提供的user_data作为一个最后的参数。当closure被终结后(销毁的第二步),它将调用destroy_data函数如果用户提供的话。

g_cclosure_new_swap将创建一个closure来调用用户提供的回调函数使用用户提供的用户数据作为第一个参数(用来取代第用g_cclosure_new创建的最后一个参数)。当closure被终结后(销毁的第二步),它将调用destroy_data函数如果用户提供过的话。

non-C closures (for the fearless)

我们在前面提到过,Closure隐藏了回调请求的细节。在C语言中, 回调请求就像是函数调用:它在堆栈上为被调用的函数创建正确的框架并执行一个呼叫说明。

C Closure 转换目标函数中代表参数的GValue数组为C语言式的参数列表,用这新的参数列表来调用C函数,得到函数的返回值,并将其转换为一个GValue并将其返回给呼叫者。

下面的代码用C实现了一个简单的C函数,用整型作为它的第一个参数,并返回空值。

 

g_cclosure_marshal_VOID__INT (GClosure     *closure,

 

GValue *return_value,

guint n_param_values,

const GValue *param_values,

gpointer invocation_hint,

gpointer marshal_data)

{

typedef void (*GMarshalFunc_VOID__INT) (gpointer data1,

gint arg_1,

gpointer data2);

register GMarshalFunc_VOID__INT callback;

register GCClosure *cc = (GCClosure*) closure;

register gpointer data1, data2;

g_return_if_fail (n_param_values == 2);

data1 = g_value_peek_pointer (param_values + 0);

data2 = closure->data;

callback = (GMarshalFunc_VOID__INT) (marshal_data ? marshal_data : cc->callback);

callback (data1,

g_marshal_value_peek_int (param_values + 1),

data2);

}

 

当然,还存在其他形式的调用。比如,James Henstridge写了一个通用的Python marshaller,用在所有的Python Closure上(一个Python Closure就是closure调用过程中,使其拥有一个基于Python的回调)。这个python marshaller转换输入的代表函数参数的GValue列表到一个Python的等价结构的tuple(你可以在GNOME CVS服务器上查看pygobject结构的源码pygtype.c看到pyg_closure_marshal)

实际应用中,Closure是用来设定语言运行时的边界:如果你正在写Python代码,并且某个Python回调函数收到了一个GTK+控件的信号,所以GTK+的C代码需要执行Python代码。由GTK+对象调用的Closure将调用Python的回调:它的行为就是GTK+的C对象,也是一个标准的Python代码的Python对象。

小结一下:gsignal的new/connect/emit.

Signal New:

1. New 一个SignalNode, 放入到全局数组中:g_signal_nodes

2. 创建 Class Closure:

    1). 生成一个Closure

    2). 指定Class member method 作为default handler

          closure->notifiers[0].data = marshal_data; // default handler, 使用的是class的指针偏移

          closure->notifiers[0].notify = (GClosureNotify) meta_marshal; //一般是g_type_class_meta_marshal 或者g_type_iface_meta_marshal

3. 指定SignalNode的C Marshaller:

    node->c_marshaller = c_marshaller;

4. 把class closure放入SignalNode的成员数组中:

    node->class_closure_bsa = g_bsearch_array_insert (node->class_closure_bsa,   &g_class_closure_bconfig,

5. 指定这个class closure的C_Marshaller:

    closure->marshal = node->c_marshaller

 

Signal connect:

1. New 一个Handler

2. New 一个closure, 并让closure持有用户自己的callback: C Handler [User Handler]

3. 把handler放入全局的handler_list

4. 指定closure的marshaller:

    handler->closure->marshal = node->marshaller;

 

Signal Emit:

1. 根据signal id, 找到signal node*

2. 对输入参数打包为GValue形式

3. 开始处理各种回调:

   1)在class closure中,回调调用顺序:

       meta_marshal --> C Marshaller --> Default Handler [call member method]

   2) 在Handler list中

      c_marsaller --> user callback

4. 返回数据再转回C语言形式

 

更简洁的版本:

Signal New:

注册该信号的default handler + C_marshaller

Signal connect:

注册该信号的user handlers

Signal Emit:

分五个阶段:

阶段 标志 动作
1 G_SIGNAL_RUN_FIRST 调用class的default method handler
2 G_CONNECT_AFTER=FALSE 调用user handlers
3 G_SIGNAL_RUN_LAST 调用class的default method handler
4 G_CONNECT_AFTER=TRUE 调用user handlers
5 G_SIGNAL_RUN_CLEANUP 调用object method handler

 

gobjec相关学习文章的list.

 

从gst的have-type信号,看signal new/connect/emit.

gobjec相关学习文章的list.

 

上层: Caller (Signal connect + user callback )

下层: Callee (Signal New + Emission机制)

 

New signal:

 

gsttypefindelement.c:

  /**
   * GstTypeFindElement::have-type:
   * @typefind: the typefind instance
   * @probability: the probability of the type found
   * @caps: the caps of the type found
   *
   * This signal gets emitted when the type and its probability has
   * been found.
   */
  gst_type_find_element_signals[HAVE_TYPE] = g_signal_new ("have-type",
      G_TYPE_FROM_CLASS (typefind_class), G_SIGNAL_RUN_FIRST,
      G_STRUCT_OFFSET (GstTypeFindElementClass, have_type), NULL, NULL,
      gst_marshal_VOID__UINT_BOXED, G_TYPE_NONE, 2,
      G_TYPE_UINT, GST_TYPE_CAPS | G_SIGNAL_TYPE_STATIC_SCOPE);

  typefind_class->have_type =
      GST_DEBUG_FUNCPTR (gst_type_find_element_have_type);

这里2个比较重要:

1. 指定类的成员函数:G_STRUCT_OFFSET (GstTypeFindElementClass, have_type),

    是通过偏移地址进行的,在后面的函数g_type_class_meta_marshal里面,会根据这个函数偏移地址offset, 提取出这个函数的指针

2. 指定c_marshal: gst_marshal_VOID__UINT_BOXED

 

跟着代码进去:

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);

因为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;
}

g_closure_set_meta_marshal (closure, GUINT_TO_POINTER (struct_offset), g_type_class_meta_marshal);

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);
    }
 

  //marshal_data ->  gst_type_find_element_have_type()  
  closure->notifiers[0].data = marshal_data;
 

  //mata_marshal --> g_type_class_meta_marshal
  closure->notifiers[0].notify = (GClosureNotify) meta_marshal;
  SET (closure, meta_marshal, 1);
}

注意,这个meta_marshal 函数暂时还不会被调用。

 

假设被调用,其实就是emit后,发生的动作:

先根据offset, 提取函数的指针:

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 = G_STRUCT_MEMBER (gpointer, class, offset);
到这里,

callback = gst_type_find_element_have_type()= marshal_data ,这个函数指针会作为marshal_data,传入meta_marshal,

meta_marshal = g_type_class_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);
}

 

同时后面会继续进行初始化:

  signal_id = g_signal_newv (signal_name, itype, signal_flags,
			     class_closure, accumulator, accu_data, c_marshaller,
			     return_type, n_params, param_types);
  /* 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);

先把C_marshaller保存下来:node->c_marshaller = c_marshaller;

这里node->c_marshaller: gst_marshal_VOID__UINT_BOXED

 

前面g_signal_type_cclosure_new (itype, class_offset)创建了class的closure, 并且指定了:

1. ) 指向class成员的gst_type_find_element_have_type(),这个估计是default handler.

2. )指定 class closure的meta marshal: g_type_class_meta_marshal()

下面这个函数,又给这个class closure指定了一个C Marshaller:

static void
signal_add_class_closure (SignalNode *node,
			  GType       itype,
			  GClosure   *closure)
{
  ClassClosure key;

  /* can't optimize NOP emissions with overridden class closures */
  node->test_class_offset = 0;

  if (!node->class_closure_bsa)
    node->class_closure_bsa = g_bsearch_array_create (&g_class_closure_bconfig);
  key.instance_type = itype;
  key.closure = g_closure_ref (closure);
  node->class_closure_bsa = g_bsearch_array_insert (node->class_closure_bsa,
						    &g_class_closure_bconfig,
						    &key);
  g_closure_sink (closure);
  if (node->c_marshaller && closure && G_CLOSURE_NEEDS_MARSHAL (closure))
    g_closure_set_marshal (closure, node->c_marshaller);
}

 

在后面调用时:

meta_marshal -->调用 C Marshaller --> 调用 callback类的成员函数(default handler)

此时:这个signal node节点手里握有3个API:

1. class的meta marshal: g_type_class_meta_marshal()

2. C marshaller: gst_marshal_VOID__UINT_BOXED

3. class的成员函数:gst_type_find_element_have_type()【default handler】

 

 

connect signal注册的user级别的callback, 会存放到handler list中;

Connect Signal:

很多地方可以connect那个signal:

比如:gst-typefind.c

  g_signal_connect (G_OBJECT (typefind), "have-type",
      G_CALLBACK (have_type_handler), &caps);
#define g_signal_connect(instance, detailed_signal, c_handler, data) \
    g_signal_connect_data ((instance), (detailed_signal), (c_handler), (data), NULL, (GConnectFlags) 0)

这里注册C_handler, 可以说是上层用户自己的callback.

gulong
g_signal_connect_data (gpointer       instance,
		       const gchar   *detailed_signal,
		       GCallback      c_handler,
		       gpointer       data,
		       GClosureNotify destroy_data,
		       GConnectFlags  connect_flags)
{
  guint signal_id;
  gulong handler_seq_no = 0;
  GQuark detail = 0;
  GType itype;
  gboolean swapped, after;

  swapped = (connect_flags & G_CONNECT_SWAPPED) != FALSE;
  after = (connect_flags & G_CONNECT_AFTER) != FALSE;

  SIGNAL_LOCK ();
  itype = G_TYPE_FROM_INSTANCE (instance);
  signal_id = signal_parse_name (detailed_signal, itype, &detail, TRUE);
  if (signal_id)
    {
      SignalNode *node = LOOKUP_SIGNAL_NODE (signal_id);


	{
	  Handler *handler = handler_new (after);

	  handler_seq_no = handler->sequential_number;
	  handler->detail = detail;
	  handler->closure = g_closure_ref ((swapped ? g_cclosure_new_swap : g_cclosure_new) (c_handler, data, destroy_data));
	  g_closure_sink (handler->closure);
	  handler_insert (signal_id, instance, handler);
	  if (node->c_marshaller && G_CLOSURE_NEEDS_MARSHAL (handler->closure))
	    g_closure_set_marshal (handler->closure, node->c_marshaller);
	}
    }
  else
    g_warning ("%s: signal `%s' is invalid for instance `%p'", G_STRLOC, detailed_signal, instance);
  SIGNAL_UNLOCK ();

  return handler_seq_no;
}

1. 分配一个hanlder内存

2. 创建一个C closure, 并且绑定user 自己的callback

3. handler只有这个closure, 也就是拿住了用户的callback

4. 把hanlder, 插入到全局的handler list中去

5. 也把signal node的C marshal: gst_marshal_VOID__UINT_BOXED, 和handler的closure绑定。

此时:这个handler的closure掌握了:

1. User defined callback: have_type_handler()   【user defined handler】

2. C Marshal: gst_marshal_VOID__UINT_BOXED

 

从上面的2个步骤可以看出:

Signal的Class Closure 和Signal Handler的Closure的 C Marshaller

指向同一个:C Marshal: gst_marshal_VOID__UINT_BOXED

 

Signal Emit:

在某种条件下,会调用

  g_signal_emit (typefind, gst_type_find_element_signals[HAVE_TYPE], 0,
      probability, caps);

激发条件,暂时先不看。

 

看发射的过程,这里干了什么?如何和new/connect呼应起来?

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);
}
void
g_signal_emit_valist (gpointer instance,
		      guint    signal_id,
		      GQuark   detail,
		      va_list  var_args)
{
  GValue *instance_and_params;
  GType signal_return_type;
  GValue *param_values;
  SignalNode *node;
  guint i, n_params;
  
  g_return_if_fail (G_TYPE_CHECK_INSTANCE (instance));
  g_return_if_fail (signal_id > 0);

  SIGNAL_LOCK ();
  node = LOOKUP_SIGNAL_NODE (signal_id);
  if (!node || !g_type_is_a (G_TYPE_FROM_INSTANCE (instance), node->itype))
    {
      g_warning ("%s: signal id `%u' is invalid for instance `%p'", G_STRLOC, signal_id, instance);
      SIGNAL_UNLOCK ();
      return;
    }
#ifndef G_DISABLE_CHECKS
  if (detail && !(node->flags & G_SIGNAL_DETAILED))
    {
      g_warning ("%s: signal id `%u' does not support detail (%u)", G_STRLOC, signal_id, detail);
      SIGNAL_UNLOCK ();
      return;
    }
#endif  /* !G_DISABLE_CHECKS */

  /* optimize NOP emissions */
  if (signal_check_skip_emission (node, instance, detail))
    {
      /* nothing to do to emit this signal */
      SIGNAL_UNLOCK ();
      /* g_printerr ("omitting emission of \"%s\"\n", node->name); */
      return;
    }

  n_params = node->n_params;
  signal_return_type = node->return_type;
  instance_and_params = g_slice_alloc0 (sizeof (GValue) * (n_params + 1));
  param_values = instance_and_params + 1;

  for (i = 0; i < node->n_params; i++)
    {
      gchar *error;
      GType ptype = node->param_types[i] & ~G_SIGNAL_TYPE_STATIC_SCOPE;
      gboolean static_scope = node->param_types[i] & G_SIGNAL_TYPE_STATIC_SCOPE;

      SIGNAL_UNLOCK ();
      G_VALUE_COLLECT_INIT (param_values + i, ptype,
			    var_args,
			    static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
			    &error);
      if (error)
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);

	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occoured
	   */
	  while (i--)
	    g_value_unset (param_values + i);

	  g_slice_free1 (sizeof (GValue) * (n_params + 1), instance_and_params);
	  return;
	}
      SIGNAL_LOCK ();
    }
  SIGNAL_UNLOCK ();
  instance_and_params->g_type = 0;
  g_value_init (instance_and_params, G_TYPE_FROM_INSTANCE (instance));
  g_value_set_instance (instance_and_params, instance);
  if (signal_return_type == G_TYPE_NONE)
    signal_emit_unlocked_R (node, detail, instance, NULL, instance_and_params);
  else
    {
      GValue return_value = { 0, };
      gchar *error = NULL;
      GType rtype = signal_return_type & ~G_SIGNAL_TYPE_STATIC_SCOPE;
      gboolean static_scope = signal_return_type & G_SIGNAL_TYPE_STATIC_SCOPE;
      
      g_value_init (&return_value, rtype);

      signal_emit_unlocked_R (node, detail, instance, &return_value, instance_and_params);

      G_VALUE_LCOPY (&return_value,
		     var_args,
		     static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,
		     &error);
      if (!error)
	g_value_unset (&return_value);
      else
	{
	  g_warning ("%s: %s", G_STRLOC, error);
	  g_free (error);
	  
	  /* we purposely leak the value here, it might not be
	   * in a sane state if an error condition occured
	   */
	}
    }
  for (i = 0; i < n_params; i++)
    g_value_unset (param_values + i);
  g_value_unset (instance_and_params);
  g_slice_free1 (sizeof (GValue) * (n_params + 1), instance_and_params);
}

1. 把输入的C参数list放到一个GValue, Collect 中

2. 调用signal_emit_unblocked_R(), 并且GValue参数作为最后一个参数

3. 把返回的值再弄回C语言的形式。

 

下面重点看看:signal_emit_unblocked_R(),

在这个函数里面,会做如下的动作:

1. 找到class closure ( callback [default handler] + c_marshaller)

2. 找到别人注册到该信号的handlers (User callback + c_marshaller)

之后就是根据条件进行回调的处理:

有3个重要的条件:

1. if ((node->flags & G_SIGNAL_RUN_FIRST) && class_closure)  //这个处理class自己注册的handler, 一般是指定好的成员函数

    if ((node->flags & G_SIGNAL_RUN_LAST) && class_closure)

2. if (node->emission_hooks)

3. if (handler_list)   //这个处理用g_signal_connect_xxx()注册的用户自己的callback (user defined handlers)

 

先看第一个:

  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;
    }
void
g_closure_invoke (GClosure       *closure,
		  GValue /*out*/ *return_value,
		  guint           n_param_values,
		  const GValue   *param_values,
		  gpointer        invocation_hint)
{
  g_return_if_fail (closure != NULL);

  g_closure_ref (closure);      /* preserve floating flag */
  if (!closure->is_invalid)
    {
      GClosureMarshal marshal;
      gpointer marshal_data;
      gboolean in_marshal = closure->in_marshal;

      g_return_if_fail (closure->marshal || closure->meta_marshal);

      SET (closure, in_marshal, TRUE);
      if (closure->meta_marshal)
	{
	  marshal_data = closure->notifiers[0].data;
	  marshal = (GClosureMarshal) closure->notifiers[0].notify;
	}
      else
	{
	  marshal_data = NULL;
	  marshal = closure->marshal;
	}
      if (!in_marshal)
	closure_invoke_notifiers (closure, PRE_NOTIFY);
      marshal (closure,
	       return_value,
	       n_param_values, param_values,
	       invocation_hint,
	       marshal_data);
      if (!in_marshal)
	closure_invoke_notifiers (closure, POST_NOTIFY);
      SET (closure, in_marshal, in_marshal);
    }
  g_closure_unref (closure);
}

前面在New Signal的时候,就已经创建了class closure, 并且注册了:

marshal_data = gst_type_find_element_have_type()

mashal = g_type_class_meta_marshal()//这个mashal是我们指定的meta mashaller

所以会执行下面的:

      marshal (closure,
	       return_value,
	       n_param_values, param_values,
	       invocation_hint,
	       marshal_data);

即:

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);
}

这个函数非常有意思:

1. 根据传入的marshal_data ( gst_type_find_element_have_typed的地址),取出其offset

2. 根据offset, 取出对应class中的成员函数:callback = G_STRUCT_MEMBER (gpointer, class, offset);

    提取的结果:callback = gst_type_find_element_have_typed()

3. 在把这个callback作为最后一个参数传入到closure的C marshal: gst_marshal_VOID__UINT_BOXED

 真正执行的是下面的函数:

gst_marshal_VOID_UINT_BOXED(closure,
		      return_value,
		      n_param_values, param_values,
		      invocation_hint,
		      gst_type_find_element_have_typed);

 

在gstmarshal.c中:

/* VOID:UINT,BOXED (./gstmarshal.list:19) */
void
gst_marshal_VOID__UINT_BOXED (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__UINT_BOXED) (gpointer     data1,
                                                 guint        arg_1,
                                                 gpointer     arg_2,
                                                 gpointer     data2);
  register GMarshalFunc_VOID__UINT_BOXED callback;
  register GCClosure *cc = (GCClosure*) closure;
  register gpointer data1, data2;

  g_return_if_fail (n_param_values == 3);

  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__UINT_BOXED) (marshal_data ? marshal_data : cc->callback);

  callback (data1,
            g_marshal_value_peek_uint (param_values + 1),
            g_marshal_value_peek_boxed (param_values + 2),
            data2);
}

    {
      data1 = g_value_peek_pointer (param_values + 0);  //拿到输入参数list
      data2 = closure->data; // = (gpointer) itype, 在GClosure*g_closure_new_simple (guint           sizeof_closure,      gpointer        data)指定的
    }
  callback = (GMarshalFunc_VOID__UINT_BOXED) (marshal_data ? marshal_data : cc->callback);

  marshal_data != NULL, 所以这里的callback使用的是marshal_data: gst_type_find_element_have_typed

 

  gst_type_find_element_have_typed (参数GValue,
            g_marshal_value_peek_uint (param_values + 1),
            g_marshal_value_peek_boxed (param_values + 2),
            itype);

 

在gsttypefindelement.c

static void
gst_type_find_element_have_type (GstTypeFindElement * typefind,
    guint probability, const GstCaps * caps)
{
  GstCaps *copy;

  g_assert (caps != NULL);

  GST_INFO_OBJECT (typefind, "found caps %" GST_PTR_FORMAT ", probability=%u",
      caps, probability);

  GST_OBJECT_LOCK (typefind);
  if (typefind->caps)
    gst_caps_unref (typefind->caps);
  typefind->caps = gst_caps_copy (caps);
  copy = gst_caps_ref (typefind->caps);
  GST_OBJECT_UNLOCK (typefind);

  gst_pad_set_caps (typefind->src, copy);
  gst_caps_unref (copy);
}

 

      G_VALUE_LCOPY (&return_value,             var_args,             static_scope ? G_VALUE_NOCOPY_CONTENTS : 0,             &error);

这个是把GValue的值再转回C语言的形式。

 

gobjec相关学习文章的list.

pkg-config的用法 (zz)

pkg-config的用法

pkg-config

pkg-config程序是干什么用的?简单的说就是向用户向程序提供相应库的路径、版本号等信息的程序。
譬如说我们运行以下命令:
pkg-config 查看gcc的CFLAGS参数
$pkg-config --libs --cflags opencv
会显示如下信息:
-I/usr/include/opencv -lcxcore -lcv -lhighgui -lcvaux
各位看官,你看这不就是我们用gcc编译连接时CFLAGS的参数吗?
因此当我们需要编译连接某个库时,我们只需要把上面那行加入gcc 的参数里面即可。
这也是configure的作用,它会检查你需要的包,产生相应的信息。
pkg-config从哪儿知道这些信息的呢?它是从包名为xxx.pc这个文件中查找到的。拿上面那个例子说,它是从opencv.pc这个文件中查知的。
pkg-config 又怎么会知道opencv.pc这个文件呢?
下面我们看一下pkg-config是怎样工作的。
缺省情况下,pkg-config首 先在prefix/lib/pkgconfig/中查找相关包(譬如opencv)对应的相应的文件(opencv.pc)。在linux上上述路径名为 /usr/lib/pkconfig/。若是没有找到,它也会到PKG_CONFIG_PATH这个环境变量所指定的路径下去找。若是没有找到,它就会报 错,例如:
Package opencv was not found in the pkg-config search path.
Perhaps you should add the directory containing `opencv.pc'
to the PKG_CONFIG_PATH environment variable
No package 'opencv' found

设置环境变量PKG_CONFIG_PATH方法举例如下:
export PKG_CONFIG_PATH=/cv/lib:$PKG_CONFIG_PATH

================================================================
查看一个.pc文件的内容:
[root@yx pkgconfig]# cat glib-2.0.pc
prefix=/usr
exec_prefix=/usr
libdir=/lib
includedir=/usr/include
configexecincludedir=/usr/lib/glib-2.0/include

glib_genmarshal=glib-genmarshal
gobject_query=gobject-query
glib_mkenums=glib-mkenums

Name: GLib
Description: C Utility Library
Version: 2.12.3
Libs: -L${libdir} -lglib-2.0
Cflags: -I${includedir}/glib-2.0 -I${configexecincludedir}

[root@yx pkgconfig]# pwd
/usr/lib/pkgconfig

可见.pc文件 是对其的库文件路径,头文件路径,版本号,Cflags等一些参数进行封装。

再来看看第一个Gtk+程序里的
`pkg-config --cflags --libs gtk+-2.0`意思:
`pkg-config --cflags --libs gtk+-2.0` 是pkg-config从路径/usr/lib/pkgconfig
/gtk+-2.0.pc中提取出来的用于编译用的。

[root@yx pkgconfig]# cat gtk+-2.0.pc
prefix=/usr
exec_prefix=/usr
libdir=/usr/lib
includedir=/usr/include
target=x11

gtk_binary_version=2.10.0
gtk_host=i686-redhat-linux-gnu

Name: GTK+
Description: GIMP Tool Kit (${target} target)
Version: 2.10.4
Requires: gdk-${target}-2.0 atk cairo
Libs: -L${libdir} -lgtk-${target}-2.0
Cflags: -I${includedir}/gtk-2.0

显然,出可以自己来指定为:-L/usr/lib -lgtk-{target}-2.0 -I/usr/include/gtk-2.0
下面来看一下{target}该是多少:

[root@yx lib]# ls gt
gthumb/ gtk-2.0/ gtkhtml/
gtk/ gtk-sharp-2.0/ gtkmm-2.4/

[root@yx lib]# ls gtk-2.0/
2.10.0 2.4.0 immodules include modules

[root@yx lib]# ls gtk-sharp-2.0/
gconfsharp-schemagen.exe

[root@yx lib]# pwd
/usr/lib
所以认为-lgtk-{target}-2.0中的{target}该是空字符:
-lgtk-{target}-2.0====>-lgtk--2.0

At Last So:(理论大致上:)

-L/usr/lib -lgtk-{target}-2.0 -I/usr/include/gtk-2.0 ====>
-L/usr/lib -lgtk--2.0 -I/usr/include/gtk-2.0
而实际上更多些:
对比pkg-config对gtk+-2.0看实际效果:

[yuxu@yx base]$ pkg-config --cflags --libs gtk+-2.0
-I/usr/include/gtk-2.0 -I/usr/lib/gtk-2.0/include -I/usr/include/atk-1.0 -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/include/freetype2 -I/usr/include/libpng12 -L/lib -lgtk-x11-2.0 -lgdk-x11-2.0 -latk-1.0 -lgdk_pixbuf-2.0 -lm -lpangocairo-1.0 -lpango-1.0 -lcairo -lgobject-2.0 -lgmodule-2.0 -ldl -lglib-2.0
后面还有很多的路径哦。

 

zz PKG_CONFIG_PATH, pkg-config, configure

总结configure,pkg-config和PKG_CONFIG_PATH zz

我想大家都在linux下用源码安装过软件,源码安装软件的第一步是啥?下载源码,没错,小王,你太有才了..

其实真正的第一步是./configure.似曾相识哦。没错,那么我也时常见过pkg-config及pkg_config_path.这几这到底啥关系呢,这就是今天的主题。

1.什么是configure

configure会根据传入的配置项目检查程序编译时所依赖的环境以及对程序编译安装进行配置,最终生成编译所需的Makefile文件供程序Make读入使用进而调用相关编译程式(通常调用编译程序都是gcc)来编译最终的二进制程序。而configure脚本在检查相应依赖环境时(例:所依赖软件的版本、相应库版本等),通常会通过pkg-config的工具来检测相应依赖环境。

2.什么是pkg-config

pkg-config用来检索系统中安装库文件的信息,典型的是用作库的编译和连接。一般来说,如果库的头文件不在/usr/include目录中,那么在编译的时候需要用-I参数指定其路径。由于同一个库在不同系统上可能位于不同的目录下,用户安装库的时候也可以将库安装在不同的目录下,所以即使使用同一个库,由于库的路径的不同,造成了用-I参数指定的头文件的路径和在连接时使用-L参数指定lib库的路径都可能不同,其结果就是造成了编译命令界面的不统一。可能由于编译,连接的不一致,造成同一份程序从一台机器copy到另一台机器时就可能会出现问题。
pkg-config 就是用来解决编译连接界面不统一问题的一个工具。基本思想:pkg-config是通过库提供的一个.pc文件获得库的各种必要信息的,包括版本信息、编译和连接需要的参数等。需要的时候可以通过pkg-config提供的参数(–cflags, –libs),将所需信息提取出来供编译和连接使用。这样,不管库文件安装在哪,通过库对应的.pc文件就可以准确定位,可以使用相同的编译和连接命令,使得编译和连接界面统一。它提供的主要功能有:

<1> 检查库的版本号。如果所需库的版本不满足要求,打印出错误信息,避免连接错误版本的库文件。
<2> 获得编译预处理参数,如宏定义,头文件的路径。
<3> 获得编译参数,如库及其依赖的其他库的位置,文件名及其他一些连接参数。
<4> 自动加入所依赖的其他库的设置。

在默认情况下,每个支持 pkg-config 的库对应的.pc文件在安装后都位于安装目录中的lib/pkgconfig目录下.新软件一般都会安装.pc文件,没有可以自己创建,并且设置环境变量PKG_CONFIG_PATH寻找.pc文件路径,否则怎么找得到呢。使用pkg-config工具提取库的编译和连接参数有两个基本的前提:

<1> 库本身在安装的时候必须提供一个相应的.pc文件。不这样做的库说明不支持pkg-config工具的使用。
<2> pkg-config必须知道要到哪里去寻找此.pc 文件。

3.PKG_CONFIG_PATH.

上边的第二个基本条件就是设置这个环境变量了。环境变量PKG_CONFIG_PATH是用来设置.pc文件的搜索路径的,pkg-config按照设置路径的先后顺序进行搜索,直到找到指定的.pc 文件为止。这样,库的头文件的搜索路径的设置实际上就变成了对.pc文件搜索路径的设置。在安装完一个需要使用的库后,比如Glib,一是将相应的.pc文件,如glib-2.0.pc拷贝到/usr/lib/pkgconfig目录下,二是通过设置环境变量PKG_CONFIG_PATH添加glib-2.0.pc文件的搜索路径。
这样设置之后,使用Glib库的其它程序或库在编译的时候pkg-config就知道首先要到/opt/gtk/lib/pkgconfig这个目录中去寻找glib-2.0.pc了(GTK+和其它的依赖库的.pc文件也将拷贝到这里,也会首先到这里搜索它们对应的.pc文件)。之后,通过pkg-config就可以把其中库的编译和连接参数提取出来供程序在编译和连接时使用。另外还需要注意的是:环境变量的这种设置方式只对当前的终端窗口有效。如果到了没有进行上述设置的终端窗口中,pkg-config将找不到新安装的glib-2.0.pc文件、从而可能使后面进行的安装(如Glib之后的Atk的安装)无法进行。
  在我们采用的安装方案中,由于是使用环境变量对GTK+及其依赖库进行的设置,所以当系统重新启动、或者新开一个终端窗口之后,如果想使用新安装的GTK+库,需要如上面那样重新设置PKG_CONFIG_PATH和LD_LIBRARY_PATH环境变量。
  这种使用GTK+的方法,在使用之前多了一个对库进行设置的过程。虽然显得稍微繁琐了一些,但却是一种最安全的使用GTK+库的方式,不会对系统上已经存在的使用了GTK+库的程序(比如GNOME桌面)带来任何冲击。