In C, input/output (I/O) operations can be synchronous or asynchronous. Synchronous I/O operations block the execution of the program until the I/O operation is completed, while asynchronous I/O operations allow the program to continue executing while the I/O operation is being performed in the background.
Synchronous I/O operations are simpler to use and understand, as the program waits for the I/O operation to complete before proceeding. For example, the fread() function in C is a synchronous I/O operation that reads data from a file into a buffer:
FILE *fp;
char buffer[1024];
int count;
fp = fopen("file.txt", "r");
count = fread(buffer, sizeof(char), 1024, fp);
fclose(fp);
In this example, the fread() function blocks the program’s execution until the specified number of characters (1024 in this case) have been read from the file. This can be useful in situations where the program needs the entire contents of the file before continuing.
Asynchronous I/O operations, on the other hand, allow the program to continue executing while the I/O operation is being performed. This can be useful in situations where the I/O operation may take a long time to complete, such as when reading from a network socket or a slow hard drive.
In C, asynchronous I/O operations are typically implemented using non-blocking I/O and callbacks. Non-blocking I/O operations return immediately, even if the I/O operation is not yet complete. The program can then continue executing, and periodically check to see if the I/O operation is complete. When the I/O operation is complete, a callback function is called to handle the data that has been read or written.
The select() function in C is an example of a non-blocking I/O operation that can be used to implement asynchronous I/O:
int sockfd;
char buffer[1024];
fd_set read_fds;
struct timeval timeout;
sockfd = socket(AF_INET, SOCK_STREAM, 0);
// Set sockfd to non-blocking mode
fcntl(sockfd, F_SETFL, O_NONBLOCK);
connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
// Set up read_fds with sockfd
FD_ZERO(&read_fds);
FD_SET(sockfd, &read_fds);
timeout.tv_sec = 5;
timeout.tv_usec = 0;
// Wait for data to be available on sockfd, with a timeout of 5 seconds
if (select(sockfd+1, &read_fds, NULL, NULL, &timeout) > 0) {
// Data is available on sockfd, read it into buffer
recv(sockfd, buffer, 1024, 0);
}
In this example, the fcntl() function is used to set the sockfd socket to non-blocking mode. The select() function is then used to wait for data to be available on sockfd, with a timeout of 5 seconds. If data is available, the recv() function is used to read the data into the buffer variable.
Asynchronous I/O operations can be more complex to implement than synchronous I/O operations, but can offer significant performance benefits in some situations.