linux 读写锁 性能,linux 下实现高性能读写锁(read/write lock)

前一篇文章分析了Windows slim read/write lock的工作原理。我们知道它的设计相当精妙,于是我们可以借鉴它的思路来设计linux下的读写锁。

在这个读写锁的设计上,需要注意的是linux和windows有以下几点区别:

(1)windows使用的keyedevent机制需要使用linux下的机制代替。这里我们选用futex机制来模拟。linux下的futex机制对外表现为下面这个接口:

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

但是由于没有公开提供该接口,所有我们需要使用syscall来调用它,如下:

#define futex(addr1, op, val, rel, addr2, val3) \

syscall(SYS_futex, addr1, op, val, rel, addr2, val3)

#define futex_wait_always(addr1) \

syscall(SYS_futex, addr1, FUTEX_WAIT, *(int*)(addr1), 0, 0, 0)

#define futex_wake_single(addr1) \

syscall(SYS_futex, addr1, FUTEX_WAKE, 1, 0, 0, 0)

在这里我们主要使用单个线程死等和唤醒单个线程两项操作。关于futex的其他知识可以参考搜索引擎,不再赘述。

(2)futex的wake机制和KeyedEvent有所区别。NtReleaseKeyedEvent唤醒等待线程时,如果此时尚不存在等待者,NtReleaseKeyedEvent会阻塞,直到有等待者出现。但是,通过在linux环境 “Linux version 3.2.0-23-generic  (gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu4) ) #36-Ubuntu SMP Tue Apr 10 20:39:51 UTC 2012 ”下 的测试,FUTEX_WAKE在没有等待者的情况下任然会直接返回,不会等待,于是采用的下面的代码模拟NtReleaseKeyedEvent。要注意到,FUTEX_WAKE的返回值是实际唤醒线程的个数,

while(1 != (futex_wake_single(tmp2)))

{

unsigned int n = 0;

RtlBackoff(&n);

}

(3)linux下的_mm_pause并不是真的调用了cpu的pause指令,而是用nop指令代替,这和VC的编译结果是不一样的,因此需要实现一个自己的__mm_pause:

__inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))

__mm_pause (void)

{

__asm__ __volatile__ ("pause" : : :"memory");

}

(4)关于函数局部变量要对齐到16字节的问题,在现在新版本的gcc编译环境下可以不考虑,gcc默认是对齐到16字节的。当然也可以指定函数属性,如下:

void __attribute__((force_align_arg_pointer)) RtlAcquireSRWLockExclusive(SRWLOCK* pSRWLock);

void __attribute__((force_align_arg_pointer)) RtlAcquireSRWLockShared(SRWLOCK* pSRWLock);

(5)VC提供的interlockedXXX系列函数相当丰富,需要使用gcc提供的__syncXXXX系列的十来个函数来代替,相当蛋疼。

由于原生态的读写锁CRWLock不支持同一线程的递归锁操作,所有增加了一个CRWLockRecur类,其中记录了线程id用于实现递归锁功能。CRWLock类的代码经过了30个线程1.5亿次随机加解锁操作测试,能够稳定工作。好了下面贴出头文件部分代码,完整代码比较长,就不贴了,可以到http://download.csdn.net/detail/yichigo/7603735下载参考,完全免费。欢迎指正!

#ifndef __RW_LOCK_H__

#define __RW_LOCK_H__

#if defined(_WIN32) || defined(WIN32) || defined(_WIN64)

#if !defined(RWL_WINDOWS)

#define RWL_WINDOWS

#endif // WIN32 or _WIN32

#elif defined(__linux__) || defined(__linux)

#if !defined(RWL_LINUX)

#define RWL_LINUX

#endif

#endif // not RWL_WINDOWS

#ifdef RWL_WINDOWS

#include

#else

#include

#include

#include

#include

#include

#include

#endif

#ifdef RWL_WINDOWS

#if !defined(ASSERT)

#define ASSERT(f) ((f) || (__debugbreak(),0))

#endif

#else /// linux

#define ASSERT assert

//

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

#define futex(addr1, op, val, rel, addr2, val3) \

syscall(SYS_futex, addr1, op, val, rel, addr2, val3)

#define futex_wait_always(addr1) \

syscall(SYS_futex, addr1, FUTEX_WAIT, *(int*)(addr1), 0, 0, 0)

#define futex_wake_single(addr1) \

syscall(SYS_futex, addr1, FUTEX_WAKE, 1, 0, 0, 0)

//

// linux下的_mm_pause不是真正的pause

// 自己实现一个

__inline void __attribute__((__gnu_inline__, __always_inline__, __artificial__))

__mm_pause (void)

{

__asm__ __volatile__ ("pause" : : :"memory");

}

#define SRWLockSpinCount 1024

#define Busy_Lock1// 已经有人获取了锁

#define Wait_Lock2// 有人等待锁

#define Release_Lock4// 说明已经有人释放一次锁

#define Mixed_Lock8// 共享锁、独占锁并存

#define EXTRACT_ADDR(s)((s) & (~0xf)) // 去掉低4位

#endif

#ifdef RWL_WINDOWS

class CRWLock

{

public:

CRWLock();

~CRWLock();

void ExclusiveLock();

void SharedLock();

void ReleaseExclusiveLock();

void ReleaseSharedLock();

private:

SRWLOCK m_SRWLock;

};

#else /// linux

class CRWLock

