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

futex函数_UNIX/LINUX

futex函数_UNIXLINUX引子在编译2.6内核的时候,你会在编译选项中看到[*]Enablefutexsupport这一项,上网查,有的资料会告诉你不选这个内核不一定能正


Linux中的线程同步机制(二)–In Glibc

在linux中进行多线程开发,同步是不可回避的一个问题。
在POSIX标准中定义了三种线程同步机制: Mutexes(互斥量), Condition Variables(条件变量)和POSIX Semaphores(信号量)
。NPTL基本上实现了POSIX,而glibc又使用NPTL作为自己的线程库。因此glibc中包含了这三种同步机制 的实现(当然还包括其他的同步机制,如APUE里提到的读写锁)。




Glibc中常用的线程同步方式举例:




Semaphore


变量定义:    sem_t sem;


初始化:      sem_init(&sem,0,1);


进入加锁:     sem_wait(&sem);


退出解锁:     sem_post(&sem);




Mutex


变量定义:    pthread_mutex_t mut;


初始化:      pthread_mutex_init(&mut,NULL);


进入加锁:     pthread_mutex_lock(&mut);


退出解锁:     pthread_mutex_unlock(&mut);






这些用于同步的函数和futex有什么关系?下面让我们来看一看:


以Semaphores为例,


进入互斥区的时候,会执行sem_wait(sem_t *sem),sem_wait的实现如下:


int sem_wait (sem_t *sem)


{



int *futex = (int *) sem;


if (atomic_decrement_if_positive (futex) > 0)


    return 0;


int   err = lll_futex_wait (futex, 0);


    return -1;


)


atomic_decrement_if_positive()的语义就是如果传入参数是正数就将其原子性的减一并立即返回。如果信号量为正,在Semaphores的语义中意味着没有竞争发生,如果没有竞争,就给信号量减一后直接返回了。




如果传入参数不是正数,即意味着有竞争,调用lll_futex_wait(futex,0),lll_futex_wait是个宏,展开后为:


#define lll_futex_wait(futex, val) \


({                                          \


    …


    __asm __volatile (LLL_EBX_LOAD                          \


              LLL_ENTER_KERNEL                          \


              LLL_EBX_LOAD                          \


              : “=a” (__status)                          \


              : “0” (SYS_futex), LLL_EBX_REG (futex), “S” (0),          \


            “c” (FUTEX_WAIT), “d” (_val),                  \


            “i” (offsetof (tcbhead_t, sysinfo))              \


              : “memory”);                          \


    …                                      \


})


可以看到当发生竞争的时候,sem_wait会调用SYS_futex系统调用,并在val=0的时候执行FUTEX_WAIT,让当前线程休眠。




从 这个例子我们可以看出,在Semaphores的实现过程中使用了futex,不仅仅是说其使用了futex系统调用(再重申一遍只使用futex系统调 用是不够的),而是整个建立在futex机制上,包括用户态下的操作和核心态下的操作。其实对于其他glibc的同步机制来说也是一样,都采纳了 futex作为其基础。所以才会在futex的manual中说:对于大多数程序员不需要直接使用futexes,取而代之的是依靠建立在futex之上 的系统库,如NPTL线程库(most programmers will in fact not be using futexes directly but instead rely on system libraries built on them, such as the NPTL pthreads implementation)。所以才会有如果在编译内核的时候不 Enable futex support,就”不一定能正确的运行使用Glibc的程序”。




小结:


1. Glibc中的所提供的线程同步方式,如大家所熟知的Mutex,Semaphore等,大多都构造于futex之上了,除了特殊情况,大家没必要再去实现自己的futex同步原语。
2. 大家要做的事情,似乎就是按futex的manual中所说得那样: 正确的使用Glibc所提供的同步方式,并在使用它们的过程中,意识到它们是利用futex机制和linux配合完成同步操作就可以了。




Linux中的线程同步机制(三)–Practice



上回说到Glibc中(NPTL)的线程同步方式如Mutex,Semaphore等都使用了futex作为其基础。那么实际使用是什么样子,又会碰到什么问题呢?


先来看一个使用semaphore同步的例子。




sem_t sem_a;


void *task1();




int main(void){



int ret=0;


pthread_t thrd1;


sem_init(&sem_a,0,1);


ret=pthread_create(&thrd1,NULL,task1,NULL); //创建子线程


pthread_join(thrd1,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); //释放信号量


}




程序很简单,我们在主线程(执行main的线程)中创建了一个线程,并用join等待其结束。在子线程中,先持有信号量,然后休息一会儿,再释放信号量,结束。


