GMutex / GCond

下面两个函数:
void g_cond_signal (GCond *cond);
void g_cond_wait (GCond *cond, GMutex *mutex);
 

用于进行线程同步。
mutex 是“mutual exclusion”(互斥)的英文缩写,用来保证线程对于共享数据的独占访问。

 

GMutex *mutex; // 互斥变量

GCond *cond; //等待条件

 

CRITICAL_SECTION 属于轻量级的线程同步对象,相对于mutex来说,它的效率会高很多。

mutex可以用于进程之间的同步,CRITICAL_SECTION只在同一个进程有效。

 

同一进程可以包括多个线程,这些线程共享相同的内存空间,而进程都有各自独立的内存空间,进程之间通信需要专门的机制,这无疑增加了内核的开销,降低了系 统性能。线程带来的开销很小,内核无需单独复制进程的内存空间或文件描述符等,这就大量地节省了CPU时间,使得创建线程比进程的速度快数十倍。另外,多 线程程序作为一种多任务、并发的工作方式,还有以下的优点:1)提高应用程序响应时间;2)使多CPU系统更加有效;3)改善程序结构。
首先我们理清一下Pthread和Gthread的区别。Pthread即POSIX thread,Posix线程是一个POSIX标准线程,该标准定义内部API创建和操纵线程。Gthread调用的是Glib库中的线程部分;GLib是GTK+和GNOME工程的基础底层核心程序库,是一个综合用途的实用的轻量级的C程序库。 本软件是带有界面的,并且是GTK+界面,因此,Gthread是最好的选择。
引入Gthread线程的文件在编译时要加入参数`pkg-config --cflags --libs gthread-2.0`。
(1)gboolean g_thread_supported();/*测试是否支持多线程*/
(2)void g_thread_init (GThreadFunctions *vtable);/*初始化多线程支持*/
(3)void gdk_threads_init (void);/*初始化GDK多线程支持*/
(4)void gdk_threads_enter (void);/*进入多线程互斥区域*/
(5)void gdk_threads_leave (void);/*退出多线程互斥区域*/
(6)GThread * g_thread_create (GThreadFunc func, gpointer data, gboolean joinable, GError **error);
这是创建线程函数,func是线程执行的外部函数,data是传给该外部函数的参数,joinable标志线程是否可分离,error是出错代码返回地址。
(7)void g_thread_exit (gpointer retval);/*线程退出,retval为返回状态值*/
(8)GMutex *g_mutex_new ();/*返回一个新的互斥锁*/
(9) void g_mutex_lock(GMutex *mutex);/*上锁*/
(10)void g_mutex_unlock (GMutex *mutex);/*解锁*/
(11)GCond* g_cond_new ();/*返回一个新的信号量*/
(12)void g_cond_signal (GCond *cond);/*释放信号量cond*/
(13)void g_cond_wait(GCond *cond, GMutex *mutex);/*等待信号量cond*/

 

#define g_mutex_new()            G_THREAD_UF (mutex_new,      ())
#define G_THREAD_UF(op, arglist)					\
    (*g_thread_functions_for_glib_use . op) arglist

#define g_mutex_new()            (*g_thread_functions_for_glib_use.mutex_new())

 

#define g_cond_new()             G_THREAD_UF (cond_new,       ())

#define g_cond_new()             (*g_thread_functions_for_glib_use.cond_new())

 

typedef gpointer (*GThreadFunc) (gpointer data);

typedef enum
{
  G_THREAD_PRIORITY_LOW,
  G_THREAD_PRIORITY_NORMAL,
  G_THREAD_PRIORITY_HIGH,
  G_THREAD_PRIORITY_URGENT
} GThreadPriority;

typedef struct _GThread         GThread;
struct  _GThread
{
  /*< private >*/
  GThreadFunc func;
  gpointer data;
  gboolean joinable;
  GThreadPriority priority;
};

typedef struct _GMutex          GMutex;
typedef struct _GCond           GCond;
typedef struct _GPrivate        GPrivate;
typedef struct _GStaticPrivate  GStaticPrivate;

