GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库,
它提供C语言的常用的数据结构的定义、相关的处理函数,有趣而实用的宏,
可移植的封装和一些运行时机能,如事件循环、线程、动态调用、对象系统等的API。
它能够在类UNIX的操作系统平台(如LINUX, HP-UNIX等),WINDOWS,OS2和BeOS等操作系统台上运行。
一、GLib在CentOS上的安装
检查系统当前的版本
# rpm -qi glibc
Name : glibc Relocations: (not relocatable)
Version : 2.5 Vendor: Red Hat, Inc.
Release : 49 Build Date: Wed 10 Mar 2010 07:01:43 PM CST
Install Date: Wed 01 Jun 2011 05:50:51 PM CST Build Host: x86-005.build.bos.redhat.com
Group : System Environment/Libraries Source RPM: glibc-2.5-49.src.rpm
Size : 11658546 License: LGPL
Signature : DSA/SHA1, Thu 18 Mar 2010 01:18:05 AM CST, Key ID 5326810137017186
Packager : Red Hat, Inc. < />
Summary : The GNU libc libraries.
Description :
The glibc package contains standard libraries which are used by
multiple programs on the system. In order to save disk space and
memory, as well as to make upgrading easier, common system code is
kept in one place and shared between programs. This particular package
contains the most important sets of shared libraries: the standard C
library and the standard math library. Without these two libraries, a
Linux system will not function.
显示系统的glibc库版本为2.5。
在CentOS中用更新命令到2.5已经是最新版本了,只好手动下载 glibc的源码包编译安装更新。
可以在 看到当前的最新版本,
我看到的最新版本是glibc-2.22。
# cd /usr/src
# mkdir glibc-2.22-build
# wget glibc-2.22.tar.gz
# tar zxvf glibc-2.22.tar.gz
# cd /usr/src/glibc-2.22-build/
# export CFLAGS="-g -O2 -march=i686"
网上有些地方加优化开关 -O2 时(大写字母O),写的是 -02 (数字零),
所以拷贝过来仍然出现3中的错误,所以请注意。
# /usr/src/glibc-2.22-build/glibc-2.22/configure --prefix=/usr/src/glibc-2.22-build/ --with-headers=/usr/include
# make
# make install
注意:
1.在目录usr/src/glibc-2.22执行configure命令时不能执行,需在外部文件夹glibc-2.22-build 执行
2.加上优化开关,export CFLAGS="-g -O2 -march=i686",否则会出现错误,此处的i686请参照自己的机器
出错与解决:
1. 运行configure时报错:
# /usr/src/glibc-2.22-build/glibc-2.22/configure --prefix=/usr/src/glibc-2.22-build/ --with-headers=/usr/include
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for gcc... gcc
checking for suffix of object files... configure: error: in `/usr/src/glibc-2.22-build/glibc-2.22':
configure: error: cannot compute suffix of object files: cannot compile
See `config.log' for more details
解决方案:
是本机的GCC没有安装或配置好,需要重新安装和配置GCC;
2. 运行configure时报错:
checking build system type... x86_64-unknown-linux-gnu
checking host system type... x86_64-unknown-linux-gnu
checking for gcc... gcc
checking for suffix of object files... o
checking whether we are using the GNU C compiler... yes
checking whether gcc accepts -g... yes
checking for readelf... readelf
checking for g++... g++
checking whether we are using the GNU C++ compiler... yes
checking whether g++ accepts -g... yes
checking whether g++ can link programs... yes
checking for sysdeps preconfigure fragments... aarch64 alpha arm hppa i386 m68k microblaze mips nacl nios2 powerpc s390 sh sparc tile x86_64 checking whether gcc compiles in -mx32 mode by default... no
configure: running configure fragment for add-on libidn
checking for assembler and linker STT_GNU_IFUNC support... no
checking whether .text pseudo-op must be used... yes
checking sysdep dirs... sysdeps/unix/sysv/linux/x86_64/64 sysdeps/unix/sysv/linux/x86_64 sysdeps/unix/sysv/linux/x86 sysdeps/unix/sysv/linux/wordsize-64 sysdeps/x86_64/nptl sysdeps/unix/sysv/linux sysdeps/nptl sysdeps/pthread sysdeps/gnu sysdeps/unix/inet sysdeps/unix/sysv sysdeps/unix/x86_64 sysdeps/unix sysdeps/posix sysdeps/x86_64/64 sysdeps/x86_64/fpu sysdeps/x86/fpu sysdeps/x86_64 sysdeps/x86 sysdeps/ieee754/ldbl-96 sysdeps/ieee754/dbl-64/wordsize-64 sysdeps/ieee754/dbl-64 sysdeps/ieee754/flt-32 sysdeps/wordsize-64 sysdeps/ieee754 sysdeps/generic
checking for a BSD-compatible install... /usr/bin/install -c
checking whether ln -s works... yes
checking whether as is GNU as... yes
checking whether ld is GNU ld... yes
checking for as... as
checking version of as... 2.17.50.0.6, bad
checking for ld... ld
checking version of ld... 2.17.50.0.6, bad
checking for gnumake... no
checking for gmake... gmake
checking version of gmake... 3.81, ok
checking for gnumsgfmt... no
checking for gmsgfmt... no
checking for msgfmt... msgfmt
checking version of msgfmt... 0.17, ok
checking for makeinfo... makeinfo
checking version of makeinfo... 4.8, ok
checking for sed... sed
checking version of sed... 4.1.5, ok
checking for gawk... gawk
checking version of gawk... 3.1.5, ok
checking if gcc is sufficient to build libc... no
checking for nm... nm
configure: error:
*** These critical programs are missing or too old: as ld compiler
*** Check the INSTALL file for required versions.
解决办法:
问题原因:
缺少autoconf或版本太低
下载安装新版本
# wget ftp://ftp.gnu.org/gnu/autoconf/autoconf-2.68.tar.gz
# tar zxvf autoconf-2.68.tar.gz
# cd autoconf-2.68
# ./configure --prefix=/usr/
# make && make install
查看当前版本:
# autoconf -V
autoconf (GNU Autoconf) 2.68
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+/Autoconf: GNU GPL version 3 or later
< < />
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by David J. MacKenzie and Akim Demaille.
3. 运行configure时报错::
checking whether ranlib is necessary... no
checking LD_LIBRARY_PATH variable... contains current directory
configure: error:
*** LD_LIBRARY_PATH shouldn't contain the current directory when
*** building glibc. Please change the environment variable
*** and run configure again.
解决办法:
查看LD_LIBRARY_PATH echo $LD_LIBRARY_PATH,我的打印的是/usr/lib/qt44/lib:
configure里说:LD_LIBRARY_PATH不能以终结符作为开始和最后一个字符,
不能有2个终结符连在一起,所以修改下LD_LIBRARY_PATH即可,export LD_LIBRARY_PATH=/usr/lib/qt44/lib
4.当有如下错误时:
configure: error: gcc must provide the header
解决办法:
get the cpuid.h file from official GCC respositories>
()> and put it into /usr/include(上面指定的--with-headers)
“locate cpuid”,其中可以发现“/usr/lib/gcc/i686-redhat-linux/4.7.0/include/cpuid.h”。打开该文件
5. 当以上问题都解决时,make make install吧,由于东西比较多,需要些时间,大概半小时左右。
二、Glibc与GCC
glibc是什么,以及与gcc的关系?
glibc是gnu发布的libc库,也即c运行库。
glibc是linux 系统中最底层的api(应用程序开发接口),几乎其它任何的运行库都会倚赖于glibc。
glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现,
主要的如下:
(1)string, 字符串处理
(2)signal, 信号处理
(3)dlfcn, 管理共享库的动态加载
(4)direct, 文件目录操作
(5)elf, 共享库的动态加载器,也即interpreter
(6)iconv, 不同字符集的编码转换
(7)inet, socket接口的实现
(8)intl, 国际化,也即gettext的实现
(9)io
(10)linuxthreads
(11)locale,本地化
(12)login, 虚拟终端设备的管理,及系统的安全访问
(13)malloc,动态内存的分配与管理
(14)nis
(15)stdlib,其它基本功能
gcc 是编译器,基本上 Linux 下所有的程序(包括内核)都是 gcc 编译的,libc 当然也是。
gcc 和 libc 是互相依赖的两个软件,它们合作的方式类似 Linux 系统的 "自举"。
先在一个可以运行的带有老 libc 和 gcc 的系统上,用老 gcc 编译出一个新版本的 gcc + 老 libc,
再用这个新 gcc 编译出一个新 gcc + 新 libc,再用这套东东编译整个新系统。
glibc版本查看, 可使用命令:
# ldd --version
ldd (GNU libc) 2.5
Copyright (C) 2006 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
三、GLib简析
GLib需要一个支持线程的操作系统和一个字符集间转换函数iconv的支持,事实上大多现代的操作系统都有以上两项功能。
GLib由基础类型、对核心应用的支持、实用功能、数据类型和对象系统五个部分组成的。
GLib的最新版本可以到网站下载其源代码。
使用GLib2.0编写的应用程序,在编译时应该在编译命令中加入`pkg-config -cflags -libs glib-2.0`,
如编译一个名为hello.c的程序,输出名为hello的可执行文件,则命令为:
# gcc `pkg-config -cflags -libs glib-2.0` hello.c -o hello
在GLIB中将线程(gthread),插件(gmoudle)和对象系统(gobject)这三个子系统区别对待,编译时要注意加入相应的参数。
如程序中用到对象系统,编译时就应加入:
`pkg-config --cflags --libs gobject-2.0`
用到线程,编译时则加入:
`pkg-config --cflags --libs gthread-2.0`
用到插件,编译时则加入:
`pkg-config --cflags --libs gmoudle-2.0`
3.1 基础类型
GLib的基础是由:
基础类型、
范围限定宏、
标准宏、
类型转换宏、
字节次序变换宏、
数学常数定义和
杂项宏等各项组成的。
这里主要介绍基础类型,因为它们遍及与GLIB相关的各种程序库和软件包中,如GTK+,GNOME,MONO等大的开源项目。
基础类型又称标准类型,GLib将C语言中的数据类型统一封装成自己的数据类型,均以小写字母'g'开头,
如:gpointer是指针类型(void *)、guint是无符号整型(unsigned int)等,
其中有一些是修饰性的,如:gint、gchar等,它们和C语言中的int、char是完全相同的。
这些数据类型使用起来和C语言中的数据类型完全一样,当你熟悉了以后会发现它们的使用方法更灵活,更直观也更易于理解一些。
当然你可以把C语言中的数据类型直接拿来使用,这丝毫不影响你编写程序的编译。
另外范围限定宏和类型转换宏也较常用,如:
宏G_MAXINT表示最大的int型值,
宏GINT_TO_POINTER(i)将整型变量i转换为指针型,
宏GPOINTER_TO_INT(p)将指针型变量p转换为整型。
逻辑类型gboolean的值TRUE和FALSE是在常数宏中定义的,
另外还包括
G_E表示自然对数,
G_PI表示圆周率,
G_PI_2表示圆周率的1/2等一些数学常数。
3.2 对核心应用的支持
GLib对核心应用的支持包括事件循环、内存操作、线程操作、动态链接库的操作和出错处理与日志等。
下面代码演示了事件循环、内存操作、线程这三种功能的简单应用:
#include <glib.h>
static GMutex *mutex = NULL;
static gboolean t1_end = FALSE;
static gboolean t2_end = FALSE;
typedef struct _Arg Arg;
struct _Arg
{
GMainLoop* loop;
gint max;
};
void run_1(Arg *arg)
{
int i ;
for(i=0; i<arg->max; i++)
{
if(g_mutex_trylock(mutex) == FALSE)
{
//g_print("%d : thread 2 locked the mutex \n", i);
g_print("%d :线程2锁定了互斥对象\n", i);
g_mutex_unlock(mutex);
}
else
{
g_usleep(10);
}
}
t1_end = TRUE;
}
void run_2(Arg *arg)
{
int i;
for(i=0; i<arg->max; i++)
{
if(g_mutex_trylock(mutex) == FALSE)
{
//g_print("%d : thread 1 locked mutex \n", i);
g_print("%d :线程1锁定了互斥对象\n", i);
g_mutex_unlock(mutex);
}
else
{
g_usleep(10);
}
}
t2_end = TRUE;
}
void run_3(Arg *arg)
{
for(;;)
{
if(t1_end && t2_end)
{
g_main_loop_quit(arg->loop);
break;
}
}
}
int main(int argc, char *argv[])
{
GMainLoop *mloop;
Arg *arg;
if(!g_thread_supported())
g_thread_init(NULL);
mloop = g_main_loop_new(NULL, FALSE);
arg = g_new(Arg, 1);
arg->loop = mloop;
arg->max = 11;
mutex = g_mutex_new();
g_thread_create(run_1, arg, TRUE, NULL);
g_thread_create(run_2, arg, TRUE, NULL);
g_thread_create(run_3, arg, TRUE, NULL);
g_main_loop_run(mloop);
g_print("线程3退出事件循环\n");
g_mutex_free(mutex);
g_print("释放互斥对象\n");
g_free(arg);
g_print("释放参数所用的内存\n");
}
Makefile文件如下:
CC = gcc
all:
$(CC) `pkg-config --cflags --libs glib-2.0 gthread-2.0` loop.c -o loop
运行:
# ./loop 10
下面为输出结果:
0 :线程1锁定了互斥对象
1 :线程2锁定了互斥对象
2 :线程1锁定了互斥对象
3 :线程2锁定了互斥对象
4 :线程1锁定了互斥对象
5 :线程2锁定了互斥对象
6 :线程1锁定了互斥对象
7 :线程2锁定了互斥对象
8 :线程1锁定了互斥对象
9 :线程2锁定了互斥对象
10 :线程1锁定了互斥对象
线程3退出事件循环
释放互斥对象
释放参数所用的内存
以上例程创建了三个线程,
其中run_1和run_2操作互斥对象,run_3检索前两个线程是否结束,
如结束的话,则执行g_main_loop_quit退出事件循环。
由于线程的运行是不确定的,所以不一定每次都是这一输出结果。
首先,定义一个结构类型来保存创建的事件循环的对象指针和线程运行时的最多循环次数,
一般情况下:
如果为此数据结构来分配内存的话,用Arg *arg = (Arg *)malloc(sizeof(Arg));
释放时用free(arg);
这种传统的做法曾经让很多C语言的初学者头痛,尤其是需要多次操作的时候,
GLib中提供了类似的函数g_malloc和g_free,最好用的方法是其将g_malloc函数封装成了:
宏g_new,这个宏有两个参数,第一个是结构类型,第二个是要分配结构的数量,
这段代码中只用到了一个Arg数据结构,所以是g_new(Arg, 1)。在程序结束时用g_free来释放。
在线程初始化时,首先是判断线程是否初始化的函数g_thread_supported,
如果其返回FALSE则表明线程并未初始化,这时必须用g_thread_init来初始化,这是较明智的做法。
事件循环GMainLoop
在用g_main_loop_new创建之后并不马上运行,
用g_main_loop_run运行后,
还要用g_main_loop_quit退出,
否则循环将一直运行下去,
这两个函数的参数都是GMainLoop型的指针,在主函数中并未直接运行g_main_loop_quit,
而是把它放在线程的函数中了,这一点需读者注意。
3.3 实用功能
GLib中包含了近二十种实用功能,从简单的字符处理到初学者很难理解的XML解析功能,
这里介绍两种较简单的:随机数和计时。
下面代码演示如何产生1-100之间的随机整数和演示如何计算30000000次累加在计算时用的时间:
/* until.c 用来测试实用功能 */
#include <glib.h>
int main(int argc, char *argv[])
{
GRand *rand;
GTimer *timer;
gint n;
gint i, j;
gint x = 0;
rand = g_rand_new(); //创建随机数对象
for(n=0; n<20; n++)
{ //产生随机数并显示出来
g_print("%d\t",g_rand_int_range(rand,1,100));
}
g_print("\n");
g_rand_free(rand); //释放随机数对象
//创建计时器
timer = g_timer_new();
g_timer_start(timer);//开始计时
for(i=0; i<10000; i++)
for(j=0; j<3000; j++)
x++;//累计
g_timer_stop(timer);//计时结束
//输出计时结果
g_print("%ld\tall:%.2f seconds was used!\n",x,g_timer_elapsed(timer,NULL));
}
Makefile文件内容如下:
CC = gcc all: $(CC) `pkg-config --cflags --libs glib-2.0 ` until.c -o until
输出结果:
48 95 95 99 90 24 90 29 78 4 53 87 1 86 7 93 57 88 75 4
30000000 all:1.47 seconds was used!
LIB中的每个对象几乎都有一个或多个*_new函数来创建,计时器GTimer和随机器GRand也一样,
也都有相对应的函数来结束对象的使用,如GTimer的g_timer_stop和GRand的g_rand_free。
这可能是GLIB实用功能中最简单的两种了,许多朋友会一目了然。
我们还应注意到GLIB的代码风格和封装技巧是具有独到之处的,这种风格和技巧足以让一些自称简洁实用的SDK汗颜,
学习掌握这一风格可能会让我们受益匪浅。
3.4 数据类型
GLib中定义了十几种常用的数据结构类型和它们的相关操作函数,下面是关于字符串类型的简单示例:
#include <glib.h>
int main(int argc, char *argv[])
{
GString *s;
s = g_string_new("Hello");
g_print("%s\n", s->str);
s = g_string_append(s," World!");
g_print("%s\n",s->str);
s = g_string_erase(s,0,6);
g_print("%s\n",s->str);
s = g_string_prepend(s,"Also a ");
g_print("%s\n",s->str);
s = g_string_insert(s,6," Nice");
g_print("%s\n",s->str);
}
Makefile文件如下:
CC = gcc
all:
$(CC) `pkg-config --cflags --libs glib-2.0 ` string.c -o str
下面是输出结果:
Hello
Hello World!
World!
Also a World!
Also a Nice World!
字符串在编程中出现频率之高,即使是初学者也很清楚,追加、删除和插入等常用操作理解后,
还可以进一步了解掌握其它更复杂的操作。
GLib提供了一种内存块(GMemChunk)数据类型,它为分配等大的内存区提供了一种非常好用的操作方式,
下面程序演示了内存块数据类型的简单用法:
#include <glib.h>
int main(int argc, char *argv[])
{
GMemChunk *chunk; //定义内存块
gchar *mem[10]; //定义指向原子的指针数组
gint i, j;
//创建内存块
chunk = g_mem_chunk_new("Test MemChunk", 5, 50, G_ALLOC_AND_FREE);
//名称,原子的长度, 内存块的长度,类型
for(i=0; i<10; i++)
{
//创建对象
//mem[i] = g_chunk_new(gchar, chunk);
mem[i] = (gchar*)g_mem_chunk_alloc(chunk);
for(j=0; j<5; j++)
{
mem[i][j] = 'A' + j;//为内存块中的指针赋值
}
}
g_mem_chunk_print(chunk); //显示内存块信息
for(i=0; i<10; i++)
{
g_print("%s\t",mem[i]);//显示内存块中的内容
}
for(i=0; i<10; i++)
{
g_mem_chunk_free(chunk,mem[i]); //释放所有分配的内存
}
g_mem_chunk_destroy(chunk);
}
Makefile文件件如下:
CC = gcc
all:
$(CC) `pkg-config --cflags --libs glib-2.0` data1.c -o data1
以下为输出结果:
GLib-INFO: Test MemChunk: 80 bytes using 2 mem areas
ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE ABCDE
这里说明这一数据类型的原因是通过它可以他细体会内存分配这一运行时处理环节的应用之妙。
我们在程序中分配的是50字节的空间,而实际用的是80字节,由此可以看出其在分配内存时本身用到了部分内存空间。
从上面的示例代码中可以看出,在GLib中几乎所有的对象都是C语言的结构类型,一般命名以大写字母G开头的单词,
如GList表示双向链表,所有与之相关的操作函数都以小写的字母g加下划线加小写的单词加下划线开头,
如以g_list_*开头的函数都是与这相关的操作函数,而且这些函数中的第一个参数多数是此对象的指针。
GLIB中的数据类型在GLIB本身,尤其是GTK+中频繁用到,了解掌握这些数据类据类型的用法是非常必要的,
这对进一步灵活开发GTK+程序来说是关键一环,而且是对大学中的《数据结构》一科的很好回顾。
在下一篇GOBJECT对象系统中将详细介绍GLIB中最重要的组成部分GOBJECT系统,
希望这一由C语言组建的单继承的对象系统会帮助你走进GTK+的世界。
参考资料
GLib的在线文档: http://developer.gnome.org/doc/API/2.0/glib/index.html
阅读(17747) | 评论(0) | 转发(0) |