Linux进程同步_linux 进程同步-程序员宅基地

技术标签: Linux  null  负载均衡  ibm  linux  测试  class  


 

目录

Linux进程同步调研... 1

背景... 1

Futex. 1

进/线程利用futex同步... 2

性能对比... 3

参考文档... 5

 

 

背景

超级负载均衡需要在线程/进程之前共享数据,这些数据需要支持多写多读,因此需要考虑同步。

         本文调研了linux下的mutex与semaphone。

         目前线上2.6.9内核下,mutex与semaphone都是基于Futex。

 

Futex

Futex是由HubertusFranke(IBM Thomas J. Watson研究中心),Matthew Kirkwood,Ingo Molnar (Red Hat)和Rusty Russell (IBM Linux科技中心)设计并维护的。最初的Futex的支持是从Linux2.5.7开始的,但是以上述语义有些不同,当前的语义是从Linux2.5.40获得的。

 

Futex 由一块能够被多个进程共享的内存空间(一个对齐后的整型变量)组成;这个整型变量的值能够通过汇编语言调用CPU提供的原子操作指令来增加或减少,并且一个进程可以等待直到那个值变成正数。Futex 的操作几乎全部在应用程序空间完成;只有当操作结果不一致从而需要仲裁时,才需要进入操作系统内核空间执行。这种机制允许使用 futex 的锁定原语有非常高的执行效率:由于绝大多数的操作并不需要在多个进程之间进行仲裁,所以绝大多数操作都可以在应用程序空间执行,而不需要使用(相对高代价的)内核系统调用。

 

Futex是一种用户态和内核态混合机制,所以需要两个部分合作完成,linux上提供了sys_futex系统调用,对进程竞争情况下的同步处理提供支持。

其原型和系统调用号为

#include <linux/futex.h>

#include <sys/time.h>

int futex (int *uaddr, int op,int val, const struct timespec *timeout,int *uaddr2, int val3);

#define __NR_futex              240

虽然参数有点长,其实常用的就是前面三个,后面的timeout大家都能理解,其他的也常被ignore。

uaddr就是用户态下共享内存的地址,里面存放的是一个对齐的整型计数器。

op存放着操作类型。定义的有5中,这里我简单的介绍一下两种,剩下的感兴趣的自己去man futex

FUTEX_WAIT: 原子性的检查uaddr中计数器的值是否为val,如果是则让进程休眠,直到FUTEX_WAKE或者超时(time-out)。也就是把进程挂到uaddr相对应的等待队列上去。

FUTEX_WAKE: 最多唤醒val个等待在uaddr上进程。

 

在futex的系统调用中,FUTEX_WAIT和FUTEX_WAKE只是用来挂起或者唤醒进程,这部分工作只能在内核态下完成。futex同步机制还包括用户态下的操作(区分futex同步机制和futex系统调用)。

 

进/线程利用futex同步

进程或者线程都可以利用futex来进行同步。

对于线程,情况比较简单,因为线程共享虚拟内存空间,虚拟地址就可以唯一的标识出futex变量,即线程用同样的虚拟地址来访问futex变量。

对于进程,情况相对复杂,因为进程有独立的虚拟内存空间,只有通过mmap()让它们共享一段地址空间来使用futex变量。每个进程用来访问futex的虚拟地址可以是不一样的,只要系统知道所有的这些虚拟地址都映射到同一个物理内存地址,并用物理内存地址来唯一标识futex变量。

 

mutex的实现原理:

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的实现原理:

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()。

 

         通过strace跟踪也能印证,使用semaphone,就算没有出现互斥,在unlock,一定会调用futex(0x7fbffff490,FUTEX_WAKE, 1)。

         如下所示列出了unlock附近的strace结果:

         mutex的strace监控:

……

brk(0)                                  = 0x502000

brk(0x523000)                           = 0x523000

exit_group(0)                           = ?

 

         semaphone的strace监控:

……

brk(0)                                  = 0x502000

brk(0x523000)                           = 0x523000

futex(0x7fbffff490,FUTEX_WAKE, 1)      = 0

exit_group(0)                           = ?

 

 

性能对比

         性能测试说明:

         构造了三个临界区(按照我们的应用情况):

