Advanced IO in Linux (non blocking IO and blocking IO, multiplexing IO, asynchronous IO)

Posted by Ryaan on Fri, 21 Jan 2022 09:32:16 +0100

1. Non blocking IO and blocking IO

(1) Difference between non blocking IO and blocking IO:

Non blocking IO is that the user directly obtains a result without waiting after sending an IO request. It is usually used with O_NONBLOCK works with fcntl.

Blocking IO is that when the user thread sends an IO request, the kernel will check whether the resource is ready. If it is not ready, it will wait for the data to be ready, and the user thread will be in a blocking state. The user thread will hand over the CPU. The common blocking functions include wait, pause, sleep and so on. When reading or writing some files.

(2) Advantages of non blocking IO: for the kernel, most of the internal default IO modes are set to blocking. This advantage is to give full play to the performance of the operating system, make the CPU work when needed, greatly improve the CPU utilization, reduce the time of standing still, and improve the efficiency of the overall system

Blocking IO:

Set the read function to read the contents entered by the mouse and keyboard. First block access to the mouse, and then block access to the keyboard. At this time, shake the mouse to get the contents of the mouse, and then enter the keyboard to get the contents of the keyboard. However, if you input the keyboard first, the process will always block the mouse input until you shake the mouse, which brings the trouble that the input must be in order.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(void)
{
	// Read mouse
	int fd = -1;
	char buf[200];
	
	fd = open("/dev/input/mouse1", O_RDONLY);
	if (fd < 0)
	{
		perror("open:");
		return -1;
	}
	
	memset(buf, 0, sizeof(buf));
	printf("before mouse read.\n");
	read(fd, buf, 50);
	printf("The contents read by the mouse are:[%s].\n", buf);
	
	
	// Read keyboard
	memset(buf, 0, sizeof(buf));
	printf("before keyboard read.\n");
	read(0, buf, 5);
	printf("The contents read by the keyboard are:[%s].\n", buf);
	
	
	return 0;
}

Note: for the standard input device of the keyboard, the corresponding file descriptor is 0; The mouse is not a standard device, but it can be viewed by ls /dev/input. After confirming the currently used device, open("/dev/input/mouse1", O_RDONLY); Open the corresponding device file and use the following method to find the mouse device.

Non blocking IO:

The simplest solution is to change the two IO positions to non blocking mode, which is similar to a polling mode. The corresponding IO operations are performed by looping reading the mouse and keyboard.

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>


int main(void)
{
	// Read mouse
	int fd = -1;
	int flag = -1;
	char buf[200];
	int ret = -1;
	
	fd = open("/dev/input/mouse1", O_RDONLY | O_NONBLOCK);
	if (fd < 0)
	{
		perror("open:");
		return -1;
	}
	
	// Make file descriptor No. 0 (stdin) non blocking
	flag = fcntl(0, F_GETFL);		// Get the original flag first
	flag |= O_NONBLOCK;				// Add non blocking attribute
	fcntl(0, F_SETFL, flag);		// Update flag
	// After these three steps, 0 becomes non blocking
	
	while (1)
	{
		// Read mouse
		memset(buf, 0, sizeof(buf));
		ret = read(fd, buf, 50);
		if (ret > 0)
		{
			printf("The contents read by the mouse are:[%s].\n", buf);
		}
		
		// Read keyboard
		memset(buf, 0, sizeof(buf));
		ret = read(0, buf, 5);
		if (ret > 0)
		{
			printf("The contents read by the keyboard are:[%s].\n", buf);
		}
	}
	
	return 0;
}

Analysis of experimental phenomena: whether you move the mouse or the keyboard first, you can print information, but the CPU is always consumed here, and other processes can not get CPU scheduling, which reduces the CPU utilization.

2.IO multiplexing

O multiplexing usually needs the help of select or poll function, which is expressed in the form of external blocking, internal non blocking, automatic polling and multi blocking IO

External blocking means that the select/poll function is blocked externally, which is the most common blocking method. Both IOS are encapsulated in select/poll. Internal non blocking automatic polling means that within the package, the mouse and keyboard inputs are always in the mode of automatic polling, and who meets the conditions will output. Multi channel blocking IO means that the package of mouse and keyboard is still blocking io.

select function:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/select.h>
#include <sys/time.h>


