在 POSIX 線程編程中避免記憶體流失

來源:互聯網
上載者:User

 POSIX 線程(pthread)編程定義了一套標準的 C 程式設計語言類型、函數和常量 — 且 pthreads 提供了一種強大的線程管理工具。要充分使用 pthreads,您要避免常見錯誤。一個常見的錯誤就是忘記聯結可接合的線程,從而導致記憶體流失並增加工作量。在該篇技巧型文章中,學習 POSIX 線程基礎,瞭解如何識別和檢測線程記憶體流失,並獲得避免出現這種情況的可靠建議。

 

 

   

POSIX 線程簡介

使用線程的主要原因是要提高程式效能。線程的建立和管理只需要較小的作業系統開銷和較少的系統資源。一個進程內的所有線程共用相同的地址空間,使得線程間的通訊更高效,且比處理序間通訊更易於實現。例如,如果一個線程在等待一個輸入/輸出系統調用完成,其他線程可以處理 CPU 密集型任務。通過線程,可以優先調度重要任務 — 甚至中斷 — 低優先順序任務。可將偶爾發生的任務放在定期調度的任務之間,建立調度靈活性。最後,pthreads 是在多 CPU 電腦上進行並行編程的理想之選。

而且使用 POSIX 線程或 pthreads 的主要原因更加簡單:作為標準 C 語言線程編程介面的一部分,它們可高可移植的。

POSIX 線程編程有諸多優勢,但是如果您不明確一些基本規則,就有可能編寫一些難以調試的代碼並造成記憶體流失。我們首先回顧一下 POSIX 線程,分為可接合線程分離線程

可接合線程

如果您希望產生一個新的線程,且需要知道它是如何終止的,那麼您需要一個可接合線程。對於可接合線程,系統分配專用儲存空間來儲存線程終止狀態。線程終止後狀態得到更新。要獲得線程終止狀態,調用 pthread_join(pthread_t thread, void** value_ptr)

系統為每個線程分配底層儲存,包括堆棧、線程 ID、線程終止狀態等。這個底層儲存將一直保留在進程空間(且不能回收),直至線程終止並為其他線程所聯結。

分離線程

大多數時候,您只需建立一個線程,向其分配一些任務,然後繼續處理其他事務。在這些情況下,您不關注線程是如何終止的,這時使用分離線程是一個很好的選擇。

對於分離線程,線上程終止後系統自動回收其底層資源。

識別泄漏

如果您建立一個可接合的線程,但是忘記聯結它,其資源或私人記憶體一直儲存在進程空間中,從未進行回收再利用。一定要聯結可接合的線程;否則,可能會引起嚴重的記憶體流失問題。

例如,Red Hat Enterprise Linux (RHEL4)上的一個線程需要一個 10MB 的堆棧,這意味著,如果不聯結它,會有至少 10MB 的記憶體流失。假設您設計一個管理器-背景工作執行緒模式的程式來處理傳入的請求。然後需要建立越來越多的背景工作執行緒來執行各個任務,最後終止這些線程。如果它們是可接合的線程,且您沒有調用 pthread_join() 來聯結它們,那麼線上程終止後,每個產生的線程都將泄漏大量的記憶體(至少每堆棧 10MB)。隨著建立並在未聯結的情況下終止的背景工作執行緒越來越多,泄漏的記憶體量也持續增加。另外,進程將無法建立新的線程,因為無記憶體可供建立新線程使用。

清單 1 顯示在忘記聯結可接合線程時引發的嚴重記憶體流失。您還可以使用該代碼檢查可在一個進程空間中共存的線程體的最大量。

清單 1. 引發記憶體流失

#include<stdio.h>#include<pthread.h>void run() {   pthread_exit(0);}int main () {   pthread_t thread;   int rc;   long count = 0;   while(1) {      if(rc = pthread_create(&thread, 0, run, 0) ) {         printf("ERROR, rc is %d, so far %ld threads created/n", rc, count);         perror("Fail:");         return -1;      }      count++;   }   return 0;}

清單 1 中調用了 pthread_create() 來建立一個含預設線程屬性的新線程。預設情況下,新建立的線程是可接合的。它不斷建立新的可接合線程,直至有故障發生。然後輸出錯誤碼和故障原因。

使用以下命令在 Red Hat Enterprise Linux Server 5.4 上編譯清單 1 中的代碼時: [root@server ~]# cc -lpthread thread.c -o thread, 您將獲得清單 2 所示的結果。

清單 2. 記憶體流失結果

[root@server ~]# ./threadERROR, rc is 12, so far 304 threads createdFail:: Cannot allocate memory

在代碼建立了 304 個線程之後,它無法建立更多線程。錯誤碼是 12,這表示無更多記憶體可用。

如清單 1 和清單 2 所示,雖然產生了可接合線程,但是卻未將其聯結,因此每個終止的可接合線程仍然佔用進程空間,泄漏進程記憶體。

RHEL 上的一個 POSIX 線程擁有一個大小為 10MB 的私人堆棧。換言之,系統為每個 pthread 分配至少 10MB 的專用儲存。在我們的樣本中,304 個線程是在進程停止前建立的;這些線程佔用 304*10MB 記憶體,合計約 3GB。一個進程的虛擬記憶體的大小是 4GB,其中四分之一的進程空間是為 Linux 核心預留的。這樣一來,就有 3GB 的記憶體空間可用作使用者空間。因此,3GB 記憶體由死線程消耗。這是很嚴重的記憶體流失。而且很容易理解它發生的速度為何如此之快。

要修複泄漏,您可以添加代碼調用 pthread_join(),該方法可聯結每個可接合線程。

檢測泄漏

如同其他記憶體流失中一樣,進程啟動時問題可能沒那麼明顯。這裡介紹一種無需訪問原始碼便可檢測此類問題的方法:

  1. 計算進程中線程堆棧的數量。這包括正在啟動並執行活動線程和已終止線程的數量。
  2. 計算進程中正在啟動並執行活動線程的數量。
  3. 比較兩者。如果現有線程堆棧的數量大於正在啟動並執行活動線程的數量,且在程式運行時這兩個數位差量在不斷增加,那麼記憶體在泄漏。

這種記憶體流失很有可能是因未能聯結可接合線程而造成的。

使用 pmap 計算線程堆棧數

在一個啟動並執行進程中,線程堆棧的數量等於進程中線程體的數量。線程體包括啟動並執行活動線程和可接合的死線程。

pmap 是一種用於彙報進程記憶體的 Linux 工具。結合使用以下命令來擷取線程堆棧數:

[root@server ~]# pmap PID | grep 10240 | wc -l

(10240KB 是 Red Hat Enterprise Linux Server 5.4 上的預設堆棧大小。)

使用 /proc/PID/task 計算活動線程數

每次建立一個線程且該線程在運行時,會有一個條目填充到 /proc/PID/task 中。當線程終止時,不管該線程是可接合的還是分離的,都會將該條目從 /proc/PID/task 中刪除。因此活動線程數可通過運行以下命令得出:

[root@server ~]# ls /proc/PID/task | wc -l.

比較輸出

檢查 pmap PID | grep 10240 | wc -l 的輸出並將其與 ls /proc/PID/task | wc -l 的輸出進行比較。如果所有線程堆棧的數量大於活動線程的數量,且在程式運行時兩者的差量在持續增長,您可以確定記憶體流失問題確實存在。

預防泄漏

在編程過程中應當聯結可接合線程。如果您在程式中建立可接合的線程,切勿忘記調用 pthread_join(pthread_t, void**) 來回收分配給線程的專用儲存。否則,將引發嚴重的記憶體流失問題。

在編程後的測試階段,您可以使用 pmap/proc/PID/task 檢測這種泄漏是否存在。如果確實存在,檢查原始碼,看是否聯結了所有可接合線程。

就這些內容。只需少量預防工作即可為您省掉大量後續工作,避免令人頭疼的記憶體流失問題。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.