您的位置:首页 > 运维架构 > Linux

Linux杂谈:进程锁核+实时线程导致的读写锁死循环

发现问题   

    公司项目测试的时候,发现运行一段时间后会出现cpu百分之百的情况。

    想着可能是哪里出现了死循环,于是打算用gdb跟一下,结果gdb居然无法attach到进程。。。。。。

 

定位问题

    查了查去,原来有一个优先级为RT的实时线程出现了死循环,并且由于配置了CPU的亲和属性,使得进程只运行在第一个核上,此时gdb就无法attach了

    使用taskset现场修改进程的cpu亲和属性后,发现这个占cpu百分百的实时线程并没有出现一般的死循环,而是每次都在pthread_rwlock_wrlock这个函数中,

    而更诡异的是,只要修改了cpu亲和属性,就没有“死循环了”。。。。。。

 

实验

    于是写了一段实验代码

1 #define _GNU_SOURCE
2 #include "stdio.h"
3 #include "stdlib.h"
4 #include "unistd.h"
5 #include "pthread.h"
6 #include <sched.h>
7
8 pthread_rwlock_t rwlock;
9
10 void* task1(void *arg)
11 {
12     pthread_setname_np(pthread_self(), "task1");
13
14     while(1)
15     {
16         printf("\r\n task1 lock \r\n");
17         pthread_rwlock_wrlock(&rwlock);
18
19         printf("\r\n task1 unlock \r\n");
20         pthread_rwlock_unlock(&rwlock);
21
22         usleep(10);
23     }
24 }
25
26 void* task2(void *arg)
27 {
28     struct sched_param sparam;
29
30     pthread_setname_np(pthread_self(), "task2");
31
32     /* 设置为最高优先级的实时任务 */
33     sparam.sched_priority = sched_get_priority_max(SCHED_RR);
34     pthread_setschedparam(pthread_self(), SCHED_RR, &sparam);
35
36     while(1)
37     {
38         printf("\r\n task2 lock \r\n");
39         pthread_rwlock_wrlock(&rwlock);
40
41         printf("\r\n task2 unlock \r\n");
42         pthread_rwlock_unlock(&rwlock);
43
44         usleep(10);
45     }
46 }
47
48 int main(int argc, char *argv[])
49 {
50     pthread_t t1, t2, t3;
51     cpu_set_t cpuset;
52
53     /* 设置cpu亲和属性,将进程绑定在第一个核上 */
54     CPU_ZERO(&cpuset);
55     CPU_SET(0, &cpuset);
56     sched_setaffinity(0, sizeof(cpuset), &cpuset);
57
58     pthread_rwlock_init(&rwlock, NULL);
59
60     pthread_create(&t2, NULL, task1, NULL);
61     sleep(3);
62     pthread_create(&t3, NULL, task2, NULL);
63
64     while (1)
65         sleep(10);
66
67     return 0;
68 }

 

运行结果,如下图

 

真的出现了CPU百分百的情况!!!

 

分析原因

1. 读写锁的“拿锁”和“放锁”操作并不是一个完整的原子操作,而是有可能操作到一半被调度出去;

2. 此次实验结果显示,task1(非实时)在做unlock操作时,已经修改了一部分读写锁的属性,此时task2(实时)lock时,发现不需要再阻塞了,只需要自旋等待(死循环)task1将unlock操作做完;然而由于task2是实时任务,整个进程又只绑定到了第一个核上,task1无法得到调度,造成了task2的死循环。

 

标签: