Linux进程同步


 

目录

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

    }

};

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: Linux进程同步实验是一种通过编写程序来模拟多个进程之间的同步和互斥操作的实验。在实验中,可以使用各种同步机制,如信号量、互斥锁、条件变量等,来实现进程之间的同步和互斥操作。通过这种实验,可以深入了解Linux操作系统的进程管理机制,提高对操作系统的理解和掌握。 ### 回答2: Linux进程同步实验是一种测试进程同步的实验,旨在演示在多进程系统中,如何协调和同步进程的操作,以便它们能够正确地共享资源和执行。Linux进程同步实验需要用到Linux操作系统以及代码编程技能。 在Linux进程同步实验中,通常利用线程、锁和信号量等机制来实现进程同步。这些机制可以帮助程序员解决多进程中可能产生的竞争条件、死锁、饥饿和优先级反转等问题。 在同步实验中,通常会引入一个共享的资源,如一个共享内存区域。多个进程都可以访问这个资源,但同时只能有一个进程对其进行修改。因此,必须通过特定的同步机制协调访问这个资源的进程。 例如,在多进程中,如果进程A想要访问共享资源,它必须先获得锁或信号量。只有在其他进程都释放了锁或信号量之后,A才能继续执行代码。当A完成对共享资源的操作后,它必须释放锁或信号量,以便其他进程能够继续访问共享资源。 总之,Linux进程同步实验可以帮助程序员了解多进程操作中的同步机制。这种实验对于任何想要在Linux系统中开发多进程应用程序的人来说都是必备的。 ### 回答3: Linux是一个多道程序系统,它允许多个进程同时执行。当多个进程同时访问和修改共享资源时,就必须进行进程同步,以避免数据的不一致性和竞争条件的发生。进程同步是指多个进程按照一定的次序访问和修改共享资源,以保证并发程序的正确性和一致性。 Linux进程同步实验可以模拟多个进程对同一共享资源的访问和修改,通过学习和实践,了解进程同步的概念、原理和技术,并完成各种同步方法的实现和分析。 在Linux系统中,可以通过信号量、互斥量、条件变量、自旋锁等多种方式实现进程同步。其中,信号量是最基本的同步机制,它可以控制多个进程对共享资源的并发访问和修改,通过p、v操作对信号量进行加锁和解锁,从而保证并发程序的正确性和一致性。 在Linux进程同步实验中,可以编写多个进程程序,模拟进程对同一共享资源的并发访问和修改。在代码中加入信号量等同步机制,观察和比较不同同步方法的效率和性能。 例如,可以编写一个生产者-消费者模型的进程程序,共享资源为缓冲区。生产者进程不断向缓冲区中添加数据,消费者进程不断从缓冲区中取出数据。在此过程中,需要保证缓冲区的数据完整性和一致性,避免多个进程同时修改同一个缓冲区引起数据竞争的情况。 通过Linux进程同步实验,可以掌握Linux系统多进程编程和进程同步的知识,提高并发编程的能力和技能。同时,也可以深入理解操作系统的运行机制和原理,为后续的学习和研究打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值