作者丨歐陽大哥2013
https://www.jianshu.com/p/813ba526204b
iOS系統通過Core Services層的Foundation框架提供基於OC語言的NSThread和NSOperationQueue類來實現對線程和線程池的管理和使用。同時也提供了一套基於C語言的GCD線程池函數庫來支持多線程的處理應用。這些高級的線程類或者函數的內部實現大部分最終都會調用POSIX標準中的pthread線程庫中的pthread_xxx系列函數(#include <pthread.h>)來完成線程的創建、運行、暫停、恢復、銷毀、結束等操作。用戶態下的線程創建通過系統調用到達內核態的BSD層並創建bsdthread對象,而BSD層則調用Mach層的ksthread對象來完成最終線程的創建和調度的。
線程架構圖
pthread庫中除了提供一系列標準的線程操作API外,還提供了一個用於監控線程創建、運行、結束、銷毀的內省函數(單詞introspection翻譯為內省,但我覺得叫攔截器可能更好一些)。這個函數定義在頭文件#include <pthread/introspection.h>中,函數的籤名為:
pthread_introspection_hook_t pthread_introspection_hook_install(pthread_introspection_hook_t hook)
函數的作用是安裝一個回調函數來掛鈎線程生命周期的四個過程。因此函數的入參是一個函數指針,返回的則是老的掛鈎函數的指針。回調函數是一個格式為pthread_introspection_hook_t類型的函數,其格式定義如下:
typedef void (*pthread_introspection_hook_t)(unsigned int event, pthread_t thread, void *addr, size_t size);
回調函數的每個參數的意義如下:
event:指定線程所處的狀態。
thread: 線程的句柄,每個pthread線程都由一個pthread_t類型句柄來唯一標識。
addr: 為線程分配的棧內存的基地址。
size: 為線程分配的棧內存的尺寸。
上面說的每一個線程有創建、運行、終止、銷毀四個狀態,而event則是用來表示線程的四種狀態的值,它的值是如下枚舉結構的某一個值:
enum {
PTHREAD_INTROSPECTION_THREAD_CREATE = 1,
PTHREAD_INTROSPECTION_THREAD_START,
PTHREAD_INTROSPECTION_THREAD_TERMINATE,
PTHREAD_INTROSPECTION_THREAD_DESTROY,
};
需要注意的是在內省函數中設置回調掛鈎函數後只會監控設置之後的所有線程狀態的變化。因此如果我們要監控整個應用生命周期的所有線程的狀態時,需要儘可能早的進行回調函數的設置,比如可以在某個類的+load方法中,或者在某個全局C++對象的構造函數中設置等等。
回調掛鈎函數中的第二個參數thread是一個類型為pthread_t線程句柄對象,這個對象的結構並沒有對外公開。但是因為pthread庫已經被蘋果開源:https://opensource.apple.com/source/libpthread/
因此我們可以通過線程句柄對象的內部定義來獲取關於線程的更多信息。以方便我們能對線程的各種數據進行更加詳細的記錄。當然這裡我們需要考慮到線程句柄的不同版本下的數據成員的問題。
最後我們實現一個簡單的在main函數內實現線程監控的代碼示例:
#include <pthread/introspection.h>
#include <pthread.h>
#include <stdlib.h>
#include <stdio.h>
pthread_introspection_hook_t g_oldpthread_introspection_hook = NULL;
void mypthread_introspection_hook(unsigned int event, pthread_t thread, void *addr, size_t size)
{
__uint64_t threadid;
pthread_threadid_np(thread, &threadid);
printf("thread_id = %d, addr = %p, size = %d\n", threadid, addr, size);
switch (event)
{
case PTHREAD_INTROSPECTION_THREAD_CREATE:
break;
case PTHREAD_INTROSPECTION_THREAD_START:
break;
case PTHREAD_INTROSPECTION_THREAD_TERMINATE:
break;
case PTHREAD_INTROSPECTION_THREAD_DESTROY:
break;
}
if (g_oldpthread_introspection_hook != NULL)
g_oldpthread_introspection_hook(event, thread, addr, size);
}
int main(int argc, char *argv[])
{
g_oldpthread_introspection_hook = pthread_introspection_hook_install(mypthread_introspection_hook);
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
你可以通過開原始碼中對pthread_t類型結構體的定義來獲取線程的更多信息,但是要注意線程庫的版本信息。
線程監控回調函數中的代碼應該儘可能的精簡和高效,包括官方的頭文件中也有一段說明(實際上是可以被appstore審核通過的):
This should only be used for introspection and debugging tools. Do not rely
on it in shipping code.
推薦↓↓↓
涵蓋:程式設計師大咖、源碼共讀、程式設計師共讀、數據結構與算法、黑客技術和網絡安全、大數據科技、編程前端、Java、Python、Web編程開發、Android、iOS開發、Linux、資料庫研發、幽默程式設計師等。