linux跟蹤線程的方法:LWP和strace命令

摘要:在使用多線程程序時,有時會遇到程序功能異常的情況,而這種異常情況並不是每次都發生,很難模擬出來。這時就需要運用在程序運行時跟蹤線程的手段,而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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章