Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3639
  • 博文数量: 6
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 90
  • 用 户 组: 普通用户
  • 注册时间: 2015-05-10 20:54
个人简介

多学习,多分享!

文章分类
文章存档

2018年(7)

我的朋友

分类: LINUX

2018-09-01 23:54:24

本文为作者原创,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:misteryoung
博客:http://blog.chinaunix.net/uid/20706239
=======================================================================

1 前言

1.1 关于glibc


    glibc是我们经常与之打交道的C库,每次我们编译C用户态程序时都离不开glibc库。但我们似乎感觉不到它的存在,那是因为gcc已经将其作为默认的C库,因此在编译时不需要显示的指定该库。
    当然,在很多嵌入式设备上,经常用uClibc来取代glibc,这是两个完全不同的C库。前者相对后者要小很多,相应的功能也会少很多。

1.2 本文内容


    本文围绕glibc库来进行讲解。本文不分析glibc框架,而是直接对具体函数实现进行分析。
    根据功能实现方式,可将glibc分为如下三部分:
    1)平台(包括CPU及操作系统)无关的,例如字符串操作函数;
    2)硬件平台(CPU)相关,例如socket等系统调用函数;
    3)操作系统相关,例如socket等系统调用函数;
    其中,后两部分函数较多,且后两部分通常是耦合在一起的。
    本文将基于Linux系统ARM平台进行分析。


2 基础知识


2.1 glibc与glibc-ports


    访问glibc官网(ftp.gnu.org/gnu/glibc),可以看到glibc的最新版本为2.28

    同时,还能看到一系列glibc-ports文件,该文件是ARM等平台的libc库实现,但并不是说glibc-ports完全独立于glibc。事实上,前者(glibc-ports)只包含与平台相关的代码,而平台无关的代码则与glibc共用。glibc-ports根目录下的README文件可证明以上观点,并摘录如下:

This is the glibc ports repository, an add-on for the GNU C Library (glibc).

It contains code that is not maintained in the official glibc source tree.

 

This includes working ports to GNU/Linux on some machine architectures that

are not maintained in the official glibc source tree.  It also includes

some code once used by old libc ports now defunct, which has been abandoned

but may be useful for some future porter to examine.  It may also include

some optimized functions tailored for specific CPU implementations of an

architecture, to be selected using --with-cpu.

    其实,glibc一开始是支持ARM平台的,后来因为某些原因将ARM等平台代码独立的出来。glibc-ports第一个版本为2.3.5。由于glibc-ports依赖于glibc,所以前者必须版本号是与后者一致的。

    这里以2.14版本为例进行讲解。


2.2 常用的宏

    在glibc/glibc-ports代码库中,经常看到strong_alias等宏的定义,这里对其进行简单分析:

