發現問題
公司專案測試的時候,發現執行一段時間後會出現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(100); 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(100); 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的死迴圈。