#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PROCESS_NUM 2
#define MAXEVENTS 1000
int sock_create_bind(int port){
int fd = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
return -1;
}
return fd;
}
int make_noblocking(int fd){
int val = fcntl(fd, F_GETFL);
val |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) < 0){
perror("fcntl set");
return -1;
}
return 0;
}
int main(int argc, char const *argv[])
{
int udp_fd, i;
struct epoll_event *events;
struct iovec iov[1];
struct msghdr msg;
struct sockaddr sa;
char buf[1024];
if (argc < 2){
printf("Input port\n");
exit(1);
}
if ((udp_fd = sock_create_bind(atoi(argv[1]))) < 0){
perror("socket_create_bind error");
exit(errno);
}
if (make_noblocking(udp_fd) < 0){
perror("make_noblocking error");
exit(errno);
}
events = malloc(MAXEVENTS * sizeof(struct epoll_event));
if (!events){
perror("malloc err");
exit(errno);
}
for (i = 0; i < PROCESS_NUM; i ++){
int pid = fork();
if (pid == 0){
int epoll_fd = epoll_create(MAXEVENTS);
if (epoll_fd < 0){
perror("Create epoll err");
exit(errno);
}
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = udp_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, udp_fd, &event) < 0){
perror("epoll_ctl err");
exit(errno);
}
while (1){
int num;
num = epoll_wait(epoll_fd, events, MAXEVENTS, -1);
// sleep(1);
printf("process %d get %d events\n", getpid(), num);
for (i = 0; i < num; i ++){
if (!(events[i].events & EPOLLIN)){
printf("get error event\n");
continue;
}else if (events[i].data.fd == udp_fd){
while (1){
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
msg.msg_name = &sa;
msg.msg_namelen = sizeof(sa);
msg.msg_iov = iov;
msg.msg_iovlen = 1;
msg.msg_control = NULL;
msg.msg_controllen = 0;
int n = recvmsg(events[i].data.fd, &msg, 0);
if (n < 0){
if (errno == EAGAIN){
printf("recvmsg() not ready\n");
break;
}else{
perror("recvmsg err");
break;
}
}
printf("read %d bytes: %s\n", n, buf);
}
}
}
}
}
}
for (i = 0; i < PROCESS_NUM; i ++){
wait(0);
}
free(events);
close(udp_fd);
return 0;
}
如果在epoll_wait之后sleep一秒,结果如下
两个进程均被唤醒,总共收到一个包。
小结:较新版本的内核做了一些优化,不一定会唤醒所有的进程。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PROCESS_NUM 2
#define MAXEVENTS 1000
int listen_sock_create_bind_listen(int port){
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
return -1;
}
if (listen(fd, 100) < 0){
return -1;
}
return fd;
}
int make_noblocking(int fd){
int val = fcntl(fd, F_GETFL);
val |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) < 0){
perror("fcntl set");
return -1;
}
return 0;
}
int main(int argc, char const *argv[])
{
int listen_fd, conn_fd, i;
struct epoll_event *events;
struct sockaddr sa;
char buf[1024];
if (argc < 2){
printf("Input port\n");
exit(1);
}
if ((listen_fd = listen_sock_create_bind_listen(atoi(argv[1]))) < 0){
perror("socket_create_bind error");
exit(errno);
}
conn_fd = accept(listen_fd, NULL, NULL);
if (conn_fd < 0){
perror("accept err");
exit(errno);
}
if (make_noblocking(conn_fd) < 0){
perror("make_noblocking error");
exit(errno);
}
events = malloc(MAXEVENTS * sizeof(struct epoll_event));
if (!events){
perror("malloc err");
exit(errno);
}
for (i = 0; i < PROCESS_NUM; i ++){
int pid = fork();
if (pid == 0){
int epoll_fd = epoll_create(MAXEVENTS);
if (epoll_fd < 0){
perror("Create epoll err");
exit(errno);
}
struct epoll_event event;
event.events = EPOLLIN | EPOLLET;
event.data.fd = conn_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, conn_fd, &event) < 0){
perror("epoll_ctl err");
exit(errno);
}
while (1){
int num;
num = epoll_wait(epoll_fd, events, MAXEVENTS, -1);
// sleep(1);
printf("process %d get %d events\n", getpid(), num);
for (i = 0; i < num; i ++){
if (!(events[i].events & EPOLLIN)){
printf("get error event\n");
continue;
}else if (events[i].data.fd == conn_fd){
while (1){
int n = recv(events[i].data.fd, buf, sizeof(buf), 0);
if (n < 0){
if (errno == EAGAIN){
printf("recv() not ready\n");
break;
}else{
perror("recv err");
break;
}
}else if (n == 0){
printf("client close connection\n");
break;
}
printf("read %d bytes: %s\n", n, buf);
}
}
}
}
}
}
for (i = 0; i < PROCESS_NUM; i ++){
wait(0);
}
free(events);
close(conn_fd);
close(listen_fd);
return 0;
}
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define PROCESS_NUM 2
#define MAXEVENTS 1000
int listen_sock_create_bind_listen(int port){
int fd = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_addr.s_addr = htonl(INADDR_ANY);
addr.sin_port = htons(port);
addr.sin_family = AF_INET;
if (bind(fd, (struct sockaddr*)&addr, sizeof(addr)) < 0){
return -1;
}
if (listen(fd, 100) < 0){
return -1;
}
return fd;
}
int make_noblocking(int fd){
int val = fcntl(fd, F_GETFL);
val |= O_NONBLOCK;
if (fcntl(fd, F_SETFL, val) < 0){
perror("fcntl set");
return -1;
}
return 0;
}
int main(int argc, char const *argv[])
{
int listen_fd, i;
struct epoll_event *events;
struct sockaddr sa;
char buf[1024];
if (argc < 2){
printf("Input port\n");
exit(1);
}
if ((listen_fd = listen_sock_create_bind_listen(atoi(argv[1]))) < 0){
perror("socket_create_bind error");
exit(errno);
}
if (make_noblocking(listen_fd) < 0){
perror("make_noblocking error");
exit(errno);
}
events = malloc(MAXEVENTS * sizeof(struct epoll_event));
if (!events){
perror("malloc err");
exit(errno);
}
for (i = 0; i < PROCESS_NUM; i ++){
int pid = fork();
if (pid == 0){
int epoll_fd = epoll_create(MAXEVENTS);
if (epoll_fd < 0){
perror("Create epoll err");
exit(errno);
}
struct epoll_event event;
event.events = EPOLLIN;
event.data.fd = listen_fd;
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) < 0){
perror("epoll_ctl err");
exit(errno);
}
while (1){
int num;
num = epoll_wait(epoll_fd, events, MAXEVENTS, -1);
// sleep(1);
printf("process %d get %d events\n", getpid(), num);
for (i = 0; i < num; i ++){
if (!(events[i].events & EPOLLIN)){
printf("get error event\n");
continue;
}else if (events[i].data.fd == listen_fd){
while (1){
// int n = recv(events[i].data.fd, buf, sizeof(buf), 0);
int conn_fd = accept(listen_fd, NULL, NULL);
if (conn_fd < 0){
if (errno == EAGAIN){
printf("accept() not ready\n");
break;
}else{
perror("accept err");
break;
}
}
close(conn_fd);
printf("process %d accept successful: \n", getpid());
}
}
}
}
}
}
for (i = 0; i < PROCESS_NUM; i ++){
wait(0);
}
free(events);
close(listen_fd);
return 0;
}
1、无论哪种情况,3.10版本的内核都会造成惊群。
2、4.18内核对epoll惊群有所优化,但是仍然存在惊群现象。
猜测:
1、由于udp报文不可分割,新版内核一般只唤醒一个进程。
2、由于tcp采用字节流的方式,所以内核唤醒多个进程。
总之,新版内核对于epoll惊群做了一些优化,具体细节需要再跟进。