点击(此处)折叠或打开

  1. # define strong_alias(name, aliasname) _strong_alias(name, aliasname)
  2. # define _strong_alias(name, aliasname) \
  3.   extern __typeof (name) aliasname __attribute__ ((alias (#name)));

  4. # define weak_alias(name, aliasname) _weak_alias (name, aliasname)
  5. # define _weak_alias(name, aliasname) \
  6.   extern __typeof (name) aliasname __attribute__ ((weak, alias (#name)));

    这里以glibc-2.14/sysdeps/unix/sysv/linux/connect.S为例:

点击(此处)折叠或打开

  1. #define __socket __libc_connect
  2. #define    NARGS    3
  3. #define NEED_CANCELLATION
  4. #include <socket.S>
  5. strong_alias (__libc_connect, __connect_internal)
  6. weak_alias (__libc_connect, __connect)

    于是,strong_alias (__libc_connect, __connect_internal)展开如下:

extern __typeof (__libc_connect) __connect_internal __attribute__ ((alias ("__libc_connect")));

    这里涉及到了“alias”,官方的解释见:

http://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Function-Attributes.html

    简而言之就是,符号__libc_connect是符号__connect_internal的别称。

    weak_alias (__libc_connect, __connect)展开如下:

extern __typeof (__libc_connect) __connect __attribute__ ((weak, alias ("__libc_connect")));
    这里的
weak表示当前符号是个弱类型符号(weak symbol),而非全局符号。weak修饰符的含义是让weak弱类型的函数可以被其它同名函数覆盖(即不会发生冲突),如果没有其它同名函数,就使用该weak函数,类似于是默认函数。也就是说,符号__libc_connect是符号__connect的弱类型别称。


3 函数分析

3.1 recvmsg

3.1.1 分析

3.1.1.1 源码1

glibc-2.14/socket/recvmsg.c

点击(此处)折叠或打开

  1. ssize_t
  2. __recvmsg (fd, message, flags)
  3.      int fd;
  4.      struct msghdr *message;
  5.      int flags;
  6. {
  7.   __set_errno (ENOSYS);
  8.   return -1;
  9. }

  10. weak_alias (__recvmsg, recvmsg)
    所以,其它文件中必须定义函数recvmsg,否则使用该文件中的函数__recvmsg

glibc-2.14/sysdeps/unix/sysv/linux/recvmsg.S

点击(此处)折叠或打开

  1. #define    socket    recvmsg
  2. #define    __socket __libc_recvmsg
  3. #define    NARGS    3
  4. #define NEED_CANCELLATION
  5. 对于ARM,相当于#include glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/socket.S
  6. #include <socket.S>    
  7. weak_alias (__libc_recvmsg, __recvmsg)
     glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/socket.S

点击(此处)折叠或打开

  1. #include <sysdep-cancel.h>
  2. #include <socketcall.h>

  3. #define P(a, b) P2(a, b)
  4. #define P2(a, b) a##b

  5. #define PUSHARGS_1    str a1, [sp, $-4]!
  6. #define PUSHARGS_2    stmfd {a1, a2}
  7. #define PUSHARGS_3    stmfd {a1, a2, a3}
  8. #define PUSHARGS_4    stmfd {a1, a2, a3, a4}
  9. #define PUSHARGS_5    stmfd {a1, a2, a3, a4}    /* Caller has already pushed arg 5 */
  10. #define PUSHARGS_6    stmfd {a1, a2, a3, a4}

  11. #define POPARGS_1    add sp, sp, #4
  12. #define POPARGS_2    add sp, sp, #8
  13. #define POPARGS_3    add sp, sp, #12
  14. #define POPARGS_4    add sp, sp, #16
  15. #define POPARGS_5    add sp, sp, #16
  16. #define POPARGS_6    add sp, sp, #16

  17. #ifndef NARGS
  18. #define NARGS 3            /* If we were called with no wrapper, this is really socket() */
  19. #endif

  20. .globl __socket
  21. ENTRY (__socket)
  22.     /* Push args onto the stack. */
  23.     NARGS等于3,展开后相当于stmfd {a1, a2, a3},也就是将3个参数入栈
  24.     P(PUSHARGS_,NARGS)

  25. /* Do the system call trap. */

  26.     mov a1, $P(SOCKOP_,socket)
  27.     mov a2, sp

  28.     
  29. swi SYS_ify(socketcall)

  30.     /* Pop args off the stack */
  31.     P(POPARGS_,NARGS)    

  32.     /* r0 is < 0 if there was an error. */
  33.     cmn r0, $124
  34.     RETINSTR(cc, r14)
  35.     b PLTJMP(SYSCALL_ERROR)

  36. PSEUDO_END (__socket)

  37. #ifndef NO_WEAK_ALIAS
  38. weak_alias (__socket, socket)
  39. #endif

glibc-2.14/sysdeps/unix/sysv/linux/socketcall.h

点击(此处)折叠或打开

  1. #define SOCKOP_socket        1
  2. #define SOCKOP_bind        2
  3. #define SOCKOP_connect        3
  4. #define SOCKOP_listen        4
  5. #define SOCKOP_accept        5
  6. #define SOCKOP_getsockname    6
  7. #define SOCKOP_getpeername    7
  8. #define SOCKOP_socketpair    8
  9. #define SOCKOP_send        9
  10. #define SOCKOP_recv        10
  11. #define SOCKOP_sendto        11
  12. #define SOCKOP_recvfrom        12
  13. #define SOCKOP_shutdown        13
  14. #define SOCKOP_setsockopt    14
  15. #define SOCKOP_getsockopt    15
  16. #define SOCKOP_sendmsg        16
  17. #define SOCKOP_recvmsg        17
  18. #define SOCKOP_accept4        18
  19. #define SOCKOP_recvmmsg        19
  20. #define SOCKOP_sendmmsg        20

glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/sysdep.h

点击(此处)折叠或打开

  1. #define SYS_ify(syscall_name) (__NR_##syscall_name)


glibc-ports-2.14/sysdeps/ arm/sysdep.h

点击(此处)折叠或打开

  1. #define RETINSTR(cond, reg) \
  2.          bx##cond reg
  3. #define PLTJMP(_x) _x
  4. #define SYSCALL_ERROR __syscall_error

3.1.1.2 展开1

    最终,glibc-2.14/sysdeps/unix/sysv/linux/recvmsg.S被展开成如下内容:

点击(此处)折叠或打开

  1. #include <sysdep-cancel.h>
  2. #include <socketcall.h>
  3. .text
  4. .globl __libc_recvmsg
  5. ENTRY (__libc_recvmsg)

  6.     /* Push args onto the stack. */
  7.     stmfd {a1, a2, a3}

  8.     /* Do the system call trap. */
  9.     mov a1, $17   将17存入r0(a1是r0寄存器的别名)中,17代表recvmsg请求
  10.     mov a2, sp    将栈地址存入r1寄存器中
  11.     系统陷入内核,并执行sys_socketcall,根据r0中的参数SOCKOP_recvmsg选择调用sys_recvmsg
  12.     swi __NR_socketcall

  13.     /* Pop args off the stack */
  14.     add sp, sp, #12    出栈

  15.     /* r0 is < 0 if there was an error. */
  16.     此时,r0中保存的是sys_recvmsg的返回值
  17.     cmn r0, $124          判断是否小于0
  18.     bxcc r14              若不是则直接返回(等价于bxcc lr)
  19.     b __syscall_error     否则执行该函数

  20. PSEUDO_END (__libc_recvmsg)

  21. weak_alias (__libc_recvmsg, __recvmsg)
  22. weak_alias (__libc_recvmsg, recvmsg)

3.1.2.1 源码2

glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/sysdep.S

点击(此处)折叠或打开

  1. ENTRY (__syscall_error)
  2.     rsb r0, r0, $0

  3. #define __syscall_error __syscall_error_1
  4. #include <sysdeps/unix/arm/sysdep.S>

glibc-ports-2.14/sysdeps/arm/sysdep.h

点击(此处)折叠或打开

  1. #define DO_RET(_reg) \
  2.     bx _reg

glibc -2.14/include/unix/libc-symbols.h

点击(此处)折叠或打开

  1. #define C_SYMBOL_NAME(name) name
    glibc-ports-2.14/sysdeps/unix/arm/sysdep.S

点击(此处)折叠或打开

  1. syscall_error:
  2.     ldr r1, 1f
  3.     str r0, [r1]
  4.     mvn r0, $0
  5.     DO_RET (r14)

  6. 1: .long C_SYMBOL_NAME(errno)
  7. #undef __syscall_error
  8. END (__syscall_error)

3.1.2.2 展开2

点击(此处)折叠或打开

  1. ENTRY (__syscall_error)

  2.     rsb r0, r0, $0   将0 - r0赋值给r0,也即将r0中的值取反(相反数),因为内核总是将错误码取反返
  3. __syscall_error_1:
  4.     ldr r1, 1f       将errno地址存入r1寄存器中
  5.     str r0, [r1]     将r0中保存的返回值存入errno中
  6.     mvn r0, $0       将0按位取反(相当于-1)后存入r0中,这样recvmsg便返回了-1
  7.     bx lr            返回
  8. 1:    .long errno
  9. END (__syscall_error)

3.1.2 总结

    由“weak_alias (__libc_recvmsg, recvmsg)”可知,recvmsg相当于__libc_recvmsg的别名,上面已经分析了__libc_recvmsg,而__libc_recvmsg最后几行代码如下:

点击(此处)折叠或打开

  1.     /* r0 is < 0 if there was an error. */
  2.     cmn r0, $124
  3.     bxcc r14
  4.     b __syscall_error
    意思是,若内核返回负值,则表示出错,此时执行__syscall_error,否则说明内核执行成功,则直接返回(r0中保存内核的返回值)。

    __syscall_error功能很简单:将内核的返回值的相反数赋值给errno,同时将r0中设置为-1,使得recvmsg返回-1,以告诉用户该函数执行失败。

    所以,当recvmsg执行失败后,返回-1的同时,errno也被设置成了内核的返回值(代表出错原因)。
  另外,这里要强调一下,很多socket系列的函数的实现方式都与recvmsg类似。例如:sendmsg、accept、connect等等。猜一下,下面的.S源文件实现的是哪个函数的功能:

点击(此处)折叠或打开

  1. #define    socket    accept
  2. #define    __socket __libc_accept
  3. #define    NARGS    3
  4. #define NEED_CANCELLATION
  5. #include <socket.S>
  6. libc_hidden_def (accept)


3.2 pthread_mutex_lock

3.2.1 分析

3.2.1.1 源码

    glibc-2.14/nptl/pthread_mutex_lock.c

点击(此处)折叠或打开

  1. #ifndef __pthread_mutex_lock
  2. strong_alias (__pthread_mutex_lock, pthread_mutex_lock)
  3. strong_alias (__pthread_mutex_lock, __pthread_mutex_lock_internal)
  4. #endif
  5. 所以,__pthread_mutex_lock是pthread_mutex_lock的别称。

  6. int
  7. __pthread_mutex_lock (mutex)
  8.      pthread_mutex_t *mutex;
  9. {
  10.   assert (sizeof (mutex->__size) >= sizeof (mutex->__data));

  11.   unsigned int type = PTHREAD_MUTEX_TYPE (mutex);
  12.   if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))
  13.     return __pthread_mutex_lock_full (mutex);

  14.   pid_t id = THREAD_GETMEM (THREAD_SELF, tid);

  15.   if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)
  16.       == PTHREAD_MUTEX_TIMED_NP)
  17.     {
  18.     simple:
  19.       /* Normal mutex. */
  20.       LLL_MUTEX_LOCK (mutex);
  21.       assert (mutex->__data.__owner == 0);
  22.     }
  23.   else if (__builtin_expect (type == PTHREAD_MUTEX_RECURSIVE_NP, 1))
  24.     {
  25.       /* Recursive mutex. */

  26.       /* Check whether we already hold the mutex. */
  27.       if (mutex->__data.__owner == id)
  28.     {
  29.      /* Just bump the counter. */
  30.      if (__builtin_expect (mutex->__data.__count + 1 == 0, 0))
  31.      /* Overflow of the counter. */
  32.      return EAGAIN;

  33.      ++mutex->__data.__count;

  34.      return 0;
  35.     }

  36.       /* We have to get the mutex. */
  37.       LLL_MUTEX_LOCK (mutex);

  38.       assert (mutex->__data.__owner == 0);
  39.       mutex->__data.__count = 1;
  40.     }
  41.   else if (__builtin_expect (type == PTHREAD_MUTEX_ADAPTIVE_NP, 1))
  42.     {
  43.       if (! __is_smp)
  44.     goto simple;

  45.       if (LLL_MUTEX_TRYLOCK (mutex) != 0)
  46.     {
  47.      int cnt = 0;
  48.      int max_cnt = MIN (MAX_ADAPTIVE_COUNT,
  49.              mutex->__data.__spins * 2 + 10);
  50.      do
  51.      {
  52.      if (cnt++ >= max_cnt)
  53.         {
  54.          LLL_MUTEX_LOCK (mutex);
  55.          break;
  56.         }

  57. #ifdef BUSY_WAIT_NOP
  58.      BUSY_WAIT_NOP;
  59. #endif
  60.      }
  61.      while (LLL_MUTEX_TRYLOCK (mutex) != 0);

  62.      mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;
  63.     }
  64.       assert (mutex->__data.__owner == 0);
  65.     }
  66.   else
  67.     {
  68.       assert (type == PTHREAD_MUTEX_ERRORCHECK_NP);
  69.       /* Check whether we already hold the mutex. */
  70.       if (__builtin_expect (mutex->__data.__owner == id, 0))
  71.     return EDEADLK;
  72.       goto simple;
  73.     }

  74.   /* Record the ownership. */
  75.   mutex->__data.__owner = id;
  76. #ifndef NO_INCR
  77.   ++mutex->__data.__nusers;
  78. #endif

  79.   return 0;
  80. }
    这里又调用了LLL_MUTEX_LOCK

点击(此处)折叠或打开

  1. #ifndef LLL_MUTEX_LOCK
  2. # define LLL_MUTEX_LOCK(mutex) \
  3.   lll_lock ((mutex)->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex))
  4. # define LLL_MUTEX_TRYLOCK(mutex) \
  5.   lll_trylock ((mutex)->__data.__lock)
  6. # define LLL_ROBUST_MUTEX_LOCK(mutex, id) \
  7.   lll_robust_lock ((mutex)->__data.__lock, id, \
  8.          PTHREAD_ROBUST_MUTEX_PSHARED (mutex))
  9. #endif
    继续跟踪lll_lock的实现。

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h

另一条线索,当__lll_lock加锁失败后,会调用__lll_lock_wait_private__lll_lock_wait进行阻塞。这里指分析__lll_lock_wait_private

点击(此处)折叠或打开

  1. #define lll_lock(futex, private) __lll_lock (&(futex), private)

  2. #define __lll_lock(futex, private)                     \
  3.   ((void) ({                                 \
  4.     int *__futex = (futex);                         \
  5.     if (__builtin_expect (atomic_compare_and_exchange_val_acq (__futex, 1, 0), 0)) \
  6.       {                                     \
  7.     if (__builtin_constant_p (private) && (private) == LLL_PRIVATE)     \
  8.      __lll_lock_wait_private (__futex);                 \
  9.     else                                 \
  10.      __lll_lock_wait (__futex, private);                 \
  11.       }                                     \
  12.   }))

    glibc-2.14/include/atomic.h

点击(此处)折叠或打开

  1. #if !defined atomic_compare_and_exchange_val_acq \
  2.     && defined __arch_compare_and_exchange_val_32_acq
  3. # define atomic_compare_and_exchange_val_acq(mem, newval, oldval) \
  4.   __atomic_val_bysize (__arch_compare_and_exchange_val,acq,         \
  5.          mem, newval, oldval)
  6. #endif

  7. #define __atomic_val_bysize(pre, post, mem, ...)             \
  8.   ({                                     \
  9.     __typeof (*mem) __atg1_result;                     \
  10.     if (sizeof (*mem) == 1)                         \
  11.       __atg1_result = pre##_8_##post (mem, __VA_ARGS__);         \
  12.     else if (sizeof (*mem) == 2)                     \
  13.       __atg1_result = pre##_16_##post (mem, __VA_ARGS__);         \
  14.     else if (sizeof (*mem) == 4)                     \
  15.       __atg1_result = pre##_32_##post (mem, __VA_ARGS__);         \
  16.     else if (sizeof (*mem) == 8)                     \
  17.       __atg1_result = pre##_64_##post (mem, __VA_ARGS__);         \
  18.     else                                 \
  19.       abort ();                                 \
  20.     __atg1_result;                             \
  21.   })

    glibc-ports-2.14/sysdeps/arm/bits/atomic.h

点击(此处)折叠或打开

  1. #define __arch_compare_and_exchange_val_8_acq(mem, newval, oldval) \
  2.   ({ __typeof (oldval) result, tmp;                     \
  3.      __asm__ ("\n"                             \
  4.      "0:\tldr\t%1,[%2]\n\t"                     \
  5.      "cmp\t%1,%4\n\t"                         \
  6.      "movne\t%0,%1\n\t"                     \
  7.      "bne\t1f\n\t"                         \
  8.      "swpb\t%0,%3,[%2]\n\t"                     \
  9.      "cmp\t%1,%0\n\t"                         \
  10.      "swpbne\t%1,%0,[%2]\n\t"                     \
  11.      "bne\t0b\n\t"                         \
  12.      "1:"                             \
  13.      : "=&r" (result), "=&r" (tmp)                 \
  14.      : "r" (mem), "r" (newval), "r" (oldval)             \
  15.      : "cc", "memory");                     \
  16.      result; })
  17. #define __arch_compare_and_exchange_val_16_acq(mem, newval, oldval) \
  18.   ({ __arm_link_error (); oldval; })

  19. #define __arch_compare_and_exchange_val_32_acq(mem, newval, oldval) \
  20.   ({ __typeof (oldval) result, tmp;                     \
  21.      __asm__ ("\n"                             \
  22.      "0:\tldr\t%1,[%2]\n\t"                     \
  23.      "cmp\t%1,%4\n\t"                         \
  24.      "movne\t%0,%1\n\t"                     \
  25.      "bne\t1f\n\t"                         \
  26.      "swp\t%0,%3,[%2]\n\t"                     \
  27.      "cmp\t%1,%0\n\t"                         \
  28.      "swpne\t%1,%0,[%2]\n\t"                     \
  29.      "bne\t0b\n\t"                         \
  30.      "1:"                             \
  31.      : "=&r" (result), "=&r" (tmp)                 \
  32.      : "r" (mem), "r" (newval), "r" (oldval)             \
  33.      : "cc", "memory");                     \
  34.      result; })

  35. #define __arch_compare_and_exchange_val_64_acq(mem, newval, oldval) \
  36.   ({ __arm_link_error (); oldval; })

另一条线索,当__lll_lock加锁失败后,会调用__lll_lock_wait_private__lll_lock_wait进行阻塞。这里指分析__lll_lock_wait_private

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.c

点击(此处)折叠或打开

  1. void __lll_lock_wait_private (int *futex)
  2. {
  3.   do
  4.     {
  5.       int oldval = atomic_compare_and_exchange_val_acq (futex, 2, 1);
  6.       if (oldval != 0)
  7.     lll_futex_wait (futex, 2, LLL_PRIVATE);
  8.     }
  9.   while (atomic_compare_and_exchange_bool_acq (futex, 2, 0) != 0);
  10. }

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h

点击(此处)折叠或打开

  1. #define lll_futex_wait(futexp, val, private) \
  2.   lll_futex_timed_wait(futexp, val, NULL, private)

  3. #define lll_futex_timed_wait(futexp, val, timespec, private) \
  4.   ({                                     \
  5.     INTERNAL_SYSCALL_DECL (__err);                     \
  6.     long int __ret;                             \
  7.     futex系统调用
  8.     __ret = INTERNAL_SYSCALL (futex, __err, 4, (futexp),         \
  9.              __lll_private_flag (FUTEX_WAIT, private),     \
  10.              (val), (timespec));             \
  11.     __ret;                                 \
  12.   })

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/sysdep.h

点击(此处)折叠或打开

  1. #define INTERNAL_SYSCALL(name, err, nr, args...)        \
  2.     INTERNAL_SYSCALL_RAW(SYS_ify(name), err, nr, args)

  3. #define INTERNAL_SYSCALL_RAW(name, err, nr, args...)        \
  4.   ({ unsigned int _sys_result;                    \
  5.      {                                \
  6.        register int _a1 asm ("a1");                \
  7.        LOAD_ARGS_##nr (args)                    \
  8.        asm volatile ("swi    %1    @ syscall " #name    \
  9.          : "=r" (_a1)                \
  10.          : "i" (name) ASM_ARGS_##nr            \
  11.          : "memory");                \
  12.        _sys_result = _a1;                    \
  13.      }                                \
  14.      (int) _sys_result; })

3.2.1.2 展开

    将__atomic_val_bysize展开后如下:

点击(此处)折叠或打开

  1. #define __atomic_val_bysize(pre, post, mem, ...)             \
  2.   ({                                     \
  3.     __typeof (*mem) __atg1_result;                     \
  4.     if (sizeof (*mem) == 1)                         \
  5.       __atg1_result = __arch_compare_and_exchange_val_8_acq (mem, __VA_ARGS__);     \
  6.     else if (sizeof (*mem) == 2)                     \
  7.       __atg1_result = __arch_compare_and_exchange_val_16_acq (mem, __VA_ARGS__);     \
  8.     else if (sizeof (*mem) == 4)                     \
  9.       __atg1_result = __arch_compare_and_exchange_val_32_acq (mem, __VA_ARGS__);     \
  10.     else if (sizeof (*mem) == 8)                     \
  11.       __atg1_result = __arch_compare_and_exchange_val_64_acq (mem, __VA_ARGS__);     \
  12.     else                                 \
  13.       abort ();                                 \
  14.     __atg1_result;                             \
  15.   })
    pthread_mutex_lock最终调用了__arch_compare_and_exchange_val_32_acq
    最终,
lll_futex_wait(futexp, val, private)展开如下:

点击(此处)折叠或打开

  1. ({                                     \
  2.     INTERNAL_SYSCALL_DECL (__err);                     \
  3. long int __ret;                             \
  4.     __ret = ({ unsigned int _sys_result;                    \
  5.      {                                \
  6.        register int _a1 asm ("a1");                \
  7.        LOAD_ARGS_4 (futexp, FUTEX_WAIT, val, timespec)                    \
  8.        asm volatile ("swi    %1    @ syscall futex"    \
  9.          : "=r" (_a1)                \
  10.          : "i" (__NR_futex) ASM_ARGS_4            \
  11.          : "memory");                \
  12.        _sys_result = _a1;                    \
  13.      }                                \
  14.      (int) _sys_result; })    
  15.     __ret;                                 \
  16.   })
    这样,lll_futex_wait 完成系统调用futex,并传递参数FUTEX_WAIT给内核。内核执行futex_wait函数,使得当前线程睡眠。

3.2.2 总结

    pthread_mutex_lock首先通过__arch_compare_and_exchange_val_32_acq进行加锁,若加锁失败,则通过__lll_lock_wait_private进行一系列后,执行futex系统调用陷入内核而阻塞,并等待被唤醒。

3.3 pthread_mutex_unlock

3.3.1 分析

3.3.1.1 源码

    glibc-2.14/nptl/pthread_mutex_unlock.c

点击(此处)折叠或打开

  1. glibc-2.14/nptl/pthread_mutex_unlock.c
  2. strong_alias (__pthread_mutex_unlock, pthread_mutex_unlock)
  3. 所以,__pthread_mutex_unlock是pthread_mutex_unlock的别称。

  4. int
  5. __pthread_mutex_unlock (mutex)
  6.      pthread_mutex_t *mutex;
  7. {
  8.   return __pthread_mutex_unlock_usercnt (mutex, 1);
  9. }

  10. int
  11. internal_function attribute_hidden
  12. __pthread_mutex_unlock_usercnt (mutex, decr)
  13.      pthread_mutex_t *mutex;
  14.      int decr;
  15. {
  16.   int type = PTHREAD_MUTEX_TYPE (mutex);
  17.   if (__builtin_expect (type & ~PTHREAD_MUTEX_KIND_MASK_NP, 0))
  18.     return __pthread_mutex_unlock_full (mutex, decr);

  19.   if (__builtin_expect (type, PTHREAD_MUTEX_TIMED_NP)
  20.       == PTHREAD_MUTEX_TIMED_NP)
  21.     {
  22.       /* Always reset the owner field. */
  23.     normal:
  24.       mutex->__data.__owner = 0;
  25.       if (decr)
  26.     /* One less user. */
  27.     --mutex->__data.__nusers;

  28.       /* Unlock. */
  29.       lll_unlock (mutex->__data.__lock, PTHREAD_MUTEX_PSHARED (mutex));
  30.       return 0;
  31.     }
  32.   else if (__builtin_expect (type == PTHREAD_MUTEX_RECURSIVE_NP, 1))
  33.     {
  34.       /* Recursive mutex. */
  35.       if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid))
  36.     return EPERM;

  37.       if (--mutex->__data.__count != 0)
  38.     /* We still hold the mutex. */
  39.     return 0;
  40.       goto normal;
  41.     }
  42.   else if (__builtin_expect (type == PTHREAD_MUTEX_ADAPTIVE_NP, 1))
  43.     goto normal;
  44.   else
  45.     {
  46.       /* Error checking mutex. */
  47.       assert (type == PTHREAD_MUTEX_ERRORCHECK_NP);
  48.       if (mutex->__data.__owner != THREAD_GETMEM (THREAD_SELF, tid)
  49.      || ! lll_islocked (mutex->__data.__lock))
  50.     return EPERM;
  51.       goto normal;
  52.     }
  53. }

    这里又调用了lll_unlock,继续跟踪该函数。

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h

点击(此处)折叠或打开

  1. #define lll_unlock(futex, private) __lll_unlock(&(futex), private)

  2. #define __lll_unlock(futex, private) \
  3.   (void)                            \
  4.     ({ int *__futex = (futex);                    \
  5.        int __oldval = atomic_exchange_rel (__futex, 0);        \
  6.        if (__builtin_expect (__oldval > 1, 0))            \
  7.      lll_futex_wake (__futex, 1, private);            \
  8. })

    glibc-2.14/include/unix/atomic.h

点击(此处)折叠或打开

  1. #ifndef atomic_exchange_rel
  2. # define atomic_exchange_rel(mem, newvalue) atomic_exchange_acq (mem, newvalue)
  3. #endif

    glibc-ports-2.14/sysdeps/ arm/bits/atomic.h

点击(此处)折叠或打开

  1. #define atomic_exchange_acq(mem, newvalue)                 \
  2.   ({ __typeof (*mem) result;                         \
  3.      if (sizeof (*mem) == 1)                         \
  4.        __asm__ __volatile__ ("swpb %0, %1, [%2]"             \
  5.              : "=&r,&r" (result)             \
  6.              : "r,0" (newvalue), "r,r" (mem) : "memory"); \
  7.      else if (sizeof (*mem) == 4)                     \
  8.        __asm__ __volatile__ ("swp %0, %1, [%2]"                 \
  9.              : "=&r,&r" (result)             \
  10.              : "r,0" (newvalue), "r,r" (mem) : "memory"); \
  11.      else                                 \
  12.        {                                 \
  13.      result = 0;                             \
  14.      abort ();                             \
  15.        }                                 \
  16.      result; })

    glibc-ports-2.14/sysdeps/unix/sysv/linux/arm/nptl/lowlevellock.h

