MST

星途 面试题库

面试题:C语言中如何在Linux环境下实现简单的非阻塞I/O并设置超时

请描述在Linux系统中,使用C语言实现非阻塞I/O操作,并为该操作设置超时机制的基本步骤和关键函数。假设你要从一个文件描述符(例如套接字)读取数据,简述如何做到在一定时间内若未读取到数据则超时返回。
21.9万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试
  1. 基本步骤
    • 设置文件描述符为非阻塞模式:通过fcntl函数修改文件描述符的标志。
    • 使用selectepoll等多路复用机制select相对简单,epoll在处理大量文件描述符时性能更优。
    • 设置超时时间:使用struct timeval结构体来定义超时时间。
    • 执行I/O操作:在多路复用机制返回有数据可读时,进行读取操作。
  2. 关键函数
    • fcntl函数:用于设置文件描述符为非阻塞模式。例如:
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
- **`select`函数**:多路复用函数,用于监听文件描述符集合的状态变化。
#include <sys/select.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds是需要检查的文件描述符总数(通常为最大文件描述符值加1)。readfdswritefdsexceptfds分别是读、写和异常事件的文件描述符集合。timeout为超时时间,如果设置为NULL则一直阻塞直到有事件发生。struct timeval结构体定义如下:

struct timeval {
    long    tv_sec;         /* seconds */
    long    tv_usec;        /* microseconds */
};

例如设置5秒超时:

struct timeval timeout;
timeout.tv_sec = 5;
timeout.tv_usec = 0;
- **`epoll`相关函数(以`epoll`为例简述)**:
    - **`epoll_create`函数**:创建一个`epoll`实例。
#include <sys/epoll.h>
int epoll_create(int size);

size参数已被忽略,但必须大于0。 - epoll_ctl函数:控制epoll实例中所监控的文件描述符。

int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);

epfdepoll_create返回的epoll实例文件描述符,op表示操作类型(如EPOLL_CTL_ADDEPOLL_CTL_MODEPOLL_CTL_DEL),fd是要操作的文件描述符,event指定事件类型和关联数据。 - epoll_wait函数:等待所监控的文件描述符上有事件发生。

int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);

epfdepoll实例文件描述符,events用于存储发生事件的文件描述符和事件信息,maxeventsevents数组的大小,timeout为超时时间(单位毫秒,-1表示一直阻塞,0表示立即返回)。 3. 示例代码(以select为例)

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

#define BUFFER_SIZE 1024

int main() {
    int fd = STDIN_FILENO; // 假设从标准输入读取,实际应用中可能是套接字等文件描述符
    int flags = fcntl(fd, F_GETFL, 0);
    fcntl(fd, F_SETFL, flags | O_NONBLOCK);

    fd_set read_fds;
    FD_ZERO(&read_fds);
    FD_SET(fd, &read_fds);

    struct timeval timeout;
    timeout.tv_sec = 5;
    timeout.tv_usec = 0;

    int activity = select(fd + 1, &read_fds, NULL, NULL, &timeout);
    if (activity < 0) {
        perror("select error");
        exit(EXIT_FAILURE);
    } else if (activity == 0) {
        printf("Timeout occurred\n");
    } else {
        char buffer[BUFFER_SIZE];
        ssize_t bytes_read = read(fd, buffer, sizeof(buffer));
        if (bytes_read < 0) {
            perror("read error");
            exit(EXIT_FAILURE);
        }
        buffer[bytes_read] = '\0';
        printf("Read data: %s\n", buffer);
    }

    return 0;
}

此代码先将标准输入设置为非阻塞模式,然后使用select函数设置5秒超时等待输入。如果超时则输出提示信息,如果有数据可读则读取并输出。实际应用中套接字等文件描述符的处理类似,但需根据具体协议进行操作。