typedef struct _GThreadFunctions GThreadFunctions;
struct _GThreadFunctions
{
  GMutex*  (*mutex_new)           (void);
  void     (*mutex_lock)          (GMutex               *mutex);
  gboolean (*mutex_trylock)       (GMutex               *mutex);
  void     (*mutex_unlock)        (GMutex               *mutex);
  void     (*mutex_free)          (GMutex               *mutex);
  GCond*   (*cond_new)            (void);
  void     (*cond_signal)         (GCond                *cond);
  void     (*cond_broadcast)      (GCond                *cond);
  void     (*cond_wait)           (GCond                *cond,
                                   GMutex               *mutex);
  gboolean (*cond_timed_wait)     (GCond                *cond,
                                   GMutex               *mutex,
                                   GTimeVal             *end_time);
  void      (*cond_free)          (GCond                *cond);
  GPrivate* (*private_new)        (GDestroyNotify        destructor);
  gpointer  (*private_get)        (GPrivate             *private_key);
  void      (*private_set)        (GPrivate             *private_key,
                                   gpointer              data);
  void      (*thread_create)      (GThreadFunc           func,
                                   gpointer              data,
                                   gulong                stack_size,
                                   gboolean              joinable,
                                   gboolean              bound,
                                   GThreadPriority       priority,
                                   gpointer              thread,
                                   GError              **error);
  void      (*thread_yield)       (void);
  void      (*thread_join)        (gpointer              thread);
  void      (*thread_exit)        (void);
  void      (*thread_set_priority)(gpointer              thread,
                                   GThreadPriority       priority);
  void      (*thread_self)        (gpointer              thread);
  gboolean  (*thread_equal)       (gpointer              thread1,
				   gpointer              thread2);
};

 

定义一些宏来访问这些成员变量:

/* shorthands for conditional and unconditional function calls */

#define G_THREAD_UF(op, arglist)					\
    (*g_thread_functions_for_glib_use . op) arglist
#define G_THREAD_CF(op, fail, arg)					\
    (g_thread_supported () ? G_THREAD_UF (op, arg) : (fail))
#define G_THREAD_ECF(op, fail, mutex, type)				\
    (g_thread_supported () ? 						\
      ((type(*)(GMutex*, const gulong, gchar const*))			\
      (*g_thread_functions_for_glib_use . op))				\
     (mutex, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : (fail))

#ifndef G_ERRORCHECK_MUTEXES
# define g_mutex_lock(mutex)						\
    G_THREAD_CF (mutex_lock,     (void)0, (mutex))
# define g_mutex_trylock(mutex)						\
    G_THREAD_CF (mutex_trylock,  TRUE,    (mutex))
# define g_mutex_unlock(mutex)						\
    G_THREAD_CF (mutex_unlock,   (void)0, (mutex))
# define g_mutex_free(mutex)						\
    G_THREAD_CF (mutex_free,     (void)0, (mutex))
# define g_cond_wait(cond, mutex)					\
    G_THREAD_CF (cond_wait,      (void)0, (cond, mutex))
# define g_cond_timed_wait(cond, mutex, abs_time)			\
    G_THREAD_CF (cond_timed_wait, TRUE,   (cond, mutex, abs_time))
#else /* G_ERRORCHECK_MUTEXES */
# define g_mutex_lock(mutex)						\
    G_THREAD_ECF (mutex_lock,    (void)0, (mutex), void)
# define g_mutex_trylock(mutex)						\
    G_THREAD_ECF (mutex_trylock, TRUE,    (mutex), gboolean)
# define g_mutex_unlock(mutex)						\
    G_THREAD_ECF (mutex_unlock,  (void)0, (mutex), void)
# define g_mutex_free(mutex)						\
    G_THREAD_ECF (mutex_free,    (void)0, (mutex), void)
# define g_cond_wait(cond, mutex)					\
    (g_thread_supported () ? ((void(*)(GCond*, GMutex*, gulong, gchar*))\
      g_thread_functions_for_glib_use.cond_wait)			\
        (cond, mutex, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : (void) 0)
# define g_cond_timed_wait(cond, mutex, abs_time)			\
    (g_thread_supported () ?						\
      ((gboolean(*)(GCond*, GMutex*, GTimeVal*, gulong, gchar*))	\
        g_thread_functions_for_glib_use.cond_timed_wait)		\
          (cond, mutex, abs_time, G_MUTEX_DEBUG_MAGIC, G_STRLOC) : TRUE)
#endif /* G_ERRORCHECK_MUTEXES */

#if defined(G_THREADS_ENABLED) && defined(G_THREADS_MANDATORY)
#define g_thread_supported()     1
#else
#define g_thread_supported()    (g_threads_got_initialized)
#endif
#define g_mutex_new()            G_THREAD_UF (mutex_new,      ())
#define g_cond_new()             G_THREAD_UF (cond_new,       ())
#define g_cond_signal(cond)      G_THREAD_CF (cond_signal,    (void)0, (cond))
#define g_cond_broadcast(cond)   G_THREAD_CF (cond_broadcast, (void)0, (cond))
#define g_cond_free(cond)        G_THREAD_CF (cond_free,      (void)0, (cond))
#define g_private_new(destructor) G_THREAD_UF (private_new, (destructor))
#define g_private_get(private_key) G_THREAD_CF (private_get, \
                                                ((gpointer)private_key), \
                                                (private_key))
#define g_private_set(private_key, value) G_THREAD_CF (private_set, \
                                                       (void) (private_key = \
                                                        (GPrivate*) (value)), \
                                                       (private_key, value))
