簡體   English   中英

暫停阻塞的套接字()

[英]Socket blocked by pause()

我正在研究接受傳入TCP連接的軟件,並且遇到了一些我不理解的東西。 首先,我將解釋軟件基本上在做什么。 請記住,有些部分是暫時的,我知道這很可能是一種糟糕的做事方式,但在原型設計過程中,我遇到了這個問題。

我有主進程為SIGINT建立一個信號處理程序。 然后主進程啟動一個新線程,將其稱為'listener',使用pthread_create()默認值。 偵聽器首先打開套接字,綁定,偵聽並設置套接字非阻塞。 然后,偵聽器將使用select()輪詢套接字,等待傳入連接。

現在,如果我在主線程中有一個啞的while(1)循環,我可以毫無問題地連接到套接字。 問題是:如果我用pause()替換while(1)循環,我就不能再連接到套接字了。 我知道監聽器線程仍然通過日志消息激活。 同樣,我不打算使用pause(),但我只是想知道發生了什么。

pause()阻止某個信號到達子線程嗎?

更新:我提供的精簡代碼似乎沒有表現出相同的行為。 如果我能確定原因,我會再次更新。

更新2:我發現了這個問題。 我發布的代碼與我的問題代碼之間存在一個重要區別。 以下是不同之處:

static void* listener_thread(void* arg)
{
    int listen_port = *(int *)arg;
    int listen_fd;
    fd_set readSet;
    int fdsMax, status;
    struct timeval timeout;

    if(open_listen_port(listen_port, &listen_fd) == -1)

...

int start_listener_thread(int port)
{
    int status = 0;

    if(0 > pthread_create(&thread_id, NULL, listener_thread, (void *)&port))

在main.c:

    if(0 == status && -1 == start_listener_thread(3000))

所以你可以看到我將端口號作為指向堆棧位置的指針傳遞給線程。 不是個好主意。 奇怪的是,如果我將pause()更改為while(1)循環,它將起作用。 而使用pause(),端口號恰好是一個有效的端口。

在start_listener_thread中為端口號分配空間可以解決問題。 感謝您的幫助!

代碼示例(剝離):

#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <pthread.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/select.h>
#include <unistd.h>

pthread_t thread_id;

void sighandler(int signum)
{
}

int open_listen_port(int listenPort, int* listenFd)
{
    struct sockaddr_in listenAddr;
    int flags;

    memset(&listenAddr, 0, sizeof(listenAddr));
    listenAddr.sin_family = AF_INET;
    listenAddr.sin_port = htons(listenPort);
    listenAddr.sin_addr.s_addr = INADDR_ANY;

    if( (*listenFd = socket(AF_INET, SOCK_STREAM, 0)) == -1 )
    {
        return(-1);
    }

    if( bind(*listenFd, (struct sockaddr*) &listenAddr,
        sizeof(listenAddr)) == -1 )
    {
        return(-1);
    }

    if( listen(*listenFd, 16) == -1 )
    {
        return(-1);
    }

    // change listener to be non-blocking
    flags = fcntl(*listenFd, F_GETFL);
    if(fcntl(*listenFd, F_SETFL, flags | O_NONBLOCK) == -1)
    {
        return(-1);
    }

    return (0);
}

static void* listener_thread(void* arg)
{
    int listen_fd;
    fd_set readSet;
    int fdsMax, status;
    struct timeval timeout;

    if(open_listen_port(3000, &listen_fd) == -1)
    {
        pthread_exit(NULL);
    }

    while(1)
    {
        FD_ZERO(&readSet);
        fdsMax = 0;
        timeout.tv_sec = 0;
        timeout.tv_usec = 500000;

        FD_SET(listen_fd, &readSet);
        if(listen_fd > fdsMax)
        {
            fdsMax = listen_fd;
        }

        status = select(fdsMax + 1, &readSet, NULL, NULL, &timeout);
    }
    return NULL;
}

int start_listener_thread()
{
    int status = 0;

    if(0 > pthread_create(&thread_id, NULL, listener_thread, NULL))
    {
        status = -1;
    }

    return(status);
}

int main(int argc, char *argv[])
{
    struct sigaction sigopt;
    int status = 0;

    memset(&sigopt, 0, sizeof(struct sigaction));
    sigopt.sa_handler = sighandler;
    if(0 != sigaction(SIGINT, &sigopt, NULL))
    {
        status = -1;
    }

    if(0 == status && -1 == start_listener_thread())
    {
        status = -1;
    }

    pause();

    return(0);
}

從OS man pause OS X:

DESCRIPTION
 Pause is made obsolete by sigsuspend(2).

 The pause() function forces a process to pause until a signal is received
 from either the kill(2) function or an interval timer.  (See
 setitimer(2).)  Upon termination of a signal handler started during a
 pause(), the pause() call will return.

man pause Linux:

DESCRIPTION
 pause()  causes the calling process (or thread) to sleep until a signal
 is delivered that either terminates the process or causes  the  invoca‐
 tion of a signal-catching function.

兩個手冊頁都暗示調用進程將休眠。 這解釋了無法調用accept()

你能確定是否正在調用accept()嗎? 您是否在適當的情況下檢查所有返回狀態和errno

我不確定睡眠線程的目的是什么。 如果必須保持主線程處於活動狀態,為什么不使用類似sleep()調用的while循環? (也許你打算稍后在這里添加代碼來進行輪詢?在這種情況下,使用usleep()以及你想要檢查的任何間隔,或者sleep(1)如果每秒一次的粒度是否足夠?)或者只是運行你的select()在主線程上。

編輯:看起來程序正在運行給我。 我把它修改如下:

--- /tmp/foo.c  2014-02-11 16:43:04.000000000 -0800
+++ /tmp/foo.c  2014-02-11 16:46:17.000000000 -0800
@@ -7,6 +7,7 @@
 #include <signal.h>
 #include <sys/select.h>
 #include <unistd.h>
+#include <stdio.h>

 pthread_t thread_id;

@@ -76,6 +77,7 @@
         }

         status = select(fdsMax + 1, &readSet, NULL, NULL, &timeout);
+        printf("select() woke up\n");
     }
     return NULL;
 }

當我進行上述更改時,它會打印select() woke up每半秒select() woke up一次,直到我連接到套接字。 然后它反復打印它。

你能更好地描述你看到的行為嗎? 是否存在阻塞的調用,例如讀取或寫入套接字?

你可以附加(或運行) gdb並找出每個線程正在做什么?

以下程序顯示在主線程暫停時線程繼續運行。 再加上Mike無法在兩個獨立的平台上重現您的問題,我認為請您重新確認您真正看到上述內容是公平的。

#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>

#define exitOnErr(msg) do { perror(msg); exit(EXIT_FAILURE); } while (0)

static void handler(int sig)
{
    printf("don't use printf in a signal handler\n");
}

void *athread(void* x)
{
    while (1)
    {
        printf("thread running\n");
        sleep(1);
    }
}

int main(int argc, char *argv[])
{
    printf("%d\n", getpid());

    pthread_t pid;

    if (signal(SIGINT, handler) == SIG_ERR)
        exitOnErr("signal");

    if(pthread_create(&pid, NULL, athread, NULL) != 0)
        exitOnErr("pthread_create");

    while(1)
    {
        pause();
        printf("pause returned\n");
    }
}

殺死-SIGINT“pid”並殺死-SIGTERM“pid”。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM