概念:
惊群效应就是当一个fd的事件被触发时,所有等待这个fd的线程或进程都被唤醒,一般都是socket的accept()会导致惊群,很多个进程都阻塞在server socket的accept(),一但有客户端连接,所有进程的accept()都会返回,但是只有一个进程会accept成功,就是惊群。
现状:
linux2.6内核后的版本已经没有惊群效应
测试:
测试环境内核版本
客户端使用脚本 client.sh
#!/bin/bash host="127.0.0.1" port=9527 num=10 for (( i=0; i<$num; i++)); do telnet $host $port > /dev/null 2>&1 & done
非IO多路复用,测试代码 tcpserver.cpp
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> int main() { int listenfd; pid_t parentid, childid; struct sockaddr_in servaddr; int forknum = 10; short port = 9527; servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); listenfd = socket(AF_INET, SOCK_STREAM, 0); int res = bind(listenfd, (sockaddr *)&servaddr, sizeof(sockaddr_in)); if (0 == res) printf("server bind success, port:%d\n", port); else { printf("server bind fail!\n"); exit(-1); } res = listen(listenfd, 100); if (0 == res) printf("server listen success!\n"); else { printf("server listen fail!\n"); exit(-1); } parentid = getpid(); for (int i=0; i<forknum; ++i) { if (getpid() == parentid) { childid = fork(); if (0 == childid) printf("the parentid is %d, chlidid is %d\n", getppid(), getpid()); } } for (; ;) { int connfd = accept(listenfd, NULL, NULL); if (connfd != -1) printf("the pid is %d, connfd is %d\n", getpid(), connfd); } }
启动进程,客户端通过telnet连接了3次,输出连接成功3次:
查看具体的fd:
epoll,测试代码 epollserver.cpp
#include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <arpa/inet.h> #include <stdlib.h> #include <unistd.h> #include <sys/epoll.h> #define MAX_EVENTS 10 int main() { int listenfd; pid_t parentid, childid; struct sockaddr_in servaddr; int forknum = 10; short port = 9527; servaddr.sin_family = AF_INET; servaddr.sin_addr.s_addr = htonl(INADDR_ANY); servaddr.sin_port = htons(port); listenfd = socket(AF_INET, SOCK_STREAM, 0); int res = bind(listenfd, (sockaddr *)&servaddr, sizeof(sockaddr_in)); if (0 == res) printf("server bind success, 0.0.0.0:%d\n", port); else { perror("bind fail"); exit(EXIT_FAILURE); } res = listen(listenfd, 100); if (0 == res) printf("server listen success\n"); else { perror("listen fail"); exit(EXIT_FAILURE); } parentid = getpid(); for (int i=0; i<forknum; ++i) { if (getpid() == parentid) { childid = fork(); if (0 == childid) printf("the parentid is %d, chlidid is %d\n", getppid(), getpid()); } } // epoll struct epoll_event ev, events[MAX_EVENTS]; int epollfd = epoll_create(10); if (-1 == epollfd) { perror("epoll_create fail"); exit(EXIT_FAILURE); } ev.events = EPOLLIN; ev.data.fd = listenfd; if (-1 == epoll_ctl(epollfd, EPOLL_CTL_ADD, listenfd, &ev)) { perror("epoll_ctl: listenfd fail"); exit(EXIT_FAILURE); } for (;;) { int nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); if (-1 == nfds) { perror("epoll_wait fail"); exit(EXIT_FAILURE); } for (int n = 0; n < nfds; ++n) { if (events[n].data.fd == listenfd) { int connfd = accept(listenfd, NULL, NULL); if (-1 == connfd) { perror("accept fail"); continue; } printf("the pid is %d, connfd is %d\n", getpid(), connfd); } } } }
初步结论:
linux比较新的内核(2.6+)已经不存在惊群效应