点击(此处)折叠或打开

  1. #define lll_futex_wake(futexp, nr, private) \
  2.   ({                                     \
  3.     INTERNAL_SYSCALL_DECL (__err);                     \
  4.     long int __ret;                             \
  5.     __ret = INTERNAL_SYSCALL (futex, __err, 4, (futexp),         \
  6.              __lll_private_flag (FUTEX_WAKE, private),     \
  7.              (nr), 0);                     \
  8.     __ret;                                 \
  9.   })

3.3.1.2 展开

    最终,lll_futex_wake(futexp, val, private)展开如下:

点击(此处)折叠或打开

  1. ({                                     \
  2.     INTERNAL_SYSCALL_DECL (__err);                     \
  3. long int __ret;                             \
  4.     __ret = ({ unsigned int _sys_result;                    \
  5.      {                                \
  6.        register int _a1 asm ("a1");                \
  7.        LOAD_ARGS_4 (futexp, FUTEX_WAKE, val, timespec)                    \
  8.        asm volatile ("swi    %1    @ syscall futex"    \
  9.          : "=r" (_a1)                \
  10.          : "i" (__NR_futex) ASM_ARGS_4            \
  11.          : "memory");                \
  12.        _sys_result = _a1;                    \
  13.      }                                \
  14.      (int) _sys_result; })    
  15.     __ret;                                 \
  16.   })

3.3.2 总结

    尽管lll_futex_waitlll_futex_wake均完成futex系统调用,但二者传递的参数不一样(前者:FUTEX_WAIT,后者:FUTEX_WAKE),导致实现的功能也不一样。

    函数pthread_mutex_unlock最终调用lll_futex_wake,而后者通过futex系统调用,陷入内核执行内核futex_wake函数,实现唤醒因无法获取锁而睡眠的线程的功能。






阅读(53) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册