Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • 常见程序性能开销cost、latency延迟耗时的经验值
  • 面试常见问题
  • 静态链接-动态链接-elf详解-elfloader
  • 动态库和静态库的依赖问题
  • glibc和ld的编译调试-为某程序单独设置ld
  • dl_iterate_phdr遍历linkmap头、获取so加载地址
  • shell、bash语法和脚本模板
  • so文件查找路径
  • 逻辑地址-线性地址or虚拟地址-物理地址
  • 通过ELF读取当前进程的符号表并获取符号的版本信息
  • 虚拟内存,cache,TLB,页表
  • 用户内存空间分布和mmap
  • numa网卡绑定
  • 隔核绑核、服务器优化
  • popen底层原理和仿照实现-execl
  • tmux用法
  • ASLR机制
  • 程序后台运行、恢复前台nohup
  • 大页内存huge_page
  • 用perf查看page-fault
  • Bash设置显示全部路径
  • 查看socket fd状态,设置nonblock
  • cout输出到屏幕的过程
  • 多进程写同一文件-write原子性-log日志
  • vim用法
  • epoll用法
  • signal信号、软中断、硬中断、alarm
  • 内核模块
  • 读写锁之pthread_rwlock和内核rwlock自旋读写锁
    • 1. 读写锁特性
    • 2. 写独占,读共享;写锁优先级高 解析
    • 3. 适用场景
    • 4. 读写锁API
      • 4.1 Linux线程相关API
      • 4.2 系统相关API
    • 5. 代码例子
    • 参考:
    • 存在读锁时加写锁测试
  • systemtap
  • xargs、awk用法
  • openssl libssl.so.10.so缺失问题
  • netstat用法
  • fork函数
  • tcp延迟确认ack
  • 90.centos7上一次std-string编译错误定位
  • docker用法
  • find用法
  • dmesg
  • gcc编译用法
  • avx-sse切换惩罚
  • Centos7防火墙
  • chmod用法
  • kernel-devel安装版本
  • Linux-Centos7系统安装、网络设置、常见报错
  • linux下g++编译c++文件
  • MegaCli 安装及使用
  • mysql
  • mysql忘记密码修改方法
  • set用法
  • crontab
  • ssh传文件scp
  • ssh连接
  • tcpdump、tshark、tcpreplay用法
  • ubantu root登录以及创建新用户
  • ubuntu安装g++和gdb
  • uClibc编译失败解决方法
  • win10安装WSL open-ssh
  • yum升级git
  • 比较so文件版本-md5sum
  • 查看磁盘信息
  • 合并两个硬盘,挂载到一个文件夹下
  • 软件安装目录usr-local-src
  • 下载centos历史版本
  • sh脚本转可执行文件、加密
  • Linux
gaoliu
2022-03-30
目录

读写锁之pthread_rwlock和内核rwlock自旋读写锁

# Linux 锁机制(2)之读写锁 rwlock_t

https://blog.csdn.net/lqy971966/article/details/103541567 (opens new window)

# 1. 读写锁特性

读写锁的特性为:写独占,读共享;写锁优先级高。
对于读写锁,掌握了这12个字就足矣了。

# 2. 写独占,读共享;写锁优先级高 解析

  1. 写独占
  2. 读共享
  3. 读写锁是“读模式加锁”时, 既有试图以写模式加锁的线程,也有试图以读模式加锁的线程。那么读写锁会阻塞随后的读模式锁请求,优先满足写模式锁。–> 写锁优先级高 ---实测不是

实测: 只有读写锁处于不加锁状态时,才能进行写模式下的加锁

# 3. 适用场景

读写锁非常适合于对数据结构读多写少的情况。

# 4. 读写锁API

# 4.1 Linux线程相关API

pthread_rwlock_init函数
pthread_rwlock_destroy函数
pthread_rwlock_rdlock函数
pthread_rwlock_wrlock函数
pthread_rwlock_tryrdlock函数
pthread_rwlock_trywrlock函数
pthread_rwlock_unlock函数
1
2
3
4
5
6
7

# 4.2 系统相关API

方法		描述
rwlock_init()	初始化指定的rwlock_t
read_lock()		获取指定的读锁
read_unlock()	释放指定的读锁
write_lock()	获得指定的写锁
write_unlock()	释放指定的写锁
write_trylock()	试图获得指定的写锁;如果写锁不可用,返回非0值

read_lock_irq()		禁止本地中断并获得指定读锁
read_unlock_irq()	释放指定的读锁并激活本地中断
read_lock_irqsave()	存储本地中断的当前状态,禁止本地中断并获得指定读锁
read_unlock_irqrestore()	释放指定的读锁并将本地中断恢复到指定前的状态

write_lock_irq()	禁止本地中断并获得指定写锁
write_unlock_irq()	释放指定的写锁并激活本地中断
write_lock_irqsave()存储本地中断的当前状态,禁止本地中断并获得指定写锁
write_unlock_irqrestore()	释放指定的写锁并将本地中断恢复到指定前的状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 5. 代码例子

/* 2个线程不定时 "写" 全局资源,3个线程不定时 "读" 同一全局资源 */

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define COUNT_OF_READ 3
#define COUNT_OF_WRITE 2

int g_iCounter;                          //全局资源
pthread_rwlock_t g_iRwLock;

void *th_write(void *arg)
{
	int iTmpCount = 0;
	int iTmpArg = (int)(int*)arg;

	while (1) {
		iTmpCount = g_iCounter;
		usleep(1000);

		pthread_rwlock_wrlock(&g_iRwLock);
		printf("=====write %d: %lu: g_iCounter=%d ++g_iCounter=%d\n", iTmpArg, pthread_self(), iTmpCount, ++g_iCounter);
		/* pthread_self 返回线程ID */
		pthread_rwlock_unlock(&g_iRwLock);

		usleep(5000);
	}
	return NULL;
}

void *th_read(void *arg)
{
	int iTmpArg = (int)(int*)arg;

	while (1) {
		pthread_rwlock_rdlock(&g_iRwLock);
		printf("-----read %d: %lu: %d\n", iTmpArg, pthread_self(), g_iCounter);
		pthread_rwlock_unlock(&g_iRwLock);

		usleep(900);
	}
	return NULL;
}

int main(void)
{
	int iCount = 0;
	pthread_t tid[COUNT_OF_WRITE + COUNT_OF_READ];

	pthread_rwlock_init(&g_iRwLock, NULL);

	for (iCount = 0; iCount < COUNT_OF_WRITE; iCount++)
		pthread_create(&tid[iCount], NULL, th_write, (void *)iCount);

	for (iCount = 0; iCount < COUNT_OF_READ; iCount++)
		pthread_create(&tid[iCount+3], NULL, th_read, (void *)iCount);

	for (iCount = 0; iCount < COUNT_OF_WRITE + COUNT_OF_READ; iCount++)
		pthread_join(tid[iCount], NULL);

	pthread_rwlock_destroy(&g_iRwLock);            //释放读写琐

	return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65

结果:

root@localhost home]# gcc rwlock.c -lpthread                                        ^
[root@localhost home]# ./a.out > rrr
^C
[root@localhost home]# head -n 30 rrr 
-----read 0: 140041912997632: 0
-----read 1: 140041904604928: 0
-----read 2: 140041896212224: 0
=====write 0: 140041929783040: g_iCounter=0 ++g_iCounter=1
-----read 2: 140041896212224: 1
-----read 1: 140041904604928: 1
=====write 1: 140041921390336: g_iCounter=0 ++g_iCounter=2
-----read 0: 140041912997632: 2
-----read 1: 140041904604928: 2
-----read 2: 140041896212224: 2
-----read 0: 140041912997632: 2
-----read 2: 140041896212224: 2
-----read 1: 140041904604928: 2
-----read 0: 140041912997632: 2
-----read 1: 140041904604928: 2
-----read 2: 140041896212224: 2
-----read 0: 140041912997632: 2
-----read 2: 140041896212224: 2
-----read 1: 140041904604928: 2
-----read 0: 140041912997632: 2
=====write 0: 140041929783040: g_iCounter=2 ++g_iCounter=3
=====write 1: 140041921390336: g_iCounter=2 ++g_iCounter=4
-----read 2: 140041896212224: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
-----read 2: 140041896212224: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
-----read 1: 140041904604928: 4
-----read 0: 140041912997632: 4
[root@localhost home]#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

代码优化了一下 参考:
https://blog.csdn.net/yychuyu/article/details/84677883

# 参考:

