您好,登錄后才能下訂單哦!
本篇文章為大家展示了基于linuxthreads-2.0.1如何分析線程的棧,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
線程本質上是進程中的一個執行流,我們知道,進程有代碼段,線程其實就是進程代碼段中的其中一段代碼。線程的一種實現是作為進程來實現的。通過調用clone,新建一個進程,然后執行父進程代碼段里的一個代碼片段。文件、內存等信息都是共享的。因為內存是共享的,所以線程不能共享棧,否則訪問棧的地址的時候,會映射到相同的物理地址,那樣就會互相影響,所以每個線程會有自己獨立的棧。在調用clone函數的時候會設置棧的范圍。下面通過linuxthreads的代碼看看線程的棧。linuxthreads里有一個__pthread_initialize函數,該函數會在main函數執行前執行。在該函數中會設置主線程的棧范圍。
// CURRENT_STACK_FRAME 即sp寄存器。按STACK_SIZE大小對齊
__pthread_initial_thread_bos = (char *)(((long)CURRENT_STACK_FRAME - 2 * STACK_SIZE) & ~(STACK_SIZE - 1));
__pthread_initial_thread_bos 保存主線程的棧頂位置。
然后當我們第一次調用pthread_create創建線程的時候,會調用pthread_initialize_manager函數初始化manager線程。manager線程是管理其他的線程的線程。
// 在堆上分配一塊內存用于manager線程的棧,棧頂
__pthread_manager_thread_bos = malloc(THREAD_MANAGER_STACK_SIZE);
// 高地址是棧底
__pthread_manager_thread_tos = __pthread_manager_thread_bos + THREAD_MANAGER_STACK_SIZE;
然后調用clone函數設置manager線程的棧。
__clone(__pthread_manager,__pthread_manager_thread_tos, CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, (void *)(long)manager_pipe[0]);
最后在函數pthread_handle_create中設置創建的線程的棧,pthread_handle_create函數是調用pthread_create函數的時候被調用的函數。
// THREAD_STACK_START_ADDRESS 即__pthread_initial_thread_bos,即主線程的棧頂
#ifndef THREAD_STACK_START_ADDRESS
#define THREAD_STACK_START_ADDRESS __pthread_initial_thread_bos
#endif
#define THREAD_SEG(seg) ((pthread_t)(THREAD_STACK_START_ADDRESS - (seg) * STACK_SIZE) - 1)
#define SEG_THREAD(thr) (((size_t)THREAD_STACK_START_ADDRESS - (size_t)(thr+1)) / STACK_SIZE)
pthread_t new_thread = THREAD_SEG(sseg);
mmap((caddr_t)((char *)(new_thread+1) - INITIAL_STACK_SIZE),INITIAL_STACK_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC,MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED | MAP_GROWSDOWN, -1, 0);
從上面代碼可知,新建的線程的棧在主線程的棧頂下面(即地址小于主線程的棧頂),創建線程的時候,首先計算新線程的棧地址,然后調用mmap劃出這塊地址。否則訪問的時候會segmentfault。最后調用clone創建進程(線程)并設置棧的棧頂和棧底位置。
__clone(pthread_start_thread, new_thread,(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND| PTHREAD_SIG_RESTART),new_thread);
內存布局如下。
從上面的棧分布我們還可以知道一個信息,即當前執行的代碼屬于哪個線程。
static inline pthread_t thread_self (void)
{
#ifdef THREAD_SELF
THREAD_SELF
#else
char *sp = CURRENT_STACK_FRAME;
// 大于初始化棧則是主線程
if (sp >= __pthread_initial_thread_bos)
return &__pthread_initial_thread;
// 這是manager線程自己申請的空間
else if (sp >= __pthread_manager_thread_bos
&& sp < __pthread_manager_thread_tos)
return &__pthread_manager_thread;
else
// sp肯定落在某個線程的棧范圍內,STACK_SIZE-1使得低n位全1, 或sp再加1即往高地址,按STACK_SIZE對齊,減去一個pthread_t得到tcb
return (pthread_t) (((unsigned long int) sp | (STACK_SIZE - 1)) + 1) - 1;
#endif
}
這就是linuxthreads獲取當前線程的是方法。
上述內容就是基于linuxthreads-2.0.1如何分析線程的棧,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。