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.pswp.cn/news/537835.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/537835.shtml
英文地址,請注明出處:http://en.pswp.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和十五分之五.一般來說,作為一個純數字的負載在沒有參考點的情況…