你讓我好奇。首先,顯而易見的是,由於沒有標準指定套接字應該喚醒,所以它不會被喚醒,因爲實現它會是一件非常痛苦的事情(因爲非阻塞標誌位於與套接字阻塞)。所以我們可以很自信地說,套接字在我們收到一個包之前不會醒來。還是會呢?
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <err.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <string.h>
#include <time.h>
#include <fcntl.h>
int sock;
static void
sighand(int s)
{
write(1, "sig\n", 4);
}
static void *
rcv(void *v)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = sighand;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGUSR1, &sa, NULL) == -1)
err(1, "sigaction");
char buf[64];
ssize_t sz = recv(sock, buf, sizeof(buf), 0);
printf("recv %d\n", (int)sz);
return NULL;
}
pthread_t t1, t2;
static void *
beeper(void *v)
{
for (;;) {
nanosleep(&((struct timespec){.tv_sec = 1}), NULL);
if (pthread_kill(t1, SIGUSR1))
errx(1, "pthread_kill");
printf("beep\n");
}
}
int
main(int argc, char **argv)
{
if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) == -1)
err(1, "socket");
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_port = htons(4711);
sin.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(sock, (struct sockaddr *)&sin, sizeof(sin)) == -1)
err(1, "bind");
if (pthread_create(&t1, NULL, rcv, NULL))
errx(1, "pthread_create");
if (pthread_create(&t2, NULL, beeper, NULL))
errx(1, "pthread_create");
/* pretend that this is correct synchronization. */
nanosleep(&((struct timespec){.tv_sec = 3}), NULL);
printf("setting non-block\n");
if (fcntl(sock, F_SETFL, O_NONBLOCK) == -1)
err(1, "fcntl");
printf("set\n");
nanosleep(&((struct timespec){.tv_sec = 3}), NULL);
return 0;
}
上面的代碼(對不起,不能縮短)。在recv中阻塞線程,等待一會兒,然後在文件描述符上設置非阻塞。如預期的那樣,沒有任何反應但後來我加了一個轉折。接收線程有一個信號處理程序,每秒喚醒一次,並使用SA_RESTART
。當然,因爲我們有SA_RESTART
recv
不應該醒來。
在OSX上,如果我們認爲任何行爲都是正確的,這將會「行爲不端」。我很確定這在所有的BSD上表現都是一樣的。在Linux上也是如此。事實上,通常我會執行SA_RESTART
的方式,我非常確定這將會在任何地方「不正常」。
好笑。這當然不意味着上面的代碼對任何事情都有用,但這是一個有趣的好奇心。要回答你的問題,這是不明確的,但在大多數情況下,它不會醒來,除非它會。請不要做這樣的怪事。
我無法在文檔中找到它,因此我會說「行爲未定義」;) –
我期望它不被支持,因爲套接字api是在多線程存在之前很久才建立的。 – alain
從多線程訪問套接字是一個壞主意,因爲它會引起競爭條件 - 例如,考慮T1的recv()調用返回錯誤(-1)的情況,並且T1響應我在套接字上調用close() ,就在T2調用fcntl()之前。在最好的情況下,T2的fcntl()調用將失敗(使用EBADF),因爲套接字句柄現在無效;在最壞的情況下,一些其他(不相關的)線程在過渡期間調用了socket(),並且被賦予了相同的fd值,現在T2已經做出了一個非阻塞的隨機套接字。 (這是一個非常難以追蹤的錯誤!) –