先给自己打个广告,本人的微信公众号:嵌入式Linux江湖,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题。

一 源码分析

1.linux中用户态的mutex实现在哪里?
下载Glibc源码后解压,mutex相关实现函数在:/work/tools/glibc-2.34/nptl中,头文件在glibc-2.34/sysdeps/nptl/bits路径下。
2.pthread_mutex_t联合体定

#/glibc-2.34/sysdeps/nptl/bits/pthreadtypes.h
typedef union
{struct __pthread_mutex_s __data;char __size[__SIZEOF_PTHREAD_MUTEX_T];long int __align;
} pthread_mutex_t;

pthread_mutex_t主要有三个成员变量,其中最关键的是__pthread_mutex_s

#/glibc-2.34/sysdeps/nptl/bits/struct_mutex.h
/* Generic struct for both POSIX and C11 mutexes.  New ports are expectedto use the default layout, however architecture can redefine it toadd arch-specific extension (such as lock-elision).  The struct havea size of 32 bytes on LP32 and 40 bytes on LP64 architectures.  */struct __pthread_mutex_s
{int __lock __LOCK_ALIGNMENT;unsigned int __count;int __owner;
#if __WORDSIZE == 64unsigned int __nusers;
#endif/* KIND must stay at this position in the structure to maintainbinary compatibility with static initializers.Concurrency notes:The __kind of a mutex is initialized either by the staticPTHREAD_MUTEX_INITIALIZER or by a call to pthread_mutex_init.After a mutex has been initialized, the __kind of a mutex is usually notchanged.  BUT it can be set to -1 in pthread_mutex_destroy or elision canbe enabled.  This is done concurrently in the pthread_mutex_*lockfunctions by using the macro FORCE_ELISION. This macro is only definedfor architectures which supports lock elision.For elision, there are the flags PTHREAD_MUTEX_ELISION_NP andPTHREAD_MUTEX_NO_ELISION_NP which can be set in addition to the alreadyset type of a mutex.  Before a mutex is initialized, onlyPTHREAD_MUTEX_NO_ELISION_NP can be set with pthread_mutexattr_settype.After a mutex has been initialized, the functions pthread_mutex_*lock canenable elision - if the mutex-type and the machine supports it - bysetting the flag PTHREAD_MUTEX_ELISION_NP. This is done concurrently.Afterwards the lock / unlock functions are using specific elisioncode-paths.  */int __kind;
#if __WORDSIZE != 64unsigned int __nusers;
#endif
#if __WORDSIZE == 64int __spins;__pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV      1
#else__extension__ union{int __spins;__pthread_slist_t __list;};
# define __PTHREAD_MUTEX_HAVE_PREV      0
#endif
};

一般地,__pthread_mutex_s中最重要的成员是如下四个(其他成员先忽略)

struct __pthread_mutex_s
{int __lock __LOCK_ALIGNMENT;unsigned int __count;int __owner;unsigned int __nusers;
};
1.lock表示当前mutex的状态,0表示初始化没有被持有的状态,此时可以对mutex执行lock操作,lock为1时表示当前mutex已经被持有,并且没有其他线程在等待它的释放,当lock > 1时,表示mutex被某个线程持有并且有另外的线程在等待它的释放。
2.count表示当前被持有的次数,一般来说对不可重入的锁,这个值只可能是0和1,对于可重入的锁,比如递归锁,这个值会大于1。
3.owner用来记录持有当前mutex的线程id,如果没有线程持有,这个值为0。
4.nusers用来记录当前有多少线程持有该互斥体,一般来说,这个值只能是0和1,但是对于读写锁来说,多个读线程是可以共同持有mutex的,因此用nusers来记录线程的数量。

3.___pthread_mutex_init初始化函数

