High Performance Server Programming-poll of I/O Reuse

Keywords: socket

The previous section describes one of the ways to reuse I/O, select. This paper will discuss the second way of poll ing.

I. Polling

1. Functional prototype

Int poll(struct pollfd *fds, int nfds, int timeout);

Fds: Specify the set of events being monitored; pass the array header address, defined by the user; size is determined by nfds;

nfds: Specifies the size of the monitored event set fds.

Timeout: timeout;

> Number of 0 ready file descriptors

- 1 Error

0 timeout

2. The difference between Poll and select:

(1) There are more types of events that users pay attention to: in select, only readfds, writefd and excefds can be used to record the corresponding three events, while poll can use events in pollfd to set events;

(2) Separate representation of kernel modifications and user concerns, and no need to reset each call;

(3) File descriptors in poll are no longer set in bits, but directly set in int s.

The value of the file descriptor of the user's concern can be greater;

The number of file descriptors that users care about is determined by the user array, so the number will be more, 65535;

(4) Poll separates user-focused events from kernel-modified events and does not need to reset each call;

(5) Poll and select return both ready and unprepared. The time complexity of traversing the ready file descriptor is O(n). User programs still need to loop to check which file descriptors are ready.

3. Event types of user concern in poll

The red part is a few of our common events.

4. Advantages and disadvantages of poll:

Advantage:

Individually representing events of file descriptors that users are concerned about can focus on more event types.

Separate user-delivered and kernel modifications, and do not need to be reset before each poll call.

Poll function has no limit on maximum file descriptor.

Disadvantages:

Each call needs to copy the user space array into the kernel.

Each return requires copying all file descriptors into the user space array, whether or not they are ready;

All file descriptors are returned, and the time complexity of searching ready file descriptors is O(n).

2. Simple Implementation of Pol Server

1. Code

#define _GNU_SOURCE
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<string.h>
#include<unistd.h>

#include<arpa/inet.h>
#include<netinet/in.h>
#include<sys/types.h>
#include<sys/socket.h>
#include<poll.h>
#define SIZE 100
void Init_fds(struct pollfd *fds)//Initialize fds structure
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		fds[i].fd = -1;
		fds[i].events = 0;
		fds[i].revents = 0;
	}
}
void Insert_fd(struct pollfd*fds,int fd,short event)//Add file descriptors and corresponding events
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		if(fds[i].fd == -1)
		{
			fds[i].fd = fd;
			fds[i].events = event;
			break;
		}
	}
}
void Delete_fd(struct pollfd *fds,int fd)//Delete file descriptors and corresponding events
{
	int i = 0;
	for(;i < SIZE;++i)
	{
		if(fds[i].fd == fd)
		{
			fds[i].fd = -1;
			fds[i].events = 0;
			break;
		}
	}
}
int main()
{
	int sockfd = socket(AF_INET,SOCK_STREAM,0);
	assert(sockfd != -1);

	struct sockaddr_in ser,cli;
	memset(&ser,0,sizeof(ser));
	ser.sin_family = AF_INET;
	ser.sin_port = htons(6000);
	ser.sin_addr.s_addr = inet_addr("127.0.0.1");

	int res = bind(sockfd,(struct sockaddr*)&ser,sizeof(ser));
	listen(sockfd,5);

	struct pollfd fds[SIZE];
	Init_fds(fds);
	Insert_fd(fds,sockfd,POLLIN);

	while(1)
	{

		int n = poll(fds,SIZE,-1);
		if(n <= 0)
		{
			printf("poll error\n");
			continue;
		}

		//File descriptor ready
		int i = 0;
		for(;i < SIZE;++i)//foreach
		{
			if(fds[i].fd != -1) //Ready to judge
			{
				int fd = fds[i].fd;
				if(fds[i].revents & POLLRDHUP)
				{
					printf("%d will close\n",fd);
					close(fd);
					Delete_fd(fds,fd);
				}
				else if(fds[i].revents & POLLIN)
				{
					if(fd == sockfd)//Link events
					{
						int len = sizeof(cli);
						int c = accept(fd,(struct sockaddr*)&cli,&len);
						if(c < 0)
						{
							continue;
						}
						Insert_fd(fds,c,POLLIN | POLLRDHUP);
					}
					else//receive data
					{
						char buff[128] = {0};
						recv(fd,buff,127,0);
						printf("%d,%s\n",fd,buff);
						send(fd,"OK",2,0);
					}
				}
			}
		}
	}
	return 0;
}

2. Result demonstration:

Summary poll:

Poll can handle more types of events than select, and poll separates user-focused events from kernel-modified events. There is no need to reset each call.

When an event comes, poll inserts the FD of the event and the event that the user concerns into an element of the fds structure array in the user mode. The array fds (including ready and unprepared events) is then copied into the kernel state. The kernel identifies the specific events and processes them. After processing, the kernel modifies the revent in the corresponding fd, and then copies all the modified fds (including ready and unprepared events) into the user state. . This is the end of the execution of this event.

Posted by sara_kovai on Wed, 31 Jul 2019 08:43:51 -0700