[1. https://cloud.tencent.com/developer/news/308874][1. https_cloud.tencent.com_developer_news_308874]

# 存在读锁时加写锁测试

如果多个线程循环读,即便每个线程会间歇释放读锁,但是若总体一直不释放读锁,则写锁pthread_rwlock_wrlock()将无法获取成功。

可以考虑synchronize_rcu这种优化过的读写锁。

修改代码如下,刚开始写线程可以写,后面读线程轮流占有锁,写线程将再也无法获取到锁。

rwlock_test.cpp

/* 2个线程不定时 "写" 全局资源,5个线程不定时 "读" 同一全局资源 */

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>

#define COUNT_OF_READ 5
#define COUNT_OF_WRITE 2

int g_iCounter;                          //全局资源
pthread_rwlock_t g_iRwLock;

void *th_write(void *arg)
{
	int iTmpCount = 0;
	size_t iTmpArg = (size_t)(int*)arg;

	while (1) {
		iTmpCount = g_iCounter;
		usleep(200000);

		pthread_rwlock_wrlock(&g_iRwLock);
		printf("=====write %d: %lu: g_iCounter=%d ++g_iCounter=%d\n", iTmpArg, pthread_self(), iTmpCount, ++g_iCounter);
		/* pthread_self 返回线程ID */
		usleep(100000);
		pthread_rwlock_unlock(&g_iRwLock);
	}
	return NULL;
}

void *th_read(void *arg)
{
	size_t iTmpArg = (size_t)(int*)arg;

	while (1) {
		usleep(900);
		pthread_rwlock_rdlock(&g_iRwLock);
		printf("-----read %lu: %lu: %d\n", iTmpArg, pthread_self(), g_iCounter);
		usleep(1000000);
		pthread_rwlock_unlock(&g_iRwLock);
	}
	return NULL;
}

int main(void)
{
	int iCount = 0;
	pthread_t tid[COUNT_OF_WRITE + COUNT_OF_READ];

	pthread_rwlock_init(&g_iRwLock, NULL);

	for (iCount = 0; iCount < COUNT_OF_WRITE; iCount++) {
		usleep(300000);
		pthread_create(&tid[iCount], NULL, th_write, reinterpret_cast<void *>(iCount));
    }

	for (iCount = 0; iCount < COUNT_OF_READ; iCount++) {
		usleep(300000);
		pthread_create(&tid[iCount+3], NULL, th_read, reinterpret_cast<void *>(iCount));
    }

	for (iCount = 0; iCount < COUNT_OF_WRITE + COUNT_OF_READ; iCount++)
		pthread_join(tid[iCount], NULL);

	pthread_rwlock_destroy(&g_iRwLock);            //释放读写琐

	return 0;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69

编译:

g++ -o a.out -pthread  rwlock_test.cpp
1

输出:

$ ./a.out
=====write 0: 140659470046976: g_iCounter=0 ++g_iCounter=1
=====write 1: 140659461654272: g_iCounter=1 ++g_iCounter=2
=====write 0: 140659470046976: g_iCounter=1 ++g_iCounter=3
-----read 0: 140659453261568: 3
-----read 1: 140659444868864: 3
-----read 2: 140659436476160: 3
-----read 3: 140659428083456: 3
-----read 0: 140659453261568: 3
-----read 4: 140659419690752: 3
-----read 1: 140659444868864: 3
-----read 2: 140659436476160: 3
-----read 3: 140659428083456: 3
-----read 0: 140659453261568: 3
-----read 4: 140659419690752: 3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 内核中的自旋读写锁

这个内核同步专栏系列文章貌似不错: https://blog.csdn.net/zhoutaopower/category_11121586.html (opens new window)

  • 2.内核自旋锁 https://stephenzhou.blog.csdn.net/article/details/86598839 (opens new window)
  • 3.内核自旋读写锁 https://stephenzhou.blog.csdn.net/article/details/86605987 (opens new window)
  • 4.顺序锁 https://stephenzhou.blog.csdn.net/article/details/86611798 (opens new window)
  • 5.信号量 https://stephenzhou.blog.csdn.net/article/details/86614945 (opens new window)
  • 6.互斥体 https://stephenzhou.blog.csdn.net/article/details/86627438 (opens new window)
  • 7.RCU机制 https://stephenzhou.blog.csdn.net/article/details/86646688 (opens new window)
编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
内核模块
systemtap

← 内核模块 systemtap→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×