int lock_test1() { // 执行1条语句

    TbAutoLock _lock(lock);

    int id = g_shm_id;

    return id;

}

 

int lock_test2() { // 执行1000条语句

    TbAutoLock _lock(lock);

    int sum = 0;

    for(int i = 0; i < 1000; i++) {

        sum += i;

    }  

    return sum;

}

 

long long lock_test3() { // 执行10万条语句

    TbAutoLock _lock(lock);

    long long sum = 0;

    for(int i = 0; i < 100000; i++) {

        sum += i;

    }  

    return sum;

}

         然后:

    for(int i=0; i<100; i++) {

        for(int j=0; j<1000; j++) {

            lock_test1();

        }

        for(int j=0; j<100; j++) {

            lock_test2();

        }

        for(int j=0; j<1000; j++) {

            lock_test1();

        }

        for(int j=0; j<10; j++) {

            lock_test3();

        }

    }

         启动100个进程,测试这三种临界区。

 

         执行时间 & 对CPU的消耗如下表:

 

Mutex

Semaphone

执行时间

34575129 us 约34s

43529735 us 约43s

cpu_id

80

57

cpu_us

13

11

cpu_sy

7

32

         可以看出:

l  Mutex性能更优,cpu_sy明显低于Semaphone,这和之前结论一致;

l  综合cpu占用,在我们的应用下,Mutex的性能是Semaphone的两倍。

 

关于pthread_mutex用于进程模型

pthread_mutex用于进程模型的条件:

l  所在共享内存上分配;

l  设置锁的状态为:PTHREAD_PROCESS_SHARED

 

后面测试代码中有完整示例

 

参考文档

Linux中的线程同步机制(一二三):

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

关于信号量与线程互斥锁的区别与实现:

http://blog.csdn.net/borefo/archive/2009/11/21/4840284.aspx

 

 

相关测试代码

class TbLock {

public:

    static const char LOCK_TYPE_THREAD = 1;

    static const char LOCK_TYPE_PROCESS = 2;

protected:

    char _type;

public:

    virtual ~TbLock() {};

    virtual int lock() {

        return 0;

    };

    virtual int unlock() {

        return 0;

    };

};

 

class TbLockMutex : public TbLock{

private:

    pthread_mutex_t *_mutex_p;

public:

    TbLockMutex(pthread_mutex_t *mutex_p, chartype) {

        _mutex_p = mutex_p;

        _type = type;

        if (type == LOCK_TYPE_PROCESS) {

            pthread_mutexattr_t mat;

            pthread_mutexattr_init(&mat);

            pthread_mutexattr_setpshared(&mat,PTHREAD_PROCESS_SHARED);

            pthread_mutex_init(_mutex_p,&mat);

        } else {

            pthread_mutex_init(_mutex_p, NULL);

        }

    }

    ~TbLockMutex() {

        pthread_mutex_destroy(_mutex_p);

    }

    int lock() {

        return pthread_mutex_lock(_mutex_p);

    }

    int unlock() {

        return pthread_mutex_unlock(_mutex_p);

    }

};

 

class TbLockSema : public TbLock{

private:

    sem_t * _sem_p;

public:

    TbLockSema(sem_t * sem_p, char type) {

        _sem_p = sem_p;

        _type = type;

        if (type == LOCK_TYPE_PROCESS) {

            sem_init(_sem_p, 1, 1);

        } else {

            sem_init(_sem_p, 0, 1);

        }

    }

    ~TbLockSema() {

        sem_destroy(_sem_p);

    }

    int lock() {

        return sem_wait(_sem_p);

    }

    int unlock() {

        return sem_post(_sem_p);

    }

};

 

class TbLockFactory {

private:

    char * _lockBuf;

    uint _lockBufSize;

    uint _curLockNum;

    char _type;

    static const uint LOCK_SIZE = sizeof(pthread_mutex_t)> sizeof(sem_t) ? sizeof(pthread_mutex_t) : sizeof(sem_t);

 

public:

 

    TbLockFactory(char * lockBuf, intlockBufSize, char type):_lockBuf(lockBuf), _lockBufSize(lockBufSize),_type(type)  {

        _curLockNum = 0;

    }

 