#/glibc-2.34/nptl/pthread_mutex_init.c
int
___pthread_mutex_init (pthread_mutex_t *mutex,const pthread_mutexattr_t *mutexattr)
{const struct pthread_mutexattr *imutexattr;ASSERT_TYPE_SIZE (pthread_mutex_t, __SIZEOF_PTHREAD_MUTEX_T);/* __kind is the only field where its offset should be checked toavoid ABI breakage with static initializers.  */ASSERT_PTHREAD_INTERNAL_OFFSET (pthread_mutex_t, __data.__kind,__PTHREAD_MUTEX_KIND_OFFSET);ASSERT_PTHREAD_INTERNAL_MEMBER_SIZE (pthread_mutex_t, __data.__kind, int);imutexattr = ((const struct pthread_mutexattr *) mutexattr?: &default_mutexattr);/* Sanity checks.  */switch (__builtin_expect (imutexattr->mutexkind& PTHREAD_MUTEXATTR_PROTOCOL_MASK,PTHREAD_PRIO_NONE<< PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)){case PTHREAD_PRIO_NONE << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:break;case PTHREAD_PRIO_INHERIT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:if (__glibc_unlikely (prio_inherit_missing ()))return ENOTSUP;break;default:/* XXX: For now we don't support robust priority protected mutexes.  */if (imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST)return ENOTSUP;break;}/* Clear the whole variable.  */memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);/* Copy the values from the attribute.  */int mutex_kind = imutexattr->mutexkind & ~PTHREAD_MUTEXATTR_FLAG_BITS;if ((imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST) != 0){#ifndef __ASSUME_SET_ROBUST_LISTif ((imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_PSHARED) != 0&& !__nptl_set_robust_list_avail)return ENOTSUP;
#endifmutex_kind |= PTHREAD_MUTEX_ROBUST_NORMAL_NP;}switch (imutexattr->mutexkind & PTHREAD_MUTEXATTR_PROTOCOL_MASK){case PTHREAD_PRIO_INHERIT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:mutex_kind |= PTHREAD_MUTEX_PRIO_INHERIT_NP;break;case PTHREAD_PRIO_PROTECT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:mutex_kind |= PTHREAD_MUTEX_PRIO_PROTECT_NP;int ceiling = (imutexattr->mutexkind& PTHREAD_MUTEXATTR_PRIO_CEILING_MASK)>> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT;if (! ceiling){/* See __init_sched_fifo_prio.  */if (atomic_load_relaxed (&__sched_fifo_min_prio) == -1)__init_sched_fifo_prio ();if (ceiling < atomic_load_relaxed (&__sched_fifo_min_prio))ceiling = atomic_load_relaxed (&__sched_fifo_min_prio);}mutex->__data.__lock = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT;break;default:break;}/* The kernel when waking robust mutexes on exit never usesFUTEX_PRIVATE_FLAG FUTEX_WAKE.  */if ((imutexattr->mutexkind & (PTHREAD_MUTEXATTR_FLAG_PSHARED| PTHREAD_MUTEXATTR_FLAG_ROBUST)) != 0)mutex_kind |= PTHREAD_MUTEX_PSHARED_BIT;/* See concurrency notes regarding __kind in struct __pthread_mutex_sin sysdeps/nptl/bits/thread-shared-types.h.  */atomic_store_relaxed (&(mutex->__data.__kind), mutex_kind);/* Default values: mutex not used yet.  */// mutex->__count = 0;   already done by memset// mutex->__owner = 0;    already done by memset// mutex->__nusers = 0;   already done by memset// mutex->__spins = 0;    already done by memset// mutex->__next = NULL;  already done by memsetLIBC_PROBE (mutex_init, 1, mutex);return 0;
}

init函数就比较简单了,将mutex结构体清零,设置结构体中__kind属性。函数的前三行部分是参数合法性判断

ASSERT_TYPE_SIZE (pthread_mutex_t, __SIZEOF_PTHREAD_MUTEX_T);/* __kind is the only field where its offset should be checked toavoid ABI breakage with static initializers.  */
ASSERT_PTHREAD_INTERNAL_OFFSET (pthread_mutex_t, __data.__kind,__PTHREAD_MUTEX_KIND_OFFSET);
ASSERT_PTHREAD_INTERNAL_MEMBER_SIZE (pthread_mutex_t, __data.__kind, int);