{

struct SRWLOCK {

size_t Ptr;

};

struct _SyncItem

{

int ifutex;

_SyncItem* back;

_SyncItem* notify;

_SyncItem* next;

size_t shareCount;

size_t flag;

};

public:

CRWLock();

~CRWLock();

void ExclusiveLock();

void SharedLock();

void ReleaseExclusiveLock();

void ReleaseSharedLock();

private:

void RtlInitializeSRWLock(SRWLOCK* pSRWLock);

void __attribute__((force_align_arg_pointer)) RtlAcquireSRWLockExclusive(SRWLOCK* pSRWLock);

void __attribute__((force_align_arg_pointer)) RtlAcquireSRWLockShared(SRWLOCK* pSRWLock);

void RtlReleaseSRWLockExclusive(SRWLOCK* pSRWLock);

void RtlReleaseSRWLockShared(SRWLOCK *pSRWLock);

void RtlpWakeSRWLock(SRWLOCK* pSRWLock, size_t st);

void RtlBackoff(unsigned int *pCount);

void RtlpOptimizeSRWLockList(SRWLOCK* pSRWLock, size_t st);

private:

SRWLOCK m_SRWLock;

};

#endif

//

//

// 增加一个获取独占锁线程id记录

// 实现独占锁递归支持

//

class CRWLockRecur : public CRWLock

{

public:

CRWLockRecur()

{

m_tid = -1;

m_nRecursion = 0;

}

~CRWLockRecur()

{

}

void OwnLock()

{

if (m_tid != GetTid())

{

ExclusiveLock();

m_tid = GetTid();

}

m_nRecursion++;

}

void ShareLock()

{

SharedLock();

}

void UnOwnLock()

{

m_nRecursion--;

if (0 == m_nRecursion)

{

m_tid = 0;

ReleaseExclusiveLock();

}

}

void UnShareLock()

{

ReleaseSharedLock();

}

private:

size_t GetTid()

{

#ifdef RWL_WINDOWS

return GetCurrentThreadId();

#else

return pthread_self();

#endif

}

private:

size_t m_tid;

unsigned int m_nRecursion;

};

#endif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
是用于多线程环境下保护共享资源的一种机制。在 Linux 内核中,实现是基于 spinlock 和原子操作的。下面我将简单介绍一下 Linux 内核中的源码实现的定义: ```c typedef struct { raw_rwlock_t raw_lock; } rwlock_t; ``` 其中,raw_rwlock_t 是一个原始类型,它是由内核提供的一个结构体类型,定义在 include/linux/spinlock_types.h 文件中。 raw_rwlock_t 的定义: ```c typedef struct { arch_rwlock_t raw_lock; } raw_rwlock_t; typedef struct { unsigned int lock; } arch_rwlock_t; ``` 其中,arch_rwlock_t 是一个体系结构相关的原始类型,它的实现由不同的处理器架构提供。 下面是 x86_64 架构下的 arch_rwlock_t 实现: ```c struct __arch_rwlock { unsigned int lock; }; typedef struct __arch_rwlock arch_rwlock_t; ``` 可以看到,在 x86_64 架构下,arch_rwlock_t 只包含一个 unsigned int 类型的 lock 成员变量,用于存储状态。 的初始化: ```c void rwlock_init(rwlock_t *lock) { raw_spin_lock_init(&lock->raw_lock.spinlock); atomic_long_set(&lock->raw_lock.rw_sem, 0); } ``` 其中,raw_spin_lock_init() 用于初始化,atomic_long_set() 用于初始化计数器。 的获取: ```c void read_lock(rwlock_t *lock) { while (1) { long count = atomic_long_read(&lock->raw_lock.rw_sem); if (count >= 0) { if (atomic_long_cmpxchg(&lock->raw_lock.rw_sem, count, count + 1) == count) { break; } } else { cpu_relax(); } } raw_spin_lock(&lock->raw_lock.spinlock); } ``` 其中,atomic_long_read() 用于计数器的值,如果值大于等于 0,则表示可用,此时使用 atomic_long_cmpxchg() 原子操作来增加计数器并获取;如果值小于 0,则表示有在使用,此时使用 cpu_relax() 函数等待释放。 的释放: ```c void read_unlock(rwlock_t *lock) { raw_spin_unlock(&lock->raw_lock.spinlock); atomic_long_dec(&lock->raw_lock.rw_sem); } ``` 其中,raw_spin_unlock() 用于释放,atomic_long_dec() 用于减少计数器的值。 的获取: ```c void write_lock(rwlock_t *lock) { raw_spin_lock(&lock->raw_lock.spinlock); while (1) { long count = atomic_long_read(&lock->raw_lock.rw_sem); if (count == 0) { if (atomic_long_cmpxchg(&lock->raw_lock.rw_sem, 0, -1) == 0) { break; } } else { cpu_relax(); } } } ``` 其中,raw_spin_lock() 用于获取,atomic_long_read() 用于计数器的值,如果值等于 0,则表示未被使用,此时使用 atomic_long_cmpxchg() 原子操作将计数器的值修改为 -1 并获取;如果值大于 0,则表示有在使用,此时使用 cpu_relax() 函数等待释放。 的释放: ```c void write_unlock(rwlock_t *lock) { atomic_long_set(&lock->raw_lock.rw_sem, 0); raw_spin_unlock(&lock->raw_lock.spinlock); } ``` 其中,atomic_long_set() 用于将计数器的值设为 0,raw_spin_unlock() 用于释放。 以上就是 Linux 内核中的源码实现。值得注意的是,在多处理器环境下,实现可能会涉及到更复杂的机制,例如者优先等待、者优先等待等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值