#define g_thread_yield()              G_THREAD_CF (thread_yield, (void)0, ())

#define g_thread_create(func, data, joinable, error)			\
  (g_thread_create_full (func, data, 0, joinable, FALSE, 		\
                         G_THREAD_PRIORITY_NORMAL, error))

 

 

glib里_GMutex类型

不透明类型

不透明数据类型隐藏了它们内部格式或结构。在C语言中,它们就像黑盒一样。支持它们的语言不是很多。作为替代,开发者们利用typedef声明一个类型,把它叫做不透明类型,希望其他人别去把它重新转化回对应的那个标准C类型。通常开发者们在定义一套特别的接口时才会用到它们。比如说用来保存进程标识符的pid_t类型。该类型的实际长度被隐藏起来了——尽管任何人都可以偷偷撩开它的面纱,发现它就是一个int。如果所有代码都不显式的利用它的长度(显式利用长度这里指直接使用int类型的长度,比如说在编程时使用sizeof(int)而不是sizeof(pid_t)),那么改变时就不会引起什么争议,这种改变确实可能会出现:在老版本的Unix系统中,pid_t的定义是short类型。

另外一个不透明数据类型的例子是atomic_t。在第八章“内核同步方法”中介绍过,它放置的是一个可以进行原子操作的整型值。尽管这种类型就是一个int,但利用不透明类型可以帮助确保这些数据只在特殊的有关原子操作的函数中才会被使用。不透明类型还帮助我们隐藏了类型的长度,该类型也并不总是完整的32位。

内核还用到了其它一些不透明类型,包括dev_t,gid_t和uid_t等等。处理不透明类型时的原则是:

¨ 不要假设该类型的长度。

¨ 不要将该类型转化回其对应的C标准类型使用。

¨ 编程时要保证在该类型实际存储空间和格式发生变化时代码不受影响。

===================================================================
 
问:glib里_GMutex类型是在哪里定义的呀? 找了一圈没看到。

只看到个typedef sturct _GMutex GMutex;// glib/gthread.h P76 glib-2.29.10
 
答:The GMutex struct is an opaque data structure.
 
Opaque data types do not reveal their internal format or structure. They are about as "black box" as you can get in C. There is not a lot of language support for them. Instead, developers declare a typedef, call it an opaque type, and hope no one typecasts it back to a standard C type. All use is generally through a special set of interfaces that the developer creates.
 
See 《Linux_Kernel_Development_Second_Edition》or
Google "Opaque data type"
 
* Sometimes OPAQUE means "this function doesn't return anything at all",
similarly to "returns void" in C and some other languages.

* Sometimes OPAQUE means "this value can be any datatype at all" (eg,
input of the count() aggregate).

