热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Linux线程互斥有几种,在Linux下,信号量和线程互斥锁的实现都是通过futex系统调用。...

在Linux下,信号量和线程互斥锁的实现都是通过futex系统调用。futex(快速用户区互斥的简称)是一个在Linux上实现锁定和构建高级抽象锁如信号量和POSIX

在Linux下,信号量和线程互斥锁的实现都是通过futex系统调用。

futex(快速用户区互斥的简称)是一个在Linux上实现锁定和构建高级抽象锁如信号量和POSIX互斥的基本工具。它们第一次出现在内核开发的2.5.7版;其语义在2.5.40固定下来,然后在2.6.x系列稳定版内核中出现。

Futex 是fast userspace

mutex的缩写,意思是快速用户空间互斥体。Linux内核把它们作为快速的用户空间的锁和信号量的预制构件提供给开发者。Futex非常基础,借助其自身的优异性能,构建更高级别的锁的抽象,如POSIX互斥体。大多数程序员并不需要直接使用Futex,它一般用来实现像NPTL这样的系统库。

Futex

由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成;这个整型变量的值能够通过汇编语言调用CPU提供的原子操作指令来增加或减少,并且一个进程可以等待直到那个值变成正数。Futex

的操作几乎全部在应用程序空间完成;只有当操作结果不一致从而需要仲裁时,才需要进入操作系统内核空间执行。这种机制允许使用 futex

的锁定原语有非常高的执行效率:由于绝大多数的操作并不需要在多个进程之间进行仲裁,所以绝大多数操作都可以在应用程序空间执行,而不需要使用(相对高代价的)内核系统调用。 ----------------------------------------------------------------

插播一段关于x86原子操作指令的说明:

cmpxchg 比较交换指令,其语义为:

int CompareAndExchange(int *ptr, int old, int new)

{

int actual = *ptr;

if (actual == old)

*ptr = new;

return actual;

}

Intel白皮书上的说明如下:

(* Accumulator = AL, AX, EAX, or RAX depending on whether a byte,

word, doubleword, or

quadword comparison is being performed *)

IF accumulator = DEST

THEN

ZF ← 1;

DEST ← SRC;

ELSE

ZF ← 0;

accumulator ← DEST;

FI;

使用此原子操作可以实现自旋锁,之前有一篇文章中描述了实现:

void lock(lock_t *lock) {

while (CompareAndExchange(&lock->flag, 0, 1) == 1)

; // spin

}

void unlock(lock_t *lock) {

lock->flag = 0;

}

关于smp下的原子操作的一些说明:

原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断。在单处理器系统(UniProcessor)中,能够在单条指令中完成的操作都可以认为是"

原子操作",因为中断只能发生于指令之间。这也是某些CPU指令系统中引入了test_and_set、test_and_clear等指令用于临界资源互斥的原因。在对称多处理器(Symmetric

Multi-Processor)结构中就不同了,由于系统中有多个处理器在独立地运行,即使能在单条指令中完成的操作也有可能受到干扰。

在x86 平台上,CPU提供了在指令执行期间对总线加锁 的手段。CPU芯片上有一条引线#HLOCK

pin,如果汇编语言的程序中在一条指令前面加上前缀"LOCK" ,经过汇编以后的机器代码就使CPU在执行这条指令的时候把#HLOCK

pin的电位拉低,持续到这条指令结束时放开,从而把总线锁住,这样同一总线上别的CPU就暂时不能通过总线访问内存了,保证了这条指令在多处理器环境中的原子性。

当然,并不是所有的指令前面都可以加lock前缀的,只有ADD, ADC, AND, BTC,

BTR, BTS, CMPXCHG,DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, 和

XCHG指令前面可以加lock指令,实现原子操作。

----------------------------------------------------------------

广告回来了,我们继续。

futex保存在用户空间的共享内存中,并且通过原子操作进行操作。在大部分情况下,资源不存在争用的情况下,进程或者线程可以立刻获得资源成功,实际上就没有必要调用系统调用,陷入内核了。实际上,futex的作用就在于减少系统调用的次数,来提高系统的性能。