int main(void)
{
	// Read mouse
	int fd = -1, ret = -1;
	char buf[200];
	fd_set myset;
	struct timeval tm;
	
	fd = open("/dev/input/mouse1", O_RDONLY);
	if (fd < 0)
	{
		perror("open:");
		return -1;
	}
	
	// Currently, there are two FDS, one of which is 0
	// Process myset
	FD_ZERO(&myset);
	FD_SET(fd, &myset);
	FD_SET(0, &myset);
	
	tm.tv_sec = 10;
	tm.tv_usec = 0;

	ret = select(fd+1, &myset, NULL, NULL, &tm);
	if (ret < 0)
	{
		perror("select: ");
		return -1;
	}
	else if (ret == 0)
	{
		printf("Timeout\n");
	}
	else
	{
		// Wait until all the IOS arrive, and then monitor which IO arrives and deal with it
		if (FD_ISSET(0, &myset))
		{
			// The keyboard is handled here
			memset(buf, 0, sizeof(buf));
			read(0, buf, 5);
			printf("The contents read by the keyboard are:[%s].\n", buf);
		}
		
		if (FD_ISSET(fd, &myset))
		{
			// Here we deal with the mouse
			memset(buf, 0, sizeof(buf));
			read(fd, buf, 50);
			printf("The contents read by the mouse are:[%s].\n", buf);
		}
	}

	return 0;
}

poll function:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <poll.h>



int main(void)
{
	// Read mouse
	int fd = -1, ret = -1;
	char buf[200];
	struct pollfd myfds[2] = {0};
	
	fd = open("/dev/input/mouse1", O_RDONLY);
	if (fd < 0)
	{
		perror("open:");
		return -1;
	}
	
	// Initialize our pollfd
	myfds[0].fd = 0;			// keyboard
	myfds[0].events = POLLIN;	// Wait for read operation
	
	myfds[1].fd = fd;			// mouse
	myfds[1].events = POLLIN;	// Wait for read operation

	ret = poll(myfds, fd+1, 10000);
	if (ret < 0)
	{
		perror("poll: ");
		return -1;
	}
	else if (ret == 0)
	{
		printf("Timeout\n");
	}
	else
	{
		// Wait until all the IOS arrive, and then monitor which IO arrives and deal with it
		if (myfds[0].events == myfds[0].revents)
		{
			// The keyboard is handled here
			memset(buf, 0, sizeof(buf));
			read(0, buf, 5);
			printf("The contents read by the keyboard are:[%s].\n", buf);
		}
		
		if (myfds[1].events == myfds[1].revents)
		{
			// Here we deal with the mouse
			memset(buf, 0, sizeof(buf));
			read(fd, buf, 50);
			printf("The contents read by the mouse are:[%s].\n", buf);
		}
	}

	return 0;
}

3. Asynchronous IO

(1) What is asynchronous IO: asynchronous IO can be understood as a set of interrupt response system implemented by the operating system with software

(2) Working mode: we register an asynchronous IO event in the current process (use signal to register a SIGIO signal processing function), and then the current process can handle its own affairs normally. When an asynchronous event occurs, the current process will receive a SIGIO signal and execute the bound processing function to process the asynchronous event

(3) Functions involved:
fcntl(F_GETFL,F_SETFL,O_ASYNC,F_SETOWN)
signal or sigaction (SIGIO)

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <signal.h>


int mousefd = -1;

// Bind to SIGIO signal and handle asynchronous notification events within the function
void func(int sig)
{
	char buf[200] = {0};
	
	if (sig != SIGIO)
		return;

	read(mousefd, buf, 50);
	printf("The contents read by the mouse are:[%s].\n", buf);
}

int main(void)
{
	// Read mouse
	char buf[200];
	int flag = -1;
	
	mousefd = open("/dev/input/mouse1", O_RDONLY);
	if (mousefd < 0)
	{
		perror("open:");
		return -1;
	}	
	// Set the file descriptor of the mouse to accept asynchronous IO
	flag = fcntl(mousefd, F_GETFL);
	flag |= O_ASYNC;
	fcntl(mousefd, F_SETFL, flag);
	// Set the receiving process of asynchronous IO events as the current process
	fcntl(mousefd, F_SETOWN, getpid());
	
	// Register the SIGIO signal capture function of the current process
	signal(SIGIO, func);
	
	// Read keyboard
	while (1)
	{
		memset(buf, 0, sizeof(buf));
		read(0, buf, 5);
		printf("The contents read by the keyboard are:[%s].\n", buf);
	}
		
	return 0;
}

Topics: socket