最近專案遇到乙個問題,程式退出的時候資源沒有正常釋放。經過除錯發現,原來是網路執行緒一直阻塞,導致一些必要的資源沒有被釋放,寫了幾個簡單的測試程式除錯了一下才明白,原來在linux下直接close socket的檔案描述符,並不會使程式中呼叫的一些阻塞式的socket函式(比如 read、recvfrom 等)退出阻塞,從而導致無法正常釋放資源。簡化示例如下。
下面是乙個簡化的udp服務程式,首先建立socket物件,然後開啟服務執行緒,將客戶端傳送過來的資料報回發給客戶端。當使用者在shell中敲入兩次回車後,程式退出。我們來觀察一下程式退出後,socket服務執行緒在怎樣的情況下可以正常退出。
#include#include
#include
#include
#include
#include
#define server_port 8888
#define buffer_len 256
int g_exit = 0;
void *service( void* arg )
printf("ok, service thread exit!\n");
pthread_exit(null);;
} int main( int argc,char * argv )
// free attribute
pthread_attr_destroy(&attr);
// wait user control exit
getchar();
getchar();
g_exit = 1;
printf("ok, waiting for thread exit...!\n");
close(fd);
// wait for thread exit
pthread_join(thr, &status);
printf("ok, exit main process !\n");
return 0;
}
上述程式,當使用者敲兩次回車後,顯示結果如下:
可以看到,沒有打出主程序和服務執行緒的退出資訊,無論是主程序還是服務執行緒都沒有正常退出,由此可見,直接close socket控制代碼,並不能使 recvfrom 函式退出阻塞。
那麼,如果把 pthread_join 換成 pthread_cancel 呢?結果是一樣的,雖然主程序退出了,但依然無法讓 service 執行緒正常退出。那麼,該如何才能正常退出 recvfrom 的阻塞呢?
網上搜了一下,可以考慮使用 shutdown 函式。
//shutdown函式原型為:#include
int shutdown(int s, int how);
//shutdown() 可以對套接字的關閉進行更細緻的控制,它允許對套接字進行單向關閉或全部禁止。
//引數 s 為待關閉的套接字描述符。
//引數 how 指定了關閉方式,具體取值如下:
//shut_rd : 將連線上的讀通道關閉,此後程序將不能再接收到任何資料,接收緩衝區中還未被讀取的資料也將被丟棄,但仍然可以在該套接字上傳送資料。
//shut_wr : 將連線上的寫信道關閉,此後程序將不能再傳送任何資料,傳送緩衝區中還未被傳送的資料也將被丟棄,但仍然可以在該套接字上接收資料。
//shut_rdwr : 讀、寫信道都將被關閉。
//執行成功返回 0,出錯則返回 -1,錯誤**存入 errno 中。
可以看到,service服務執行緒已經正常退出了。進一步測試,如果只是shutdown寫信道或者只shutdown讀通道呢?
經過測試可以發現,如果只關閉寫信道 shutdown(fd,shut_wr); 服務執行緒依然無法正常退出,而如果只關閉讀通道 shutdown(fd,shut_rd),則服務執行緒正常退出了。分析如下:因為recvfrom在fd的讀通道等待列表中,因此必須關閉讀通道時才能將recvfrom阻塞喚醒。
那麼,為啥shutdown就可以使得recvfrom退出阻塞,而close卻不能呢?
我的理解如下:shutdown破壞了socket連線的讀寫通道,導致讀寫阻塞的socket函式被喚醒,而close函式只是做了關閉連線釋放socket資源的操作,卻並沒有進行讀寫通道的清理工作,從而無法成功喚醒讀寫函式的阻塞。(期待高手給出更深層次的解釋)
進一步,那麼,解決這一問題,還有其他的什麼辦法沒有?
下面我簡單地羅列一下網上搜到的可行的一些方法,以後有時間再深入研究:
1. 設定socket傳送/接收超時
2. 使用非阻塞方式,非同步socket模型
如何停止被BlockingQueue阻塞的執行緒
如下阻塞佇列 執行緒的 很常見,當服務停止時,如何停止被blockingqueue阻塞的執行緒?blockingqueue blockingqueue newarrayblockingqueue 10 final thread thread newthread new runnable catch ...
阻塞 非阻塞socket的理解
b 阻塞socket是這樣的 b recv socket1,buf,length 去網絡卡緩衝區讀取socket1的資料,讀到的資料儲存到buf 如果網絡卡緩衝區有1個位元組,就返回1個,有兩個就返回兩個,當然不能超過length 如果網絡卡緩衝區沒有資料,那麼就一直等待,直到有資料可讀 是的,很傻...
程序的阻塞 喚醒 掛起 啟用
程序控制的一些概念 程序控制是程序管理中最基本的功能。建立 終止 可負責程序執行中的狀態轉換。程序控制一般是由os的核心中的原語來實現的。原語 primitive 是由若干條指令組成的,用於完成一定功能的乙個過程。它與一般過程的區別在於 它們是 原子操作 action operation 即不可分割...