2017-09-04 59 views
0

目前我正在網絡編程中我碰到的功能PSELECT的一個(附帶的概念),它解決了選擇,即問題的信號。使用select(),在intr_flag的測試和選擇的調用之間有問題,如果信號發生,如果永遠選擇塊,它將會丟失。怎麼做PSELECT塊使用信號屏蔽在網絡編程

if (intr_flag) 
handle_intr(); /* handle the signal */ 
if ((nready = select(...)) < 0) { 
if (errno == EINTR) { 
if (intr_flag) 
handle_intr(); 
} 

然而,它說,隨着PSELECT,我們現在可以代碼這個例子中可靠地

sigset_t newmask, oldmask, zeromask; 
sigemptyset(&zeromask); 
sigemptyset(&newmask); 
sigaddset(&newmask, SIGINT); 
sigprocmask(SIG_BLOCK, &newmask, &oldmask); /* block SIGINT */ 
if (intr_flag) //here 
handle_intr(); /* handle the signal */ 
if ((nready = pselect (... , &zeromask)) < 0) { 
if (errno == EINTR) {  //here 
if (intr_flag) 
handle_intr(); 
} 
... 
} 

,它給出的代碼是可靠的解釋是 - 測試intr_flag變量之前,我們阻止SIGINT。當pselect被調用時,它用空集(即零掩碼)替換進程的信號掩碼,然後檢查描述符,可能會進入睡眠狀態。但是,當pselect返回時,在調用pselect(即SIGINT被阻止)之前,進程的信號掩碼會重置爲其值。

但與PSELECT上面的代碼中提到的,我們阻止信號那麼如何才能檢查錯誤EINTR?由於pselect會阻塞所有信號,因此當發生中斷時,它應該阻止中斷或被傳送到進程。只有當pselect返回時才能傳遞信號。

根據在其前面的評論這裏提及,中斷或仍然可能發生信號PSELECT被調用之前在兩者之間的第一檢查和PSELECT或當PSELECT稱爲矛盾阻擋中斷的目的,行任何其他信號,因此應該導致競爭條件,因爲選擇在那裏。

請誰能解釋這是怎麼可能的,因爲我是新來的這些概念。

回答

1

好了,主要的想法是,這樣做ready = pselect(nfds, &readfds, &writefds, &exceptfds, timeout, &sigmask);相當於執行以下操作原子

sigset_t sigsaved; 

sigprocmask(SIG_SETMASK, &sigmask, &sigsaved); 
/* NB: NOTE-1 */ 
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout); 
sigprocmask(SIG_SETMASK, &sigsaved, NULL); 

要利用這一點,我們第一塊,比方說,SIGINT

sigset_t emptyset, blockset; 

sigemptyset(&blockset); 
sigaddset(&blockset, SIGINT); 
sigprocmask(SIG_BLOCK, &blockset, NULL); 

那時我們不能收到SIGINT,因此我們無法處理它。但我們不需要它,直到我們輸入pselect()。我們想要做的 我們已經封鎖SIGINT後什麼是設置一個適當的信號處理程序。我們有一個標誌static volatile int intr_flag = 0;在我們的主代碼之外聲明,我們定義了一個名爲handler()的處理程序,它只是簡單地intr_flag = 1;。因此,我們將其設置爲一個處理程序:

sa.sa_handler = handler; 
sa.sa_flags = 0; 
sigemptyset(&sa.sa_mask); 
sigaction(SIGINT, &sa, NULL); 

然後我們配置readfs(聲明沒有顯示在這裏), 初始化一個空的信號設定和調用pselect()

sigemptyset(&emptyset); 
ready = pselect(nfds, &readfds, NULL, NULL, NULL, &emptyset); 

因此,我將概述這一個更多的時間 - 我們沒有收到SIGINT,直到我們叫pselect()。我們不需要它。只要我們輸入pselect(),信號SIGINT將被解除阻塞(因爲提供給pselect()的空信號集),並且pselect()可以被SIGINT中斷。 而此時pselect()回報,我們再不會在pselect()收到任何進一步SIGINT,但如果SIGINT發生點,那麼我們將其檢測爲每errnoEINTR,如果我們檢查intr_flag,我們會發現它是1。我們會理解,我們需要相應的行爲。所以,很明顯信號處理器可以在信號解除阻塞後立即完成工作,而後者發生在pselect()調用本身內。

最重要的這裏的細節是,如果我們沒有特殊pselect()呼叫原子方式來實現,那麼我們有使用普通select()時做上述自己的片斷周圍/* NB: NOTE-1 */標籤的步驟。而且,因爲它不會是原子,我們將有機會在兩個操作之間將SIGINT交付給我們 - 其中/* NB: NOTE-1 */提示所在位置,即在我們解除信號傳遞並且在輸入select()之前。那時信號確實會丟失。這就是爲什麼我們需要撥打pselect()

總而言之,原子性pselect()是其使用的解釋。 如果你對這個概念不太熟悉,你可以參考維基百科上的article或計算機科學主題的專門書籍。

此外,我會提供我的答案,連接到LWN上的article,這是更詳盡的。

+0

很好的解釋:)謝謝! –

+0

不客氣。 –