线程互斥锁pthread_mutex_t的实现原理:

pthread_mutex_lock:

atomic_dec(pthread_mutex_t.value);

if(pthread_mutex_t.value!=0)

futex(WAIT)

else

success

pthread_mutex_unlock:

atomic_inc(pthread_mutex_t.value);

if(pthread_mutex_t.value!=1) futex(WAKEUP) else

success

信号量sem_t的实现原理(直接从glibc/nptl/DESIGN-sem.txt中摘的):

sem_wait(sem_t *sem)

{

for (;;) {

if

(atomic_decrement_if_positive(sem->count))

break;

futex_wait(&sem->count, 0)

}

}

sem_post(sem_t *sem)

{

n = atomic_increment(sem->count);

// Pass the new value of sem->count

futex_wake(&sem->count, n +

1); }

对比,pthread_mutex_unlock()和sem_post()的实现,我们发现一个不同点,sem_post()无论如何都会调用

futex_wake(),进行系统调用。但是pthread_mutex_unlock()却符合futex的初衷,只有在需要仲裁的时候才调用

futex_wake()。那么什么是仲裁条件呢?

前面说过信号量和线程互斥锁语义上的区别在于信号量的value>=0,而线程互斥锁的value可以为负数。

对于lock操作,这两个倒是没有多少差别。信号量只要value>0就可以获得资源,线程互斥锁需要value=1。

但是对于unlock操作,这两个就有一些差别了。信号量和线程互斥锁,都会增加对应的value。如果加1后,value为1,对于线程互斥锁来讲,实际上表明资源可用,并且之前没有其他的线程在等待这个资源;否则说明还有其他线程在等待这个资源,需要调用futex系统调用唤醒它们。但是对于信号量,由于value必须>=0。那么加1后,即使value为1,也无法判定现在没有其他的进程或线程正在等待资源,所以必须调用futex系统调用。 例如:

#include

#include

#include

sem_t sem_a;

void *task1();

int main(void)

{

int ret=0;

pthread_t thrd1;

pthread_t thrd2;

sem_init(&sem_a,0,1);

ret=pthread_create(&thrd1,NULL,task1,NULL);

//创建子线程

ret=pthread_create(&thrd2,NULL,task1,NULL);

//创建子线程

pthread_join(thrd1,NULL); //等待子线程结束

pthread_join(thrd2,NULL); //等待子线程结束

}

void *task1()

{

int sval = 0;

sem_wait(&sem_a); //持有信号量

sleep(5); //do_nothing

sem_getvalue(&sem_a,&sval);

printf("sem value = %d/n",sval);

sem_post(&sem_a); //释放信号量

}

上面sem的value初始化为1,但是有两个线程争用资源。那么第一个线程获得资源成功,当它unlock的时候,sem的value变为1。但是,这个时候,实际上还有一个线程在等待资源。因此,必须要进行futex_wake()系统调用,唤醒等待资源的线程。

感兴趣的同学可以使用strace跟踪一下,进行验证。要注意忽略程序运行初始化的那个futex_wake ;-)