* Sometimes OPAQUE means "I know exactly what this should be, but it's
an internal datatype with no SQL-level equivalent". This is usually
what's at stake with a trigger function.

* Sometimes OPAQUE means "I know exactly what this should be, but I
haven't declared it yet" (ie, a forward reference). This is needed
to declare I/O functions of user-defined types, since the system
requires the functions to already exist when the type is declared.
 
如果声明了_GMutex,那么GMutex就变成了普通的结构,如果不声明他就是一个不透明类型。

再说,mutex类型是内核对象,必须定义在内核中,不依赖于系统是很难实现的。

不透明类型是一种灵活的类型,他的大小是未知的。所以一般只能定义GMutex *变量,不能定义GMutex对象。然后通过一套接口来操作GMutex,就是
代码:

struct _GThreadFunctions
{
GMutex* (*mutex_new) (void);

GMutex* (*mutex_lock) (void);
GMutex* (*mutex_trylock) (void);
GMutex* (*mutex_unlock) (void);

GMutex* (*mutex_free) (void);
/* .... */
};

下面是win32中对mutex_new 的实现
static GMutex * g_mutex_new_win32_cs_impl(void)
{
CRITICAL_SECTION * cs = g_new(CRITICAL_SECTION ,1);
gpointer * retval = g_new(gpointer, 1);
InitializeCriticalSection(cs);
*retval = cs;
return (GMutex *)retval;
}
// GMutex隐藏了实际具体平台相关的结构,相当于对用户统一表示,GMutex只是作为一种标示来使用,用户是不可见的。

这有点像C++的interface, 那一堆函数指针就是vtable。这张表要进行初始化,间接让他们指向操作系统提供的API。
所以就没有定义_GMutex必要,因为这份数据已经定义在系统核心中,我们只需要一个叫GMutex的名字来引用这份数据。

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

内核中的 likely() 与 unlikely()


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

 

首先要明确:

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

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

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


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

 

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



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

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

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

 

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


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


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

第一个例子: example1.c

 

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


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

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

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

 

Disassembly of section .text:

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



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

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


第二个例子: example2.c


 

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


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

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

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


 

Disassembly of section .text:

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



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

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

 

G_LIKELY和G_UNLIKELY

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

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

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

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

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

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

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

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

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

glib data structure.

 

1. Glib Array (可变长的数组)

可以存放任意类型的元素,并且大小随着元素的增加可以自动增长。

glib库数组GArray介绍

glib库中的数组GArray类型很像C++标准容器库中的vector容器。要使用glib库中的数组中需要声明一个指向GArray类型的指针。  

struct _GArray
{
  gchar *data;
  guint len;
};

struct _GByteArray
{
  guint8 *data;
  guint	  len;
};

struct _GPtrArray
{
  gpointer *pdata;
  guint	    len;
};

2. Glib SList/SList (单项列表/双向列表)

glib库单向链表GSList介绍

typedef struct _GSList GSList;

struct _GSList
{
  gpointer data;
  GSList *next;
};

 

 

glib库双向链表GList介绍

双向链表与单向链表的区别是,从一个节点,不仅能访问到它的下一个节点,还能访问到它的上一个节点,其定义如下:

typedef struct _GList GList;

struct _GList
{
  gpointer data;
  GList *next;
  GList *prev;
};

 

 

继续阅读

Glib参考资料。

1. GLib Reference Manual

http://developer.gimp.org/api/2.0/glib/index.html (完整的)

https://developer.gnome.org/glib/stable/

 

2. glib

http://wenku.baidu.com/view/c72d83222f60ddccda38a07b.html

 

3. 使GlibC (使用数据结构)

http://wenku.baidu.com/view/29d6911ffc4ffe473368abb8.html

 

4. Glib

http://wenku.baidu.com/view/28dd2e0590c69ec3d5bb7564.html?pn=51

 

glib/gobject的一个网友的:

http://socol.iteye.com/category/86299

 

glib中用的非常多的:

指针,指针数组,数组指针,函数指针,指针函数:

http://see.xidian.edu.cn/cpp/u/cjinjie4/