Linux下可以用两个epoll监听同一个描述符吗,有事件发生时,怎么工作?

epoll相关的内核源码分析可以参考之前的一篇文章。对于多个epoll监听同一个描述符的情况,区分ET和LT模式进行分别实验测试。在LT模式下,fd文件事件只会处理文件等待队列头部的回调,而对于ET模式来说,会查找等待队列上未过期的注册事件项并响应该事件。

实验程序:

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <sys/epoll.h>
#include <arpa/inet.h>
#include <assert.h>
#include <stdio.h>
#include <unistd.h>
#include <errno.h>
#include <string>
#include <string.h>
#include <fcntl.h>
#include <stdlib.h>
#include <pthread.h>

/**
* 还是需要从内核底层原理来理解这里的机制
*/

int connfd;
int epollfd1, epollfd2;

int setnonblocking(int fd)
{
	int old_option = fcntl(fd, F_GETFL);
	int new_option = old_option | O_NONBLOCK;
	fcntl(fd, F_SETFL, new_option);
	return old_option;
}

void addfd(int epollfd, int fd, bool enable_et)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN;
	if (enable_et)
	{
		event.events |= EPOLLET;
	}
	epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
}

void modfd(int epollfd, int fd)
{
	epoll_event event;
	event.data.fd = fd;
	event.events = EPOLLIN;
	event.events |= EPOLLET;
	epoll_ctl(epollfd, EPOLL_CTL_MOD, fd, &event);
}

void* thread1(void* arg)
{
	char buf[1024];
	epoll_event event[1024];

	while (1)
	{
		int number = epoll_wait(epollfd1, event, 1024, -1);

		for (int i = 0; i < number; i++)
		{
			if (event[i].events & EPOLLIN)
			{
				//sleep(1);
				int fd = event[i].data.fd;
				printf("this is thread 1\n");

				memset(buf, 0, 1024);
				int ret = recv(fd, buf, 24, 0);
				printf("%s\n", buf);
			}
		}

		modfd(epollfd1, connfd);
	}

	return NULL;
}

void* thread2(void* arg)
{
	char buf[1024];
	epoll_event event[1024];
	while (1)
	{
		int number = epoll_wait(epollfd2, event, 1024, -1);

		for (int i = 0; i < number; i++)
		{
			if (event[i].events & EPOLLIN)
			{
				int fd = event[i].data.fd;
				printf("this is thread 2\n");

				memset(buf, 0, 1024);
				int ret = recv(fd, buf, 24, 0);
				printf("%s\n", buf);
			}
		}
		modfd(epollfd2, connfd);
	}

	return NULL;
}

int main(int argc, char* argv[])
{
	if (argc <= 2)
	{
		printf("usage: %s ip_address port_number\n",
			basename(argv[0]));
		return 1;
	}

	const char* ip = argv[1];
	int port = atoi(argv[2]);
	printf("ip is %s and port is %d\n", ip, port);

	int ret = 0;
	struct sockaddr_in address;
	bzero(&address, sizeof(address));
	address.sin_family = AF_INET;
	inet_pton(AF_INET, ip, &address.sin_addr);
	address.sin_port = htons(port);

	int listenfd = socket(PF_INET, SOCK_STREAM, 0);
	assert(listenfd >= 0);

	int on = 1;
	if (setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) < 0) {
		printf("setsockopt failure!\n");
		return 0;
	}

	ret = bind(listenfd, (struct sockaddr*) & address, sizeof(address));
	assert(ret != -1);

	ret = listen(listenfd, 5);
	assert(ret != -1);

	epollfd1 = epoll_create(5);
	assert(epollfd1 != -1);

	epollfd2 = epoll_create(5);
	assert(epollfd2 != -1);

	struct sockaddr_in client_address;
	socklen_t client_addrlength = sizeof(client_address);

	int connfd = accept(listenfd,
					(struct sockaddr*) & client_address,
					&client_addrlength);
	if (connfd < 0) {

		printf("errno is: %d\n", errno);
		close(listenfd);
	}

	setnonblocking(connfd);

	int error;
	pthread_t td1, td2;

	addfd(epollfd1, connfd, true);
	addfd(epollfd2, connfd, true);

	pthread_create(&td1, NULL, thread1, NULL);
	pthread_create(&td2, NULL, thread2, NULL);

	pthread_join(td1, NULL);
	pthread_join(td2, NULL);


	close(connfd);
	close(listenfd);

	return 0;
}

发布了140 篇原创文章 · 获赞 65 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/paradox_1_0/article/details/103578275