摘要:在使用多線程程序時,有時會遇到程序功能異常的情況,而這種異常情況並不是每次都發生,很難模擬出來。這時就需要運用在程序運行時跟蹤線程的手段,而linux系統的LWP和strace命令正是這種技術手段。本文對LWP和strace命令做了簡明扼要的介紹,並通過一個實例來說明如何運用。總而言之,LWP和strace的使用可以提高多線程程序的可維護性。
問題描述:
我們來看一個問題:程序tcp_client同時創建多個線程向同一個服務器發送數據,每個線程發送不同類型的數據,服務器接收數據后,可以通過界面查看到多個數據在刷新。程序運行一段時間后,突然出現只有某個類型的數據不刷新,其他數據刷新正常,但是服務端和客戶端程序都沒有報錯。這時我們該怎么辦?
很明顯,這時程序調試者需要知道不刷新線程的線程ID,然后通過某種手段查看該線程為何出現了異常。通過linux的ps -eLF|grep pid命令可以查看進程的線程ID(LWP號),但是問題在於如果我們沒有在創建線程時記錄其線程ID,我們仍然無法得知哪個LWP號是我們想要追蹤的。
我們來看一下LWP號是什么,以及如何在創建線程時記錄每個線程的LWP號。
pthread_create是基於clone實現的, 創建出來的其實是進程, 但這些進程與父進程共享很多東西,
共享的東西都不用復制給子進程, 從而節省很多開銷, 因此,這些子進程也叫輕量級進程(light-weight process)簡稱LWP. 每個LWP都會與一個內核線程綁定, 這樣它就可以作為獨立的調度實體參與cpu競爭.
LWP被pthread封裝后, 以線程面目示人, 它有自己的id, 這里要區分 phtread_create
給LWP分配的id, 和操作系統給LWP分配的進程id. 這兩者是不同的, 前者用於標志線程,可以暴露給
用戶, 后者其實就是進程的id, 它沒有暴露出來, 必須通過系統調用來得到.
獲取LWP的方法是通過syscall系統調用(具體說明參加man手冊),下面是一個例子程序:
#include <stdio.h> #include <unistd.h> #include <sys/syscall.h> #include <pthread.h> //線程處理函數,輸出LWP號 void *func(void *argv) { //獲取LWP的方法1 int lwp; lwp = syscall(__NR_gettid); printf("lwp[%d]\n", lwp); //獲取LWP的方法2 int lwp2; lwp2 = syscall(SYS_gettid); printf("lwp2[%d]\n", lwp2); sleep(50); } int main(void) { pthread_t thd; pthread_create(&thd, NULL, func, NULL); //創建線程 sleep(60); return 0; }
在獲取LWP號以后,接下來就是通過strace命令對線程進行追蹤了。
strace常用來跟蹤進程執行時的系統調用和所接收的信號。 在Linux世界,進程不能直接訪問硬件設備,當進程需要訪問硬件設備(比如讀取磁盤文件,接收網絡數據等等)時,必須由用戶態模式切換至內核態模式,通 過系統調用訪問硬件設備。strace可以跟蹤到一個進程產生的系統調用,包括參數,返回值,執行消耗的時間。
在理想世界里,每當一個程序不能正常執行一個功能時,它就會給出一個有用的錯誤提示,告訴你在足夠的改正錯誤的線索。但遺憾的是,我們不是生活在理想世界 里,起碼不總是生活在理想世界里。有時候一個程序出現了問題,你無法找到原因。這就是調試程序出現的原因。strace是一個必不可少的 調試工具,strace用來監視系統調用。你不僅可以調試一個新開始的程序,也可以調試一個已經在運行的程序(把strace綁定到一個已有的PID上 面)。
strace的參數有很多,常見的參數介紹如下:
-o filename 將strace的輸出寫入文件filename
-p pid 跟蹤指定的進程pid.
-t 在輸出中的每一行前加上時間信息.
-tt 在輸出中的每一行前加上時間信息,微秒級.
-T 顯示每一調用所耗的時間.
-f 跟蹤由fork調用所產生的子進程.
-ff 如果提供-o filename,則所有進程的跟蹤結果輸出到相應的filename.pid中,pid是各進程的進程 號.
舉個例子:我們對tcp_client程序做一個小小的改動,在通過pthread_create創建線程后,每個線程里都通過syscall函數記錄其LWP號,同時記錄該LWP號對應的數據類型。如果程序再次出現文章開頭時提到的異常狀況,這時我們可以通過“strace -o tcp_client.txt -tt -p 欲追蹤的LWP號”,這樣就很快就能知道發生異常的線程在做哪些系統調用了。
總結:在設計程序的時候,一定要考慮將來的維護問題。對於多線程程序,其調試會更加困難,通過在創建線程中記錄LWP號和對應的線程功能,可以為以后的調試提供很大幫助。
參考文章:
1、關於Linux的進程和線程
http://kenby.iteye.com/blog/1014039
2、linux strace命令:
http://www.cnblogs.com/ggjucheng/archive/2012/01/08/2316692.html
本文出自 “籃彩大神” 博客,請務必保留此出處http://lancaidashen.blog.51cto.com/11086793/1734196