紧接着的一段语句都是为了设置mutex_kind的值

imutexattr = ((const struct pthread_mutexattr *) mutexattr?: &default_mutexattr);/* Sanity checks.  */switch (__builtin_expect (imutexattr->mutexkind& PTHREAD_MUTEXATTR_PROTOCOL_MASK,PTHREAD_PRIO_NONE<< PTHREAD_MUTEXATTR_PROTOCOL_SHIFT)){case PTHREAD_PRIO_NONE << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:break;case PTHREAD_PRIO_INHERIT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:if (__glibc_unlikely (prio_inherit_missing ()))return ENOTSUP;break;default:/* XXX: For now we don't support robust priority protected mutexes.  */if (imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST)return ENOTSUP;break;}/* Clear the whole variable.  */memset (mutex, '\0', __SIZEOF_PTHREAD_MUTEX_T);/* Copy the values from the attribute.  */int mutex_kind = imutexattr->mutexkind & ~PTHREAD_MUTEXATTR_FLAG_BITS;if ((imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_ROBUST) != 0){#ifndef __ASSUME_SET_ROBUST_LISTif ((imutexattr->mutexkind & PTHREAD_MUTEXATTR_FLAG_PSHARED) != 0&& !__nptl_set_robust_list_avail)return ENOTSUP;
#endifmutex_kind |= PTHREAD_MUTEX_ROBUST_NORMAL_NP;}switch (imutexattr->mutexkind & PTHREAD_MUTEXATTR_PROTOCOL_MASK){case PTHREAD_PRIO_INHERIT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:mutex_kind |= PTHREAD_MUTEX_PRIO_INHERIT_NP;break;case PTHREAD_PRIO_PROTECT << PTHREAD_MUTEXATTR_PROTOCOL_SHIFT:mutex_kind |= PTHREAD_MUTEX_PRIO_PROTECT_NP;int ceiling = (imutexattr->mutexkind& PTHREAD_MUTEXATTR_PRIO_CEILING_MASK)>> PTHREAD_MUTEXATTR_PRIO_CEILING_SHIFT;if (! ceiling){/* See __init_sched_fifo_prio.  */if (atomic_load_relaxed (&__sched_fifo_min_prio) == -1)__init_sched_fifo_prio ();if (ceiling < atomic_load_relaxed (&__sched_fifo_min_prio))ceiling = atomic_load_relaxed (&__sched_fifo_min_prio);}mutex->__data.__lock = ceiling << PTHREAD_MUTEX_PRIO_CEILING_SHIFT;break;default:break;}/* The kernel when waking robust mutexes on exit never usesFUTEX_PRIVATE_FLAG FUTEX_WAKE.  */if ((imutexattr->mutexkind & (PTHREAD_MUTEXATTR_FLAG_PSHARED| PTHREAD_MUTEXATTR_FLAG_ROBUST)) != 0)mutex_kind |= PTHREAD_MUTEX_PSHARED_BIT;

设置__kind的值

/* See concurrency notes regarding __kind in struct __pthread_mutex_sin sysdeps/nptl/bits/thread-shared-types.h.  */atomic_store_relaxed (&(mutex->__data.__kind), mutex_kind);/* Default values: mutex not used yet.  */// mutex->__count = 0;   already done by memset// mutex->__owner = 0;    already done by memset// mutex->__nusers = 0;   already done by memset// mutex->__spins = 0;    already done by memset// mutex->__next = NULL;  already done by memsetLIBC_PROBE (mutex_init, 1, mutex);

有人可能会有疑问,我们在用户态调用的函数是pthread_mutex_init,但是上面的函数名称是___pthread_mutex_init,这两个是同样的函数吗?
答案是肯定的,glibc中做了如下的声明,有关这部分声明大家可以追一下代码看一下,这里就不做详细分析。

4.___pthread_mutex_lock函数
同样地,我们在app层调用的函数时pthread_mutex_lock,它在glibc中的声明如下,也是和pthread_mutex_init相同的方式。

#/glibc-2.34/nptl/pthread_mutex_lock.c
#if PTHREAD_MUTEX_VERSIONS
libc_hidden_ver (___pthread_mutex_lock, __pthread_mutex_lock)
# ifndef SHARED
strong_alias (___pthread_mutex_lock, __pthread_mutex_lock)
# endif
//这里声明了pthread_mutex_lock,实际上就是___pthread_mutex_lock
versioned_symbol (libpthread, ___pthread_mutex_lock, pthread_mutex_lock, GLIBC_2_0);# if OTHER_SHLIB_COMPAT (libpthread, GLIBC_2_0, GLIBC_2_34)
compat_symbol (libpthread, ___pthread_mutex_lock, __pthread_mutex_lock,GLIBC_2_0);
# endif
#endif /* PTHREAD_MUTEX_VERSIONS */

接下来,我们看___pthread_mutex_lock函数,它的声明如下,___pthread_mutex_lock函数实际上就是PTHREAD_MUTEX_LOCK。

# define PTHREAD_MUTEX_LOCK ___pthread_mutex_lock

再看PTHREAD_MUTEX_LOCK,完整实现部分如下

int
PTHREAD_MUTEX_LOCK (pthread_mutex_t *mutex)
{/* See concurrency notes regarding mutex type which is loaded from __kindin struct __pthread_mutex_s in sysdeps/nptl/bits/thread-shared-types.h.  */unsigned int type = PTHREAD_MUTEX_TYPE_ELISION (mutex);LIBC_PROBE (mutex_entry, 1, mutex);if (__builtin_expect (type & ~(PTHREAD_MUTEX_KIND_MASK_NP| PTHREAD_MUTEX_ELISION_FLAGS_NP), 0))return __pthread_mutex_lock_full (mutex);if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_NP)){FORCE_ELISION (mutex, goto elision);simple:/* Normal mutex.  */LLL_MUTEX_LOCK_OPTIMIZED (mutex);assert (mutex->__data.__owner == 0);}
#if ENABLE_ELISION_SUPPORTelse if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_ELISION_NP)){elision: __attribute__((unused))/* This case can never happen on a system without elision,as the mutex type initialization functions will notallow to set the elision flags.  *//* Don't record owner or users for elision case.  This is atail call.  */return LLL_MUTEX_LOCK_ELISION (mutex);}
#endifelse if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)== PTHREAD_MUTEX_RECURSIVE_NP, 1)){/* Recursive mutex.  */pid_t id = THREAD_GETMEM (THREAD_SELF, tid);/* Check whether we already hold the mutex.  */if (mutex->__data.__owner == id){/* Just bump the counter.  */if (__glibc_unlikely (mutex->__data.__count + 1 == 0))/* Overflow of the counter.  */return EAGAIN;++mutex->__data.__count;return 0;}/* We have to get the mutex.  */LLL_MUTEX_LOCK_OPTIMIZED (mutex);assert (mutex->__data.__owner == 0);mutex->__data.__count = 1;}else if (__builtin_expect (PTHREAD_MUTEX_TYPE (mutex)== PTHREAD_MUTEX_ADAPTIVE_NP, 1)){if (LLL_MUTEX_TRYLOCK (mutex) != 0){int cnt = 0;int max_cnt = MIN (max_adaptive_count (),mutex->__data.__spins * 2 + 10);do{if (cnt++ >= max_cnt){LLL_MUTEX_LOCK (mutex);break;}atomic_spin_nop ();}while (LLL_MUTEX_TRYLOCK (mutex) != 0);mutex->__data.__spins += (cnt - mutex->__data.__spins) / 8;}assert (mutex->__data.__owner == 0);}else{pid_t id = THREAD_GETMEM (THREAD_SELF, tid);assert (PTHREAD_MUTEX_TYPE (mutex) == PTHREAD_MUTEX_ERRORCHECK_NP);/* Check whether we already hold the mutex.  */if (__glibc_unlikely (mutex->__data.__owner == id))return EDEADLK;goto simple;}pid_t id = THREAD_GETMEM (THREAD_SELF, tid);/* Record the ownership.  */mutex->__data.__owner = id;
#ifndef NO_INCR++mutex->__data.__nusers;
#endifLIBC_PROBE (mutex_acquired, 1, mutex);return 0;
}

我们逐步分析这个函数中的实现,如下是参数检查

LIBC_PROBE (mutex_entry, 1, mutex);

如下是普通mutex(mutexkind = PTHREAD_MUTEX_NORMAL,也就是我们在电泳pthread_mutex_init设置的mutex类型)的调用,也是用户最常用的mutex类型,我们主要关注这段实现

if (__glibc_likely (type == PTHREAD_MUTEX_TIMED_NP)){FORCE_ELISION (mutex, goto elision);simple:/* Normal mutex.  */LLL_MUTEX_LOCK_OPTIMIZED (mutex);assert (mutex->__data.__owner == 0);}

显然这个if分支中,主要是调用了宏LLL_MUTEX_LOCK_OPTIMIZED,现在我们重点关注一下这个宏

# define LLL_MUTEX_LOCK_OPTIMIZED(mutex) lll_mutex_lock_optimized (mutex)
static inline void
lll_mutex_lock_optimized (pthread_mutex_t *mutex)
{/* The single-threaded optimization is only valid for privatemutexes.  For process-shared mutexes, the mutex could be in ashared mapping, so synchronization with another process is neededeven without any threads.  If the lock is already marked asacquired, POSIX requires that pthread_mutex_lock deadlocks fornormal mutexes, so skip the optimization in that case aswell.  */int private = PTHREAD_MUTEX_PSHARED (mutex);if (private == LLL_PRIVATE && SINGLE_THREAD_P && mutex->__data.__lock == 0)mutex->__data.__lock = 1;elselll_lock (mutex->__data.__lock, private);
}

