We are aware of the essential function that IO operations play in reading from and writing to files. However, old IO operations can prevent the program from running and cause delays. Non-blocking IO methods can be used to resolve this issue. Since IO is non-blocking, a program can keep running while the IO operations are in progress. The “select” function is a frequently used tool in the C programming language to provide a non-blocking IO. With the “select” function, it helps to monitor numerous file descriptors, such as sockets or file handles, to read/write readiness or errors. The “select” function allows us to effectively manage several IO tasks without delaying the program’s execution. It provides a way to check the status of multiple IO resources continuously.
Let us discuss on how to implement non-blocking IO with the “select” function in the C language. We will discuss about the basic usage of “select” and provide a programming example to explain its application.
What Is the “Select” Function?
The “select” function is a powerful tool in the C language which helps us to implement a non-blocking IO. This function enables us to monitor multiple file descriptors, like sockets or file handles, to check if they are ready to read or write. The function takes three sets of file descriptors which are the read set, write set, and exception set. Using these sets, we can specify which descriptors we want to monitor for specific operations. The function takes a timeout value which allows us to specify the maximum time to wait for an event. When an event occurs on any of the monitored descriptors or when the timeout expires, “select” returns and provides information on the ready descriptors. This way, we can perform the IO operations efficiently without blocking the program execution which makes it suitable to handle multiple IO operations.
The “select” function for non-blocking IO brings several advantages. It allows us for efficient handling of multiple IO operations without requiring a thread per connection which reduces the resource consumption.
However, there are some disadvantages of the “select” function, such as the maximum number of file descriptors it can monitor, which is often limited by the operating system. Also, as the number of file descriptors increases, the performance of the “select” function can decrease.
Implementing the Non-Blocking IO with “Select” in C
Programming Example 1:
#include <stdlib.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h> // Include the <string.h> header for strlen
int main ()
{
// File descriptors to monitor
int fd1, fd2;
// Let us Open or create files and set them to non-blocking mode
fd1 = open ("file1.txt", O_RDONLY | O_NONBLOCK);
fd2 = open ("file2.txt", O_WRONLY | O_NONBLOCK);
fd_set read_fds, write_fds; // File descriptor sets
struct timeval timeout; // Timeout for select
while (1)
{
FD_ZERO (&read_fds); // Clear the read set
FD_ZERO (&write_fds); // Clear the write set
FD_SET(fd1, &read_fds); // Add fd1 to the read set
FD_SET(fd2, &write_fds); // Add fd2 to the write set
timeout.tv_sec = 4; // Set a timeout of 4 seconds
timeout.tv_usec = 0;
int ready_fds = select (fd2 + 1, &read_fds, &write_fds, NULL, &timeout);
if (ready_fds == -1) {
perror("select");
exit (EXIT_FAILURE);
}
else if (ready_fds == 0) {
printf ("Timeout occurred\n");
}
else
{
if (FD_ISSET(fd1, &read_fds)) {
// fd1 is ready for reading
char buffer [100]; // Create a buffer to read into
ssize_t bytesRead = read(fd1, buffer, sizeof (buffer) - 1);
if (bytesRead > 0) {
buffer [bytesRead] = '\0'; // Null-terminate the string
printf ("Read from file1.txt: %s \n", buffer);
}
}
if (FD_ISSET (fd2, &write_fds)) {
// fd2 is ready for writing
const char* message = "Good morning";
ssize_t bytesWritten = write (fd2, message, strlen (message));
if (bytesWritten > 0) {
printf ("Wrote to file2.txt: %s \n", message);
}
}
}
}
// Let us close the File descriptors
close (fd1);
close (fd2);
return 0;
}
Output:
Wrote to file2.txt: Good morning
Wrote to file2.txt: Good morning
Wrote to file2.txt: Good morning
Timeout occurred
Explanation:
In the program, we implement the non-blocking IO with “select” in the C language to monitor two files which are “file1.txt” and “file2.txt”. It sets the files to non-blocking mode which means that the program can now continue executing without waiting for the files to be fully read or written. The programming example uses the “select” function to check if there is any activity on the files within a specified timeout period. If there is no activity during the timeout, it only prints “Timeout occurred”. If there is activity, it checks which file has activity. If there is activity on “file1.txt”, the program reads the contents of the file and prints them. If there is activity on “file2.txt”, it prints a “Good morning” message to the file. The program continues to monitor the files indefinitely until it is terminated. Lastly, it closes the file descriptors to release the system resources.
Conclusion
The “select” function in C provides a good solution to implement the non-blocking I/O operations. By allowing the monitoring of multiple file descriptors, it enables the efficient handling of multiple I/O tasks without blocking the program’s execution. Although, it’s important to consider the disadvantages such as the maximum number of file descriptors that can be monitored and the potential performance issues with a large number of descriptors. Despite these flaws, the “select” function remains a good choice to manage the non-blocking I/O in the C programs.