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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/537835.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux下redmine安装插件报错

报错如下: There was an error parsing Gemfile: compile error - syntax error, unexpected :, expecting $end gem tzinfo-data, platforms: [:mingw, :x64_mingw, :mswin, :jruby]^. Bundler cannot continue. 原因是: redmine不同版本对ruby版本有…

ajax post 提交无法进入controller 请求200

最近写js遇到个问题: 用ajax的post方式给后台提交数据,页面200,但是不进入controller 断点,我以为我post参数不对。 网上查的: 1.说路径不对,但是我通过get方式是可以进入的,路径是没问题的&…

cuda 编译 linux,Linux下安装Tensorflow源码及编译

下载Tensorflow源码git clone https://github.com/tensorflow/tensorflow如果无法下载也可以在github上直接下载tensorflow的打包文件,这样也能编译,但是不能使用git命令可根据需要切换到不同的分支安装bazel输入以下命令echo "deb [archamd64] htt…

testflight进行用户的beta测试

发发发转载于:https://www.cnblogs.com/caimaomao/p/9681483.html

linux限制ping的时间,如何限制Linux命令程序运行的时间

Linux提供了大量的命令,每个命令都是唯一的,并且在特定的情况下使用。Linux的目标是帮助您尽可能地高效工作。Linux命令的一个属性是时间限制。您可以为任何您想要的命令设置时间限制。如果时间过期,命令停止执行。在本教程中,您将…

微软网络工具psping介绍

该工具功能主要包括:ICMP Ping、TCP Ping、延迟测试、带宽测试,而且它是微软出品的下载地址:https://download.sysinternals.com/files/PSTools.ziphttp://technet.microsoft.com/en-us/sysinternals/jj729731解压后把psping.exe丢到C:\Windo…

mac svn

https://formac.informer.com/tortoisesvn

linux服务器虚拟内存设置,修改Linux服务器虚拟内存Swap大小

Linux的Swap相当于Windows的虚拟内存,当物理内存不够的时候,就需要将物理内存中的一部分空间释放出来,以供当前运行的程序使用。那些被释放出来的空间可能来自一些很长时间没有什么操作的程序,这些被释放的空间临时保存到Swap空间…

高级软件工程第一次作业--准备

1) 回想一下你对计算机/软件工程专业的畅想 考研之所以选择计算机专业,是因为本科就是这个专业。不去跨专业,是因为觉得换个专业考,比起那些科班出身的人,考上的机率会更小,也有一部分原因是因为比起计算机…

里程碑事件

里程碑事件:里程碑事件往往是一个时间要求为零的任务,即它并非是一个要实实在在完成的任务,而是一个标志性的事件。 例如,在软件开发项目中的“测试”是一个子任务,“撰写测试报告”也是一个子任务,但“完…

linux无法下载ftp,linux 不能下载怎么办

linux 不能下载怎么办?关于Linux下vsftp匿名用户上传和下载的配置配置要注意三部分,请一一仔细对照:1、vsftpd.conf文件的配置(vi /etc/vsftpd/vsftpd.conf)#允许匿名用户登录FTPanonymous_enableYES#设置匿名用户的登…

Mysql8.0Mysql5.7Mysql5.6Mysql5.5特性对比

Mysql5.5 特性,相对于Mysql5.1 性能提升 默认InnoDB plugin引擎。具有提交、回滚和crash恢复功能、ACID兼容。 行级锁(一致性的非锁定读 MVCC)。 表与索引存储在表空间、表大小无限制。 支持dynamic(primary key缓存内存 避免主键查询引起的IO )与compressed(支…

Spring IOC实现原理

2019独角兽企业重金招聘Python工程师标准>>> 1、BeanDefinition 对依赖翻转模式中管理对象依赖关系的数据抽象 实现依赖翻转功能的核心数据结构依赖翻转功能都是围绕对BeanDefinition 处理完成的有了这些BeanDefinition 基础数据结构,容器才能发挥作用2、…

linux3.0 nand分区,OK6410(256MRAM2Gnandflash) Linux3.0.1内核移植

这里我主要移植的是增加yaffs2文件系统支持,修改和移植nand驱动。1.不知道为什么我这yaffs2的补丁打上了就没发现过编译错误,相当顺利啊 呵呵。附上补丁地址-----------------------------------点击打开链接修改补丁里patch‐ker.sh 文件。屏蔽下面这段…

authentication plugin caching_sha2

新电脑安装最新版本的Mysql,用Navicat Premium连接,提示:authentication plugin caching_sha2 因为mysql8.0开始更新了验证方式,Navicat Premium版本连接新MySQL的时候,还是上一个版本的认证方式。 不仅Navicat Prem…

openlayers入门开发系列之图层控制

本篇的重点内容是利用openlayers实现地图图层控制功能,效果图如下: 部分核心代码如下:页面引用ztree插件图层管理器界面布局图层管理器初始化jsLayerSwitcher类定义详细的实现过程见:这里

linux df命令无反馈,Linux-df命令

df命令:说明:df命令用于显示磁盘分区上的可使用的磁盘空间。默认显示单位为KB。可以利用该命令来获取硬盘被占用了多少空间,目前还剩下多少空间等信息参数:-a或--all:包含全部的文件系统;--block-size&…

windows版本的redis

redis官网是不提供windows版本的。 微软的github上有windows版本下载,不会是redis的最新版本,是基于redis稳定版本制作的。 redis官网关于文档版本的说明: Redis uses a standard practice for its versioning: major.minor.patchlevel. A…

一点一点看JDK源码(五)java.util.ArrayList 后篇之removeIf与Predicate

一点一点看JDK源码(五)java.util.ArrayList 后篇之removeIf与Predicate liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) Predicate为jdk1.8新增接口,由…

linux选择运行的核数量,linux – 如何根据可用内核的数量选择最大负载阈值?

负载在Linux上经常被误解.在Linux上,它是运行或不间断睡眠状态中所有任务的度量.请注意,这是任务,而不是进程.线程包含在此值中.内核每五秒计算一次加载,并且是一个加权平均值.这是微小负载是平均5/60,五分钟5/300和十五分之五.一般来说,作为一个纯数字的负载在没有参考点的情况…