首先来看代码
#include <stdio.h> #include <string.h> #include <sys/socket.h> #include <netinet/in.h> #include <unistd.h> #include <arpa/inet.h> #include <sys/wait.h> int main() { int sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd == -1 ) { perror("socket"); return -1; } struct sockaddr_in serv_addr; memset(&serv_addr, 0, sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(8080); serv_addr.sin_addr.s_addr = htonl(INADDR_ANY); if (bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) { perror("bind"); return -1; } if (listen(sockfd, 20) == -1) { perror("listen"); return -1; } for (int i = 0; i < 2; i++) { pid_t pid = fork(); if (pid < 0) { perror("fork"); } else if (pid == 0) { // child process while (1) { struct sockaddr_in cli_addr; memset(&cli_addr, 0, sizeof(cli_addr)); socklen_t cli_addr_len = 0; int connfd = accept(sockfd, (struct sockaddr*)&cli_addr, &cli_addr_len); if (connfd == -1) { perror("accept"); return -1; } char *cli_addr_str = inet_ntoa(cli_addr.sin_addr); int cli_addr_port = ntohs(cli_addr.sin_port); printf("pid: %d, cli addr: %s:%d\n", getpid(), cli_addr_str, cli_addr_port); char buff[4096] = {0}; if (recv(connfd, buff, sizeof(buff), 0) == -1) { perror("recv"); return -1; } char html_resp[] = "HTTP/1.1 200 OK\r\n\r\nhello"; if (send(connfd, html_resp, strlen(html_resp), 0) == -1) { perror("send"); return -1; } close(connfd); } } else if (pid > 0) { // parent process printf("forked pid: %d\n", pid); } } while (1) { pid_t end_pid = wait(NULL); if (end_pid == -1) { perror("wait"); return -1; } printf("child process %d end\n", end_pid); } close(sockfd); return 0; }
编译一下
g++ -Wall -g main.cpp -o addrreuse.exe
多次模拟请求
wget http://localhost:8080/123
服务端的输出如下:
forked pid: 23278 forked pid: 23279 pid: 23278, cli addr: 0.0.0.0:0 pid: 23279, cli addr: 0.0.0.0:0 pid: 23278, cli addr: 0.0.0.0:0 pid: 23279, cli addr: 0.0.0.0:0
事实证明,是可以多个进程来 accept 一个 sockfd 的,(好吧,高手当我在说废话就行了),然后注意到有一个东西叫惊群效应,http://blog.csdn.net/chenxinl/…
惊群效应:指一个fd的事件被触发后,等候这个fd的所有线程/进程都被唤醒。
低版本的linux内核在accept这边有所谓的“惊群效应”,是指一个链接过来,会把堵塞在accept上的所有子进程都唤醒;但是只有一个子进程都读取到链接,其他子进程唤醒后发现没有数据,继续睡觉去了;这样就产生了大量的进程切换开销。 但目前的内核版本已经修复了这个问题,一个链接过来,内核只会唤醒一个子进程出来accept,这样就不用担心惊群效应了。
但是对于其他类型的fd,惊群效应还是存在的,而且是必须的;比如多个进程wait在一个pipe的读事件上,有数据过来后,多个进程都会被唤醒,因为内核也不知道这些数据就给一个进程读的,还是每个进程都读一点,这个逻辑是应用层控制的;而accept也相当于一个读事件,但内核确认一个进程接受一个链接就可以了。
同理,如果多个子进程把同一个listen的fd添加到各自的epoll_wait中,有链接过来会导致所有的子进程唤醒,这也会导致惊群效应;但这个是应用层面需要解决的问题。
关于httpserver惊群效应和如何处理:http://bbs3.chinaunix.net/viewthread.php?action=printable&tid=1676724
但是另外看到这里,http://www.iteye.com/topic/561…
在2.6内核下,内核只会wakeup一个进程。就是说,2.6的内核下面,不会出现惊群的现象。