因为这段代码中只有一个线程使用信号量,也就是没有线程间竞争发生,按照futex的理论,因为没有竞争,所以所有的锁操作都将在用户态中完成,而不会执行系统调用而陷入内核。我们用
strace
来跟踪一下这段程序的执行过程中所发生的系统调用:


…


20533 futex(0xb7db1be8, FUTEX_WAIT, 20534, NULL


20534 futex(0x8049870, FUTEX_WAKE, 1)   = 0


20533 <&#8230; futex resumed> )             = 0


&#8230; 


20533是main线程的id,20534是其子线程的id。出乎我们意料之外的是这段程序还是发生了两次futex系统调用,我们来分析一下这分别是什么原因造成的。




1. 
出人意料的&#8221;sem_post()&#8221;


20534 futex(0x8049870, FUTEX_WAKE, 1)   = 0


子 线程还是执行了FUTEX_WAKE的系统调用,就是在sem_post(&sem_a);的时候,请求内核唤醒一个等待在sem_a上的线程, 其返回值是0,表示现在并没有线程等待在sem_a(这是当然的,因为就这么一个线程在使用sem_a),这次futex系统调用白做了。这似乎和 futex的理论有些出入,我们再来看一下sem_post的实现。


int sem_post (sem_t *sem)


{



int *futex = (int *) sem;


int nr = atomic_increment_val (futex);


int err = lll_futex_wake (futex, nr);


return 0;


}


我们看到,Glibc在实现sem_post的时候给futex原子性的加上1后,不管futex的值是什么,都执行了lll_futex_wake(),即futex(FUTEX_WAKE)系统调用。


在 第二部分中(见前文),我们分析了sem_wait的实现,当没有竞争的时候是不会有futex调用的,现在看来真的是这样,但是在sem_post的时 候,无论有无竞争,都会调用sys_futex(),为什么会这样呢?我觉得应该结合semaphore的语义来理解。
在semaphore的语义 中,sem_wait()的意思是:&#8221;挂起当前进程,直到semaphore的值为非0,它会原子性的减少semaphore计数值。&#8221; 
我们可以看到,semaphore中是通过0或者非0来判断阻塞或者非阻塞线程。即无论有多少线程在竞争这把锁,只要使用了 semaphore,semaphore的值都会是0。这样,
当线程推出互斥区,执行sem_post(),释放semaphore的时候,将其值由0改 1,并不知道是否有线程阻塞在这个semaphore上,所以只好不管怎么样都执行futex(uaddr, FUTEX_WAKE, 1)尝试着唤醒一个进程。
而相反的,当sem_wait(),如果semaphore由1变0,则意味着没有竞争发生,所以不必去执行futex系统调 用。我们假设一下,如果抛开这个语义,如果允许semaphore值为负,则也可以在sem_post()的时候,实现futex机制。




2. 半路杀出的&#8221;pthread_join()&#8221;


那另一个futex系统调用是怎么造成的呢? 是因为pthread_join();


在Glibc中,pthread_join也是用futex系统调用实现的。程序中的pthread_join(thrd1,NULL); 就对应着 


20533 futex(0xb7db1be8, FUTEX_WAIT, 20534, NULL


很 好解释,主线程要等待子线程(id号20534上)结束的时候,调用futex(FUTEX_WAIT),并把var参数设置为要等待的子线程号 (20534),然后等待在一个地址为0xb7db1be8的futex变量上。当子线程结束后,系统会负责把主线程唤醒。于是主线程就


20533 <&#8230; futex resumed> )             = 0


恢复运行了。


要注意的是,如果在执行pthread_join()的时候,要join的线程已经结束了,就不会再调用futex()阻塞当前进程了。




3. 更多的竞争。


我们把上面的程序稍微改改: 


在main函数中:


int main(void){



&#8230;


sem_init(&sem_a,0,1);


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


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


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


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


pthread_join(thrd1,NULL);


pthread_join(thrd2,NULL);


pthread_join(thrd3,NULL);


pthread_join(thrd4,NULL);


&#8230;


}




这样就有更的线程参与sem_a的争夺了。我们来分析一下,这样的程序会发生多少次futex系统调用。


1) sem_wait()


    第一个进入的线程不会调用futex,而其他的线程因为要阻塞而调用,因此sem_wait会造成3次futex(FUTEX_WAIT)调用。


2) sem_post()


    所有线程都会在sem_post的时候调用futex, 因此会造成4次futex(FUTEX_WAKE)调用。


3) pthread_join()


    别忘了还有pthread_join(),我们是按thread1, thread2, thread3, thread4这样来join的,但是线程的调度存在着随机性。如果thread1最后被调度,则只有thread1这一次futex调用,所以 pthread_join()造成的futex调用在1-4次之间。(虽然不是必然的,但是4次更常见一些)    