显然这个函数中主要是调用了lll_lock,我们继续看lll_lock宏的实现

#define lll_lock(futex, private) \__lll_lock (&(futex), private)

继续看__lll_lock宏定义,如下

/* This is an expression rather than a statement even though its value isvoid, so that it can be used in a comma expression or as an expressionthat's cast to void.  */
/* The inner conditional compiles to a call to __lll_lock_wait_private ifprivate is known at compile time to be LLL_PRIVATE, and to a call to__lll_lock_wait otherwise.  */
/* If FUTEX is 0 (not acquired), set to 1 (acquired with no waiters) andreturn.  Otherwise, ensure that it is >1 (acquired, possibly with waiters)and then block until we acquire the lock, at which point FUTEX will still be>1.  The lock is always acquired on return.  */
#define __lll_lock(futex, private)                                      \((void)                                                               \({                                                                   \int *__futex = (futex);                                            \if (__glibc_unlikely                                               \(atomic_compare_and_exchange_bool_acq (__futex, 1, 0)))        \{                                                                \if (__builtin_constant_p (private) && (private) == LLL_PRIVATE) \__lll_lock_wait_private (__futex);                           \else                                                           \__lll_lock_wait (__futex, private);                          \}                                                                \}))

Linux线程同步(三)---互斥锁源码分析相关推荐

  1. Linux线程同步(二)---互斥锁实现线程同步

    一 why 先给自己打个广告,本人的微信公众号:嵌入式Linux江湖,主要关注嵌入式软件开发,股票基金定投,足球等等,希望大家多多关注,有问题可以直接留言给我,一定尽心尽力回答大家的问题. 在博客&l ...

  2. linux线程同步之互斥锁——linux的关键区域

    在windows中,为了让多个线程达到同步的目的,在对于全局变量等大家都要用的资源的使用上,通常得保证同时只能由一个线程在用,一个线程没有宣布对它的释放之前,不能够给其他线程使用这个变量.在windo ...

  3. golang RWMutex读写互斥锁源码分析

    针对Golang 1.9的sync.RWMutex进行分析,与Golang 1.10基本一样除了将panic改为了throw之外其他的都一样. RWMutex是读写互斥锁.锁可以由任意数量的读取器或单 ...

  4. C++ 线程同步之互斥锁

    文章目录 1.简介 2.std::mutex 3.线程同步 4.std::lock_guard 5.std::recursive_mutex-少用 6.std::timed_mutex 1.简介 进行 ...

  5. Linux内存管理 brk(),mmap()系统调用源码分析2:brk()的内存释放流程

    Linux brk(),mmap()系统调用源码分析 brk()的内存释放流程 荣涛 2021年4月30日 内核版本:linux-5.10.13 注释版代码:https://github.com/Rt ...

  6. jieba tfidf_【NLP】【三】jieba源码分析之关键字提取(TF-IDF/TextRank)

    [一]综述 利用jieba进行关键字提取时,有两种接口.一个基于TF-IDF算法,一个基于TextRank算法.TF-IDF算法,完全基于词频统计来计算词的权重,然后排序,在返回TopK个词作为关键字 ...

  7. (转)Linux设备驱动之HID驱动 源码分析

    //Linux设备驱动之HID驱动 源码分析 http://blog.chinaunix.net/uid-20543183-id-1930836.html HID是Human Interface De ...

  8. Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析

    Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析 在NTM系列文章(二)中,我们已经成功运行了一个ntm工程的源代码.在这一章中,将对它的源码实现进行分析. ...

  9. 信号灯文件锁linux线程,linux——线程同步(互斥量、条件变量、信号灯、文件锁)...

    一.说明 linux的线程同步涉及: 1.互斥量 2.条件变量 3.信号灯 4.文件读写锁 信号灯很多时候被称为信号量,但个人仍觉得叫做信号灯比较好,因为可以与"SYSTEM V IPC的信 ...

