當(dāng)前位置:首頁 > 嵌入式培訓(xùn) > 嵌入式學(xué)習(xí) > 講師博文 > linux線程庫詳解,還附帶典型實例哦
在linux環(huán)境下的并發(fā)程序設(shè)計有兩個選擇,分別是多進(jìn)程并發(fā)與多線程并發(fā)。
關(guān)于多進(jìn)程與多線程編程之前已經(jīng)有很多博文介紹過了,但關(guān)于線程庫的介紹卻很少。
在linux環(huán)境下當(dāng)采用多線程編程時,需要在編譯的時候加上-lpthread(或-pthread) 以顯示鏈接該庫。之所以這樣是因為pthread并非Linux系統(tǒng)的默認(rèn)庫,而是POSIX線程庫。而說起線程庫并不是一句話能夠說得清楚,因為這涉及到了Linux內(nèi)核和線程庫的發(fā)展史。
起初在線程概念出現(xiàn)之后,并沒有在類unix操作系統(tǒng)中得到廣泛支持。比如在linux2.4以及以前版本,因為還沒有線程的概念,linux內(nèi)核不知道什么是線程,程序員也就沒有辦法在操作系統(tǒng)上創(chuàng)建線程。而后來隨著技術(shù)發(fā)展,線程帶來的好處被大家所認(rèn)識,因為創(chuàng)建進(jìn)程開銷的資源更多,且進(jìn)程間的切換相比線程更慢。于是我們希望Linux能實現(xiàn)多線程編程,然而要修改一個操作系統(tǒng)并不是件容易的事情,于是采用的辦法是寫函數(shù)來實現(xiàn),而不是去修改操作系統(tǒng)的內(nèi)核。而這些函數(shù)也就是最初的線程庫,由于在linux內(nèi)核中沒有線程的概念,因此這種線程是用進(jìn)程來模擬的,實際上在不同的線程內(nèi)調(diào)用getpid()函數(shù),就會發(fā)現(xiàn)得到的值不同,因為它們在內(nèi)核的進(jìn)程鏈表中有不同的task_struct結(jié)構(gòu)體來表示,有各自不同的進(jìn)程標(biāo)識符PID。因此這種線程也被稱為用戶級線程。雖然當(dāng)時的線程庫已經(jīng)和POSIX的標(biāo)準(zhǔn)非常接近了,但是在linux的線程實現(xiàn)版本和POSIX標(biāo)準(zhǔn)之間還是存在著細(xì)微的差別,最明顯的是關(guān)于信號處理部分,這些差別中的大部分都受底層linux內(nèi)核的限制,而不是函數(shù)所能改變的。
許多項目都在研究如何才能改善linux對線程的支持,當(dāng)然這種改善不僅僅是清除POSIX標(biāo)準(zhǔn)和linux具體實現(xiàn)之間的細(xì)微差別,還要增強linux線程的性能和刪除一些不需要的限制,這其中大部分工作集中在了如何將用戶級的線程映射到內(nèi)核級的線程。
其中IBM公司的NGPT(Next Generation POSIX Threads),和Redhat公司的NPTL(Native POSIX Thread Library)通過修改linux內(nèi)核來支持新的線程庫,兩者都極大地提升了性能。在2002年,NGPT項目組宣布,由于不希望分化團隊,所以停止為NGPT添新功能,而只是繼續(xù)進(jìn)行l(wèi)inux上的線程支持工作,從而有效地將他們的重?fù)?dān)放到了NPTL的身上。也因此NPTL成為了linux線程的新標(biāo)準(zhǔn)。NPTL有了很多優(yōu)點:沒有使用管理線程。因為管理線程的一些需求,例如向作為進(jìn)程一部分的所有線程發(fā)送終止信號,是并不需要的,因為內(nèi)核本身就可以實現(xiàn)這些功能。內(nèi)核還會處理每個線程堆棧所使用的內(nèi)存的回收工作。它甚至還通過在清除父線程之前進(jìn)行等待,從而實現(xiàn)對所有線程結(jié)束的管理,這樣可以避免僵尸進(jìn)程的問題。
最后關(guān)于編譯參數(shù)-lpthread與-pthread其實是有區(qū)別的。主要在可移植性和安全性上。
在Linux中,pthread是作為一個單獨的庫存在的(libpthread.so),但是在其他Unix變種中卻不一定,比如在FreeBSD中是沒有單獨的pthread庫的,因此在FreeBSD中不能使用-lpthread來鏈接pthread,而使用-pthread則不會存在這個問題,因為FreeBSD的編譯器能正確將-pthread展開為該系統(tǒng)下的依賴參數(shù)。同樣道理,其他不同的變種也會有這樣那樣的區(qū)別,如果使用-lpthread,則可能在移植到其他Unix變種中時會出現(xiàn)問題,為了保持較高的可移植性,我們最好還是使用-pthread。在多數(shù)系統(tǒng)中,-pthread會被展開為-D_REENTRANT -lpthread,即是除了鏈接pthread庫外,還先定義了宏_REENTRANT。定義這個宏的目的,是為了打開系統(tǒng)頭文件中的各種多線程支持分支。比如,我們常常使用的錯誤碼標(biāo)志errno,如果沒有定義_REENTRANT,則實現(xiàn)為一個全局變量;若是定義了_REENTRANT,則會實現(xiàn)為每線程獨有,從而避免線程競爭錯誤。綜上所述,在編譯和鏈接時都使用-pthread 選項而不是傳統(tǒng)的-lpthread能夠保持向后兼容性和安全性。
Linux環(huán)境創(chuàng)建線程的實例。
#include
#include
#include
#include
#include
void *child_pthread(void *argc)
{
while(1)
{
printf("子線程process pid:%d,thread's id=%u\n",getpid(),(unsigned int)pthread_self());
sleep(2);
}
}
int main(void)
{
int ret;
pthread_t pid;
ret = pthread_create(&pid,NULL,child_pthread,NULL);
if(ret){
perror("pthread_create");
exit(0);
}
while(1)
{
printf("主線程process pid:%d,thread's id:%u\n",getpid(),(unsigned int)pthread_self());
sleep(2);
}
return 0;
}
在linux環(huán)境下創(chuàng)建線程采用pthread_create()
頭文件
#include
函數(shù)聲明
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
返回值
若成功則返回0,否則返回出錯編號
編譯運行結(jié)果如下圖所示,其中編譯選項-pthread指定鏈接線程庫。運行結(jié)果主線程與子線程的進(jìn)程id一致,線程id則不同。