推荐阅读
  • 在CentOS 7环境中安装配置Redis及使用Redis Desktop Manager连接时的注意事项与技巧
    在 CentOS 7 环境中安装和配置 Redis 时,需要注意一些关键步骤和最佳实践。本文详细介绍了从安装 Redis 到配置其基本参数的全过程,并提供了使用 Redis Desktop Manager 连接 Redis 服务器的技巧和注意事项。此外,还探讨了如何优化性能和确保数据安全,帮助用户在生产环境中高效地管理和使用 Redis。 ... [详细]
  • 深入解析C语言中结构体的内存对齐机制及其优化方法
    为了提高CPU访问效率,C语言中的结构体成员在内存中遵循特定的对齐规则。本文详细解析了这些对齐机制,并探讨了如何通过合理的布局和编译器选项来优化结构体的内存使用,从而提升程序性能。 ... [详细]
  • 在操作系统中,阻塞状态与挂起状态有着显著的区别。阻塞状态通常是指进程因等待某一事件(如I/O操作完成)而暂时停止执行,而挂起状态则是指进程被系统暂时移出内存,以释放资源或降低系统负载。此外,本文还深入分析了`sleep()`函数的实现机制,探讨了其在不同操作系统中的具体实现方式及其对进程调度的影响。通过这些分析,读者可以更好地理解操作系统如何管理进程的不同状态以及`sleep()`函数在其中的作用。 ... [详细]
  • centos 7.0 lnmp成功安装过程(很乱)
    下载nginx[rootlocalhostsrc]#wgethttp:nginx.orgdownloadnginx-1.7.9.tar.gz--2015-01-2412:55:2 ... [详细]
  • 本文整理了一份基础的嵌入式Linux工程师笔试题,涵盖填空题、编程题和简答题,旨在帮助考生更好地准备考试。 ... [详细]
  • MySQL Decimal 类型的最大值解析及其在数据处理中的应用艺术
    在关系型数据库中,表的设计与SQL语句的编写对性能的影响至关重要,甚至可占到90%以上。本文将重点探讨MySQL中Decimal类型的最大值及其在数据处理中的应用技巧,通过实例分析和优化建议,帮助读者深入理解并掌握这一重要知识点。 ... [详细]
  • 在Linux系统中避免安装MySQL的简易指南
    在Linux系统中避免安装MySQL的简易指南 ... [详细]
  • Android 构建基础流程详解
    Android 构建基础流程详解 ... [详细]
  • 在 Linux 环境下,多线程编程是实现高效并发处理的重要技术。本文通过具体的实战案例,详细分析了多线程编程的关键技术和常见问题。文章首先介绍了多线程的基本概念和创建方法,然后通过实例代码展示了如何使用 pthreads 库进行线程同步和通信。此外,还探讨了多线程程序中的性能优化技巧和调试方法,为开发者提供了宝贵的实践经验。 ... [详细]
  • 本文详细探讨了Zebra路由软件中的线程机制及其实际应用。通过对Zebra线程模型的深入分析,揭示了其在高效处理网络路由任务中的关键作用。文章还介绍了线程同步与通信机制,以及如何通过优化线程管理提升系统性能。此外,结合具体应用场景,展示了Zebra线程机制在复杂网络环境下的优势和灵活性。 ... [详细]
  • 在Linux系统中,为了提高安全性,可以通过设置命令执行超时和用户超时注销来防止因用户长时间未操作而带来的安全隐患。具体而言,可以通过编辑 `/etc/profile` 文件,添加或修改相关参数,确保用户在指定时间内无操作后自动注销。此外,还可以利用 `timeout` 命令来限制特定命令的执行时间,进一步增强系统的稳定性和安全性。 ... [详细]
  • 本文深入探讨了IO复用技术的原理与实现,重点分析了其在解决C10K问题中的关键作用。IO复用技术允许单个进程同时管理多个IO对象,如文件、套接字和管道等,通过系统调用如`select`、`poll`和`epoll`,高效地处理大量并发连接。文章详细介绍了这些技术的工作机制,并结合实际案例,展示了它们在高并发场景下的应用效果。 ... [详细]
  • 欢迎来到Netgen新时代:探索网络生成技术的无限可能
    欢迎进入Netgen的新时代:探索网络生成技术的无限潜力。本文将详细介绍如何编译下载的Netgen源代码,生成Netgen程序,并提供开发所需的库nglib。此外,还将探讨Netgen在现代网络设计与仿真中的应用前景,以及其在提高网络性能和可靠性方面的关键作用。 ... [详细]
  • 基于bionic c分析线程的一生
    1.概述和问题进程和线程操作系统基础和重要的机制,从源码角度理解进程和线程的区别对于理解操作系统的基本原理非常有帮助,同时进程和线程的创建又是通过系统 ... [详细]
  • Linux多线程(2)
    线程的知识点太多,太重要,所以分成三部分进行总结学习线程安全多个线程并发同一段代码时,不会出现不同的结果。常见对全局变量或者静态变量进行操作,并且没有锁保护的情况下,会出现该问题。 ... [详细]
author-avatar
極點創意文化
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有