最新文章

  1. 一边动,一边画,自己就变二次元!华人小哥参与的黑科技:实时交互式视频风格化...
  2. pyqt5从子目录加载qrc文件_PyQt5快速上手基础篇10-QSettings用法
  3. The Historical Accident of Waterfall Validity--瀑布开发模型在历史事件中的印证
  4. Matlab Simulink如何生成谐波
  5. Weights Assignment For Tree Edges 树,拓扑序(1500)
  6. console react 去除_vue或react项目生产环境去掉console.log的操作
  7. 【java】多线程控制(二)- - -线程池
  8. 微信终于可以隐身了,快看看!
  9. PHP中关于时间,时间戳 时区的设置问题
  10. [人工智能-综述-3]:人工智能与硅基生命,人类终将成为造物主
  11. HealthKit入门:第2部分
  12. COMPILATION ERROR
  13. 开发一个类似美团的外卖小程序多少钱
  14. verilog键盘输入示例代码及分析(摩尔型有限状态机)
  15. Android仿同花顺自选股列表控件
  16. 小米手机关闭广告的方法,三步让你的小米手机跟广告说再见
  17. flashback table 闪回表到指定时间或SCN
  18. C# Hello World 实例
  19. 灰色GM(1,1)模型及其在电力负荷预测中的应用附Matlab代码
  20. 京东 探索星球瓜分 1000 亿京豆 脚本

热门文章

  1. fluent 对电机油冷分析_油冷机冬季常见故障、原因分析及排除方法
  2. 一文带你熟悉android的smali语法一
  3. PostgreSQL 简介
  4. Hive面试基本须知
  5. 打破传统桎梏,挑战性能巅峰,网友:这轻薄本性能强的像游戏本
  6. myeclipse2014版本破解出现的问题,过程及解决方案
  7. SAP调用外围系统接口SXI_MONITOR有显示但是实际上没有调用接口的问题
  8. 列表页详情页html源码,UI布局欣赏:文章列表与内容详情页设计
  9. python处理点云数据_python将指定点云文件(asc)转换为PCD格式
  10. 数据对象与对象之间相似度与相异度的度量