    TbLock * getMutexLock() {

        pthread_mutex_t *mutex = NULL;

        if ((_curLockNum+1)*LOCK_SIZE <_lockBufSize) {

            mutex = (pthread_mutex_t*)(_lockBuf+ _curLockNum*LOCK_SIZE);

            _curLockNum++;

            return new TbLockMutex(mutex,_type);

        }

        return NULL;

    }

 

    TbLock * getSemaLock() {

        sem_t * sem = NULL;

        if ((_curLockNum+1)*LOCK_SIZE <_lockBufSize) {

            sem = (sem_t *)(_lockBuf +_curLockNum*LOCK_SIZE);

            _curLockNum++;

            return new TbLockSema(sem, _type);

        }

        return NULL;

    }

};

 

class TbAutoLock {

    TbLock * _lock;

public:

    TbAutoLock(TbLock * lock){

        _lock = lock;

        _lock->lock();

    }

    ~TbAutoLock(){

        _lock->unlock();

    }

};

 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/kofshower/article/details/7428685

智能推荐

获取大于等于一个整数的最小2次幂算法(HashMap#tableSizeFor)_整数 最小的2的几次方-程序员宅基地

文章浏览阅读2w次,点赞51次,收藏33次。一、需求给定一个整数,返回大于等于该整数的最小2次幂(2的乘方)。例: 输入 输出 -1 1 1 1 3 4 9 16 15 16二、分析当遇到这个需求的时候,我们可能会很容易想到一个"笨"办法:..._整数 最小的2的几次方

Linux 中 ss 命令的使用实例_ss@,,x,, 0-程序员宅基地

文章浏览阅读865次。选项,以防止命令将 IP 地址解析为主机名。如果只想在命令的输出中显示 unix套接字 连接,可以使用。不带任何选项,用来显示已建立连接的所有套接字的列表。如果只想在命令的输出中显示 tcp 连接,可以使用。如果只想在命令的输出中显示 udp 连接,可以使用。如果不想将ip地址解析为主机名称,可以使用。如果要取消命令输出中的标题行,可以使用。如果只想显示被侦听的套接字,可以使用。如果只想显示ipv4侦听的,可以使用。如果只想显示ipv6侦听的,可以使用。_ss@,,x,, 0

conda activate qiuqiu出现不存在activate_commandnotfounderror: 'activate-程序员宅基地

文章浏览阅读568次。CommandNotFoundError: 'activate'_commandnotfounderror: 'activate

Kafka 实战 - Windows10安装Kafka_win10安装部署kafka-程序员宅基地

文章浏览阅读426次,点赞10次,收藏19次。完成以上步骤后,您已在 Windows 10 上成功安装并验证了 Apache Kafka。在生产环境中,通常会将 Kafka 与外部 ZooKeeper 集群配合使用,并考虑配置安全、监控、持久化存储等高级特性。在生产者窗口中输入一些文本消息,然后按 Enter 发送。ZooKeeper 会在新窗口中运行。在另一个命令提示符窗口中,同样切换到 Kafka 的。Kafka 服务器将在新窗口中运行。在新的命令提示符窗口中,切换到 Kafka 的。,应显示已安装的 Java 版本信息。_win10安装部署kafka

【愚公系列】2023年12月 WEBGL专题-缓冲区对象_js 缓冲数据 new float32array-程序员宅基地

文章浏览阅读1.4w次。缓冲区对象(Buffer Object)是在OpenGL中用于存储和管理数据的一种机制。缓冲区对象可以存储各种类型的数据,例如顶点、纹理坐标、颜色等。在渲染过程中,缓冲区对象中存储的数据可以被复制到渲染管线的不同阶段中,例如顶点着色器、几何着色器和片段着色器等,以完成渲染操作。相比传统的CPU访问内存,缓冲区对象的数据存储和管理更加高效,能够提高OpenGL应用的性能表现。_js 缓冲数据 new float32array

四、数学建模之图与网络模型_图论与网络优化数学建模-程序员宅基地

文章浏览阅读912次。(1)图(Graph):图是数学和计算机科学中的一个抽象概念,它由一组节点(顶点)和连接这些节点的边组成。图可以是有向的(有方向的,边有箭头表示方向)或无向的(没有方向的,边没有箭头表示方向)。图用于表示各种关系,如社交网络、电路、地图、组织结构等。(2)网络(Network):网络是一个更广泛的概念,可以包括各种不同类型的连接元素,不仅仅是图中的节点和边。网络可以包括节点、边、连接线、路由器、服务器、通信协议等多种组成部分。网络的概念在各个领域都有应用,包括计算机网络、社交网络、电力网络、交通网络等。_图论与网络优化数学建模

随便推点

android 加载布局状态封装_adnroid加载数据转圈封装全屏转圈封装-程序员宅基地

文章浏览阅读1.5k次。我们经常会碰见 正在加载中,加载出错, “暂无商品”等一系列的相似的布局,因为我们有很多请求网络数据的页面,我们不可能每一个页面都写几个“正在加载中”等布局吧,这时候将这些状态的布局封装在一起就很有必要了。我们可以将这些封装为一个自定布局,然后每次操作该自定义类的方法就行了。 首先一般来说,从服务器拉去数据之前都是“正在加载”页面, 加载成功之后“正在加载”页面消失,展示数据;如果加载失败,就展示_adnroid加载数据转圈封装全屏转圈封装

阿里云服务器(Alibaba Cloud Linux 3)安装部署Mysql8-程序员宅基地

文章浏览阅读1.6k次,点赞23次,收藏29次。PS: 如果执行sudo grep 'temporary password' /var/log/mysqld.log 后没有报错,也没有任何结果显示,说明默认密码为空,可以直接进行下一步(后面设置密码时直接填写新密码就行)。3.(可选)当操作系统为Alibaba Cloud Linux 3时,执行如下命令,安装MySQL所需的库文件。下面示例中,将创建新的MySQL账号,用于远程访问MySQL。2.依次运行以下命令,创建远程登录MySQL的账号,并允许远程主机使用该账号访问MySQL。_alibaba cloud linux 3

excel离散度图表怎么算_excel离散数据表格-Excel 离散程度分析图表如何做-程序员宅基地

文章浏览阅读7.8k次。EXCEL中数据如何做离散性分析纠错。离散不是均值抄AVEDEV……=AVEDEV(A1:A100)算出来的是A1:A100的平均数。离散是指各项目间指标袭的离散均值(各数值的波动情况),数值较低表明项目间各指标波动幅百度小,数值高表明波动幅度较大。可以用excel中的离散公式为STDEV.P(即各指标平均离散)算出最终度离散度。excel表格函数求一组离散型数据,例如,几组C25的...用exc..._excel数据分析离散

学生时期学习资源同步-JavaSE理论知识-程序员宅基地

文章浏览阅读406次,点赞7次,收藏8次。i < 5){ //第3行。int count;System.out.println ("危险!System.out.println(”真”);System.out.println(”假”);System.out.print(“姓名:”);System.out.println("无匹配");System.out.println ("安全");

linux 性能测试磁盘状态监测:iostat监控学习,包含/proc/diskstats、/proc/stat简单了解-程序员宅基地

文章浏览阅读3.6k次。背景测试到性能、压力时,经常需要查看磁盘、网络、内存、cpu的性能值这里简单介绍下各个指标的含义一般磁盘比较关注的就是磁盘的iops,读写速度以及%util(看磁盘是否忙碌)CPU一般比较关注,idle 空闲,有时候也查看wait (如果wait特别大往往是io这边已经达到了瓶颈)iostatiostat uses the files below to create ..._/proc/diskstat

glReadPixels读取保存图片全黑_glreadpixels 全黑-程序员宅基地

文章浏览阅读2.4k次。问题:在Android上使用 glReadPixel 读取当前渲染数据,在若干机型(华为P9以及魅族某魅蓝手机)上读取数据失败,glGetError()没有抓到错误,但是获取到的数据有误,如果将获取到的数据保存成为图片,得到的图片为黑色。解决方法:glReadPixels实际上是从缓冲区中读取数据,如果使用了双缓冲区,则默认是从正在显示的缓冲(即前缓冲)中读取,而绘制工作是默认绘制到后缓..._glreadpixels 全黑