關於linux線程
在許多經典的操作系統教科書中, 總是把進程定義爲程序的執行實例, 它並不執行什麼, 只是維護應用程序所需的各種資源. 而線程則是真正的執行實體.
爲了讓進程完成一定的工作, 進程必須至少包含一個線程. 如圖1.
進程所維護的是程序所包含的資源(靜態資源), 如: 地址空間, 打開的文件句柄集, 文件系統狀態, 信號處理handler, 等;
線程所維護的運行相關的資源(動態資源), 如: 運行棧, 調度相關的控制信息, 待處理的信號集, 等;
然而, 一直以來, linux內核並沒有線程的概念. 每一個執行實體都是一個task_struct結構, 通常稱之爲進程. 如圖2.
進程是一個執行單元, 維護着執行相關的動態資源. 同時, 它又引用着程序所需的靜態資源.
通過系統調用clone創建子進程時, 可以有選擇性地讓子進程共享父進程所引用的資源. 這樣的子進程通常稱爲輕量級進程.
linux上的線程就是基於輕量級進程, 由用戶態的pthread庫實現的.
使用pthread以後, 在用戶看來, 每一個task_struct就對應一個線程, 而一組線程以及它們所共同引用的一組資源就是一個進程.
但是, 一組線程並不僅僅是引用同一組資源就夠了, 它們還必須被視爲一個整體.
對此, POSIX標準提出瞭如下要求:
1, 查看進程列表的時候, 相關的一組task_struct應當被展現爲列表中的一個節點;
2, 發送給這個"進程"的信號(對應kill系統調用), 將被對應的這一組task_struct所共享, 並且被其中的任意一個"線程"處理;
3, 發送給某個"線程"的信號(對應pthread_kill), 將只被對應的一個task_struct接收, 並且由它自己來處理;
4, 當"進程"被停止或繼續時(對應SIGSTOP/SIGCONT信號), 對應的這一組task_struct狀態將改變;
5, 當"進程"收到一個致命信號(比如由於段錯誤收到SIGSEGV信號), 對應的這一組task_struct將全部退出;
6, 等等(以上可能不夠全);
linuxthreads
在linux 2.6以前, pthread線程庫對應的實現是一個名叫linuxthreads的lib.
linuxthreads利用前面提到的輕量級進程來實現線程, 但是對於POSIX提出的那些要求, linuxthreads除了第5點以外, 都沒有實現(實際上是無能爲力):
1, 如果運行了A程序, A程序創建了10個線程, 那麼在shell下執行ps命令時將看到11個A進程, 而不是1個(注意, 也不是10個, 下面會解釋);
2, 不管是kill還是pthread_kill, 信號只能被一個對應的線程所接收;
3, SIGSTOP/SIGCONT信號只對一個線程起作用;
還好linuxthreads實現了第5點, 我認爲這一點是最重要的. 如果某個線程"掛"了, 整個進程還在若無其事地運行着, 可能會出現很多的不一致狀態. 進程將不是一個整體, 而線程也不能稱爲線程.
或許這也是爲什麼linuxthreads雖然與POSIX的要求差距甚遠, 卻能夠存在, 並且還被使用了好幾年的原因吧~
但是, linuxthreads爲了實現這個"第5點", 還是付出了很多代價, 並且創造了linuxthreads本身的一大性能瓶頸.
接下來要說說, 爲什麼A程序創建了10個線程, 但是ps時卻會出現11個A進程了. 因爲linuxthreads自動創建了一個管理線程. 上面提到的"第5點"就是靠管理線程來實現的.
當程序開始運行時, 並沒有管理線程存在(因爲儘管程序已經鏈接了pthread庫, 但是未必會使用多線程).
程序第一次調用pthread_create時, linuxthreads發現管理線程不存在, 於是創建這個管理線程. 這個管理線程是進程中的第一個線程(主線程)的兒子.
然後在pthread_create中, 會通過pipe向管理線程發送一個命令, 告訴它創建線程. 即是說, 除主線程外, 所有的線程都是由管理線程來創建的, 管理線程是它們的父親.
於是, 當任何一個子線程退出時, 管理線程將收到SIGUSER1信號(這是在通過clone創建子線程時指定的). 管理線程在對應的sig_handler中會判斷子線程是否正常退出, 如果不是, 則殺死所有線程, 然後自殺.
那麼, 主線程怎麼辦呢? 主線程是管理線程的父親, 其退出時並不會給管理線程發信號. 於是, 在管理線程的主循環中通過getppid檢查父進程的ID號, 如果ID號是1, 說明父親已經退出, 並把自己託管給了init進程(1號進程). 這時候, 管理線程也會殺掉所有子線程, 然後自殺. 那麼, 如果主線程是調用pthread_exit主動退出的呢? 按照posix的標準,這種情況下其他子線程是應該繼續運行的. 於是, 在linuxthreads中, 主線程調用pthread_exit以後並不會真正退出, 而是會在pthread_exit函數中阻塞等待所有子線程都退出了, pthread_exit纔會讓主線程退出. (在這個等等過程中, 主線程一直處於睡眠狀態.)
可見, 線程的創建與銷燬都是通過管理線程來完成的, 於是管理線程就成了linuxthreads的一個性能瓶頸.
創建與銷燬需要一次進程間通信, 一次上下文切換之後才能被管理線程執行, 並且多個請求會被管理線程串行地執行.
注:以上的這些描述來自百度空間--
kouu's home!
線程管理初步
1.創建線程和結束線程
首先,我們需要了解下線程的引用標示符--pthread_t,這是一個命名別名,完整的定義如下:
typedef unsigned long int pthread_t;
下面介紹關於線程操作的相關係統函數
使調用進程終止,所有線程都終止了
等待線程
•由於一個進程中的多個線程是共享數據段的,通常在線程退出之後,退出線程所佔用的資源並不會隨着線程的終止而得到釋放
•pthread_join()函數
取消線程
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void print_msg(char *ptr);
int main()
{
pthread_t thread1, thread2;
int i,j;
char *msg1="Hello\n";
char *msg2="World\n";
pthread_create(&thread1,NULL, (void *)(&print_msg), (void *)msg1);
pthread_create(&thread2,NULL, (void *)(&print_msg), (void *)msg2);
sleep(1);
return 0;
}
void print_msg(char *ptr)
{
int retval;
int id=pthread_self();
printf("Thread ID: %lx\n",id);
printf("%s",ptr);
pthread_exit(&retval);
}
這個程序並不完善,其中包含許多線程同步的問題需要解決。
#include <stddef.h>
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
void print_msg(char *ptr);
int main()
{
pthread_t thread1, thread2;
int i,j;
void *retval;
char *msg1="Hello\n";
char *msg2="World\n";
pthread_create(&thread1,NULL, (void *)(&print_msg), (void *)msg1);
pthread_create(&thread2,NULL, (void *)(&print_msg), (void *)msg2);
pthread_join(thread1,&retval);
pthread_join(thread2,&retval);
return 0;
}
void print_msg(char *ptr)
{
int i;
for(i=0;i<10000;i++)
printf("%s",ptr);
}
在兩個線程結束運行後,主進程才退出執行。