Linux gossip: introduction to Select

Posted by juancarlosc on Sun, 05 Dec 2021 18:42:32 +0100

1. Introduction to select

Under linux, we can use select for I/O reuse, monitor multiple file descriptors and judge whether there are qualified events.
When using the select function, we can see whether there are readable, writable or error events.

2 function explanation

2.1 function prototype

#include <sys/select.h>

int select(int nfds, fd_set *readfds, fd_set *writefds,
                  fd_set *exceptfds, struct timeval *timeout);

2.2 parameters

2.2.1 nfds

Integer variable whose value is the largest of all file descriptors plus 1. Its maximum value is FD_SETSIZE,FD_ SetSize is generally defined as 1024, which may be different for different systems.

2.2.2 readfds

This file description set is used to monitor whether the files in this file set are data readable. For example, if we monitor whether the serial port is data readable, we can put the file descriptor of the serial port into this set. When a readable event is monitored, the readfds file description set will clear the unreadable file descriptors and retain only the readable file descriptors. Set it to NULL when not needed.

2.2.3 writefds

This file description collection is used to monitor whether writable events occur. When monitored, the non writable file descriptors will be cleared and only the writable file descriptors will be retained. Set it to NULL when not needed.

2.2.4 exceptfds

This file description collection is used to monitor whether errors are sent. Set it to NULL when not needed.

2.2.5 timeout

The waiting time used when events in the monitored file collection do not occur. If it is NULL, it means blocking and waiting until the event occurs. If it is 0, it means returning immediately. If it is not 0, it means waiting time.

2.3 file description collection operation

The so-called file description set fd_set, a collection of file descriptors. Under linux, everything is a file, so devices, pipes, socket s, etc. are files and have their own file descriptors.

The file description set FD mentioned above_ Set, there are mainly the following four macros that can be operated. At the same time, the maximum number of files in the file description set is FD_SETSIZE. If this value is exceeded, some unknown situations will occur.

2.3.1 FD_ZERO()

Empty the file description set and set all the file description sets to 0.

2.3.2 FD_SET()

Add a file descriptor to the file description collection.

2.3.3 FD_CLR()

Deletes a file descriptor from the file description collection.

2.3.4 FD_ISSET()

Used to determine whether a file descriptor is a member of the file description collection.

2.4 notes

When the function returns normally, that is, when an event occurs, the event that does not occur will be cleared. Therefore, after processing the event, if you need to continue listening for all events, you need to re add all file descriptors to the file description set (that is, first integrate the situation description, and then re add all file descriptors).

Because the select function will update the timeout parameters, for example, set the timeout time to 5s, and then after the timeout exits, select will update the timeout time to 0. Therefore, if you need to listen again, you need to reset the timeout time.

2.5 example

As follows, a small demo will listen to two file descriptors in a loop, with a timeout of 5s.

int test()
{
	int s32FD1 = 17;  //File descriptor 1. The demo here is written as a fixed value
	int s32FD2 = 18;  //File descriptor 2. The demo here is written as a fixed value
	fd_set fds;		   //File description collection
	int s32MaxFd = s32FD2 + 1; //The maximum value in all file descriptors plus 1
	int s32Ret = 0;
	    
	struct timeval tv;

	while(1)
	{	
		/* Rejoin the file description collection */
		FD_ZERO(&fds);	   		//Empty file description collection
		FD_SET(s32FD1, &fds);  	//Add description set
		FD_SET(s32FD2, &fds);	    //Add description set
		
		/* Reset timeout */
		tv.tv_sec = 5;
		tv.tv_usec = 0;

		s32Ret = select(s32MaxFd, &fds, NULL, NULL, &tv);
		if (s32Ret > 0)
		{
			if (FD_ISSET(s32FD1, &fds) //FD1 file has readable events
			{
				//do something...
			}			
			else if (FD_ISSET(s32FD2, &fds) //FD2 file has readable events
			{
				//do something...
			}
		}
	}
	
	return 0;
}

3 detailed exploration of file descriptor set

The file description set is introduced above. What exactly is the file description set? Let's track it below.
View fd_set structure prototype, as follows:

/* The fd_set member is required to be an array of longs.  */
typedef long int __fd_mask;

/* fd_set for select and pselect.  */
typedef struct
  {
    /* XPG4.2 requires this member name.  Otherwise avoid the name
       from the global namespace.  */
#ifdef __USE_XOPEN
    __fd_mask fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->fds_bits)
#else
    __fd_mask __fds_bits[__FD_SETSIZE / __NFDBITS];
# define __FDS_BITS(set) ((set)->__fds_bits)
#endif
  } fd_set;

As can be seen from the above, FD_ The members in the set structure are long int arrays. In my linux system, FD_ The length of set is 128 bits, FD_ The value of SetSize is 1024. 128 * 8 = 1024, so each bit corresponds to 8 file descriptors. The first bit element of the array corresponds to descriptors 0 ~ 31, the second bit element corresponds to descriptors 32-63, and so on.

In the above example, we add the file descriptor 17 and 18 to the set. 17 and 18 are in the first bit element, so we can print the value of the first bit element to further determine.

	printf("fdset:%ld, sizeof:%d, FD_SETSIZE:%d\n", (long int)fds.fds_bits[0], sizeof(fd_set), FD_SETSIZE);

#fdset:393216, sizeof:128, FD_SETSIZE:1024

The value of fdset is 393216, which is converted to binary to 110 0000. You can see that the counting starts from 0 and the 17th and 18th bits are set to 1

When select listens to 17 and 18, 17 has a readable event and prints FD_ Value of set:

printf("fdset:%ld\n", (long int)fds.__fds_bits[0]);
#fdset:131072

The value of fdset is 131072, which is converted to binary to 100000. You can see that counting starts from 0, bit 17 is set to 1, and bit 18 is set to 0. Therefore, when you need to re select, you need to add all file descriptors to FD again_ set.

4. Disadvantages of select

The file description set of select contains all the file descriptors that need to be monitored. If the number is large, each call needs to be traversed from the beginning, increasing CPU consumption.
When select returns, we need to traverse to know which file descriptor is triggered.
When we need to listen again, we need to add all file descriptors to the file descriptor set again.

Topics: Linux Unix server