What is IO multiplexing

I/O multiplexing

  • IO multiplexing is an efficient IO operation method, which can monitor multiple file descriptors at the same time, maximize the use of the time of blocking and waiting for IO operations, and improve system performance and response speed. Common IO multiplexing technologies include select, poll, and epoll, etc., all of which can simultaneously process multiple IO events in one thread without creating multiple threads or processes, thus saving system resources. In high-concurrency network programming, IO multiplexing is a common programming model, which is widely used in systems such as network servers and databases.

  • IO multiplexing mode: select, poll and epoll

  • select, poll, and epoll are commonly used IO multiplexing technologies, and they can all be used to monitor multiple file descriptors at the same time to achieve efficient IO operations.

1. select

select is one of the earliest IO multiplexing technologies, applicable to most UNIX systems and Windows systems. It is implemented through the select function, which can monitor multiple file descriptors at the same time, including sockets and other types of file descriptors. When an IO event occurs on the file descriptor, the select function will return and return the set of file descriptors with the IO event to the application. The application can determine which file descriptors have IO events by traversing this collection.

But select has some disadvantages. First of all, its file descriptor set is represented by the fd_set structure. The size of this structure is fixed, so the number of file descriptors it can monitor is limited. Secondly, every time the select function is called, the application needs to copy the file descriptor set from the user space to the kernel space, and this process is time-consuming. Finally, when there are a large number of file descriptors, every time the select function is called, all file descriptors need to be traversed. Even if only a small part of the file descriptors have IO events, this will waste system resources.

2. poll

Poll is also an IO multiplexing technology. Similar to select, it can also monitor multiple file descriptors at the same time, including sockets and other types of file descriptors. The difference is that the file descriptor set of poll is represented by the pollfd structure array. The size of this structure array is variable, so there is no upper limit to the number of file descriptors it can monitor. In addition, each time the poll function is called, the application does not need to copy the file descriptor set from the user space to the kernel space, and this process is relatively fast.

But poll also has some disadvantages. First of all, when there are a large number of file descriptors, it is still necessary to traverse all the file descriptors every time the poll function is called, even if only a small part of the file descriptors have IO events, which will waste system resources. Second, when there are a large number of file descriptors, the pollfd structure array can become very large, which can take up a lot of memory space.

3. epoll

epoll is an IO multiplexing mechanism provided by the Linux operating system. Compared with traditional select and poll, it has higher performance and stronger scalability.

The core concept of epoll is "event", which is the IO event of the file descriptor. It supports three types of events: read event, write event and error event. When an IO event occurs on a file descriptor, the operating system will notify the application, and the application will obtain the event through the epoll API and process it.

The main advantages of epoll are:

High performance: In the case of high concurrency, epoll has higher performance because it uses the event notification mechanism to only process events that need to be processed instead of traversing all file descriptors. Strong scalability: epoll supports a larger number of file descriptors than select and poll, and can handle hundreds of thousands or even millions of file descriptors. Less data copies between kernel space and user space.

code description

1. select


#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int fd1, fd2;
    fd_set read_fds;
    int max_fd;
    int ret;

    fd1 = STDIN_FILENO;
    fd2 = STDOUT_FILENO;

    FD_ZERO(&read_fds);
    FD_SET(fd1, &read_fds);
    FD_SET(fd2, &read_fds);
    max_fd = (fd1 > fd2) ? fd1 : fd2;

    while (1) {
        ret = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
        if (ret < 0) {
            perror("select error");
            exit(EXIT_FAILURE);
        }

        if (FD_ISSET(fd1, &read_fds)) {
            // fd1有数据可读
            // ...
        }

        if (FD_ISSET(fd2, &read_fds)) {
            // fd2有数据可读
            // ...
        }
    }

    return 0;
}
复制代码

2. poll


#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int fd1, fd2;
    struct pollfd fds[2];
    int timeout;
    int ret;

    fd1 = STDIN_FILENO;
    fd2 = STDOUT_FILENO;

    fds[0].fd = fd1;
    fds[0].events = POLLIN;
    fds[1].fd = fd2;
    fds[1].events = POLLIN;

    timeout = -1; // 永久等待

    while (1) {
        ret = poll(fds, 2, timeout);
        if (ret < 0) {
            perror("poll error");
            exit(EXIT_FAILURE);
        }

        if (fds[0].revents & POLLIN) {
            // fd1有数据可读
            // ...
        }

        if (fds[1].revents & POLLIN) {
            // fd2有数据可读
            // ...
        }
    }

    return 0;
}
复制代码

3. epoll


#include <sys/epoll.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
    int fd1, fd2;
    int epoll_fd;
    struct epoll_event event, events[2];
    int timeout;
    int ret;
    int i;

    fd1 = STDIN_FILENO;
    fd2 = STDOUT_FILENO;

    epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) {
        perror("epoll_create1 error");
        exit(EXIT_FAILURE);
    }

    event.data.fd = fd1;
    event.events = EPOLLIN;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd1, &event) < 0) {
        perror("epoll_ctl error");
        exit(EXIT_FAILURE);
    }

    event.data.fd = fd2;
    event.events = EPOLLIN;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd2, &event) < 0) {
        perror("epoll_ctl error");
        exit(EXIT_FAILURE);
    }

    timeout = -1; // 永久等待

    while (1) {
        ret = epoll_wait(epoll_fd, events, 2, timeout);
        if (ret < 0) {
            perror("epoll_wait error");
	        exit(EXIT_FAILURE);
        }
        for (i = 0; i < ret; i++) {
        if (events[i].data.fd == fd1) {
            // fd1有数据可读
            // ...
        }

        if (events[i].data.fd == fd2) {
            // fd2有数据可读
            // ...
        }
    }
}

return 0;
}
复制代码

in conclusion

IO multiplexing is an efficient, flexible and easy-to-maintain I/O programming method, which can effectively improve system performance and response speed, and avoid overhead and resource waste caused by thread or process switching. In practical applications, you can choose different IO multiplexing methods according to the application scenario, and pay attention to handling various abnormal situations and I/O event types. Using IO multiplexing can make the system more efficient, flexible and easy to maintain, and it is an indispensable and important technology in network programming and operating system design.

write at the end

感谢大家的阅读,晴天将继续努力,分享更多有趣且实用的主题,如有错误和纰漏,欢迎给予指正。 更多文章敬请关注作者个人公众号 晴天码字

Guess you like

Origin juejin.im/post/7222305855979094073