所以这段程序至多会造成3+4+4=11次futex系统调用,用strace跟踪,验证了我们的想法。


19710 futex(0xb7df1be8, FUTEX_WAIT, 19711, NULL


19712 futex(0x8049910, FUTEX_WAIT, 0, NULL


19713 futex(0x8049910, FUTEX_WAIT, 0, NULL


19714 futex(0x8049910, FUTEX_WAIT, 0, NULL


19711 futex(0x8049910, FUTEX_WAKE, 1


19710 futex(0xb75f0be8, FUTEX_WAIT, 19712, NULL


19712 futex(0x8049910, FUTEX_WAKE, 1


19710 futex(0xb6defbe8, FUTEX_WAIT, 19713, NULL


19713 futex(0x8049910, FUTEX_WAKE, 1


19710 futex(0xb65eebe8, FUTEX_WAIT, 19714, NULL


19714 futex(0x8049910, FUTEX_WAKE, 1)   = 0


(19710是主线程,19711,19712,19713,19714是4个子线程)




4. 更多的问题


事 情到这里就结束了吗? 
如果我们把semaphore换成Mutex试试。你会发现当自始自终没有竞争的时候,mutex会完全符合futex机制,不管是lock还是 unlock都不会调用futex系统调用。有竞争的时候,第一次pthread_mutex_lock的时候不会调用futex调用,看起来还正常。但 是最后一次pthread_mutex_unlock的时候,虽然已经没有线程在等待mutex了,可还是会调用futex(FUTEX_WAKE)。原因是什么?欢迎讨论!!!




小结:


1. 虽然semaphore,mutex等同步方式构建在futex同步机制之上。然而受其语义等的限制,并没有完全按futex最初的设计实现。
2. pthread_join()等函数也是调用futex来实现的。
3. 不同的同步方式都有其不同的语义,不同的性能特征,适合于不同的场景。我们在使用过程中要知道他们的共性,也得了解它们之间的差异。这样才能更好的理解多线程场景,写出更高质量的多线程程序。





转载地址:


http://blog.csdn.net/Javadino/archive/2008/09/06/2891385.aspx


http://blog.csdn.net/Javadino/archive/2008/09/06/2891388.aspx

http://blog.csdn.net/Javadino/archive/2008/09/06/2891399.aspx

Linux中的线程同步机制(四)&#8211;C语言实现


futex 的逻辑可以用如下C语言表示

int val = 0;
void lock()
{

    int c
    if ((c = cmpxchg(val, 0, 1)) != 0) {

        if (c != 2)
            c = xchg(val, 2);
        while (c != 0) {

            futex_wait((&val, 2);
            c = xchg(val, 2);
        }
    }
}   
    
void unlock()
{   
    if (atomic_dec(val) != 1)
        futex_wake(&val, 1);
}


val 0: unlock

val 1: lock, no waiters

val2 : lock , one or more waiters


推荐阅读
  • 本文介绍了在Windows系统上使用C语言命令行参数启动程序并传递参数的方法,包括接收参数程序的代码和bat文件的编写方法,同时给出了程序运行的结果。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • MyBatis多表查询与动态SQL使用
    本文介绍了MyBatis多表查询与动态SQL的使用方法,包括一对一查询和一对多查询。同时还介绍了动态SQL的使用,包括if标签、trim标签、where标签、set标签和foreach标签的用法。文章还提供了相关的配置信息和示例代码。 ... [详细]
  • OO第一单元自白:简单多项式导函数的设计与bug分析
    本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 模板引擎StringTemplate的使用方法和特点
    本文介绍了模板引擎StringTemplate的使用方法和特点,包括强制Model和View的分离、Lazy-Evaluation、Recursive enable等。同时,还介绍了StringTemplate语法中的属性和普通字符的使用方法,并提供了向模板填充属性的示例代码。 ... [详细]
  • 基于Socket的多个客户端之间的聊天功能实现方法
    本文介绍了基于Socket的多个客户端之间实现聊天功能的方法,包括服务器端的实现和客户端的实现。服务器端通过每个用户的输出流向特定用户发送消息,而客户端通过输入流接收消息。同时,还介绍了相关的实体类和Socket的基本概念。 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • 本文介绍了GTK+中的GObject对象系统,该系统是基于GLib和C语言完成的面向对象的框架,提供了灵活、可扩展且易于映射到其他语言的特性。其中最重要的是GType,它是GLib运行时类型认证和管理系统的基础,通过注册和管理基本数据类型、用户定义对象和界面类型来实现对象的继承。文章详细解释了GObject系统中对象的三个部分:唯一的ID标识、类结构和实例结构。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
author-avatar
开卷_汤娅造_673
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有