参考网址(GLib Reference Manual):
http://developer.gnome.org/glib/2.28/
(By Ksir 2011-4-29)
一、什么是glib库
glib库是linux平台下最常用的c语言函数库,它具有很好的可移植性和实用性。glib是gtk+库和gnome的基础。glib的各种实用程序具有一致的接口。glib为许多标准的、常用的C语言结构提供了相应的替代物。它的编码风格是半面向对象,标识符加了一个前缀“g”,这也是一种通行的命名约定。使glib库的程序都应该包含glib的头文件glib.h。如果程序已经包含了gtk .h则不需要再包含glib.h。
二、glib的类型定义
glib的类型定义不是使用C的标准类型,它自己有一套类型系统。它们比常用的C语言的类型更丰富,也更安全可靠。引进这套系统是为了多种原因。
以下是glib基本类型定义:
整数类型:gint8、guint8、gint16、guint16、gint32、guint32、gint64、guint64。其中gint8是8位的整数,guint8是8位的无符号整数,其他依此类推。这些整数类型能够保证大小。不是所有的平台都提供64位整型,如果一个平台有这些, glib会定义G_HAVE_GINT64。
整数类型gshort、glong、gint和short、long、int完全等价。
布尔类型gboolean:它可使代码更易读,因为普通C没有布尔类型。Gboolean可以取两个值:TRUE和FALSE。实际上FALSE定义为0,而TRUE定义为非零值。
字符型gchar和char完全一样,只是为了保持一致的命名。
浮点类型gfloat、gdouble和float、double完全等价。
指针gpointer对应于标准C的void *,但是比void *更方便。
指针gconst pointer对应于标准C的const void *(注意,将const void *定义为const gpointer是行不通的)。
三、内存管理
glib用自己的g_变体包装了标准的malloc( )和free( ),即g_malloc() 和g_free( )。它们有以下几个小优点:
• g_malloc()总是返回gpointer,而不是char *,所以不必转换返回值。
• 如果低层的malloc ( )失败,g_malloc ( )将退出程序,所以不必检查返回值是否是NULL。
• g_malloc() 对于分配0字节返回NULL。
• g_free()忽略任何传递给它的NULL指针。
四、Glib编译
GLib的最新版本是GLib2.2.1,可以到www.gtk.org网站下载其源代码。使用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` |
五、对核心应用的支持
(来自http://www.ibm.com/developerworks/cn/linux/l-glib/index.html)
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 |
下面为输出结果:
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,而是把它放在线程的函数中了,这一点需读者注意。