MST

星途 面试题库

面试题:C语言中如何使用select实现Linux下的非阻塞I/O

在Linux环境下,使用C语言编写一个简单的程序,利用select函数实现对一个文件描述符(如套接字)的非阻塞I/O操作,简述select函数的参数含义以及如何根据返回值判断I/O事件。
45.5万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

以下是示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/select.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define PORT 8888
#define MAX_EVENTS 10

int main() {
    int sockfd;
    struct sockaddr_in servaddr, cliaddr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 设置为非阻塞模式
    int flags = fcntl(sockfd, F_GETFL, 0);
    fcntl(sockfd, F_SETFL, flags | O_NONBLOCK);

    memset(&servaddr, 0, sizeof(servaddr));
    memset(&cliaddr, 0, sizeof(cliaddr));

    servaddr.sin_family = AF_INET;
    servaddr.sin_addr.s_addr = INADDR_ANY;
    servaddr.sin_port = htons(PORT);

    if (bind(sockfd, (const struct sockaddr *)&servaddr, sizeof(servaddr)) < 0) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    if (listen(sockfd, MAX_EVENTS) < 0) {
        perror("listen failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

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

    struct timeval timeout;
    timeout.tv_sec = 5;  // 等待5秒
    timeout.tv_usec = 0;

    int activity = select(sockfd + 1, &read_fds, NULL, NULL, &timeout);
    if (activity < 0) {
        perror("select error");
    } else if (activity == 0) {
        printf("select timeout\n");
    } else {
        if (FD_ISSET(sockfd, &read_fds)) {
            int connfd = accept(sockfd, (struct sockaddr *)&cliaddr, &(socklen_t){sizeof(cliaddr)});
            if (connfd < 0) {
                perror("accept failed");
            } else {
                printf("Accepted connection\n");
                close(connfd);
            }
        }
    }

    close(sockfd);
    return 0;
}

select函数参数含义

  1. nfds:监控的文件描述符集里最大文件描述符加1。
  2. readfds:指向fd_set结构的指针,用于检查可读性的文件描述符集合。
  3. writefds:指向fd_set结构的指针,用于检查可写性的文件描述符集合。
  4. exceptfds:指向fd_set结构的指针,用于检查是否有异常条件的文件描述符集合。
  5. timeout:指向timeval结构的指针,用于设置select等待的时间。NULL表示无限等待;timeval结构体中tv_sec是秒数,tv_usec是微秒数。

根据返回值判断I/O事件

  1. 返回值大于0:表示有文件描述符就绪,即有I/O事件发生。可以通过FD_ISSET宏来判断具体哪个文件描述符发生了事件。例如,若要判断sockfd是否可读,可使用if (FD_ISSET(sockfd, &read_fds))
  2. 返回值等于0:表示在指定的timeout时间内没有任何文件描述符就绪,即超时。
  3. 返回值小于0:表示发生错误,errno会被设置以指示错误类型,如EINTR表示操作被信号中断,EBADF表示文件描述符无效等。