Linux中的select函数是如何实现阻塞机制的?

Linux 中的 select 函数用于监控文件描述符的变化,当指定条件未满足时,它会阻塞程序执行,直到有文件描述符准备好或超时。

Linux中的select函数与阻塞

在Linux编程中,select函数是一种用于多路复用I/O的机制,它允许一个进程监视多个文件描述符(如套接字、管道等),以查看是否有任何文件描述符准备好进行读取、写入或有异常发生。select函数的使用涉及到阻塞和非阻塞两种模式,本文将详细探讨select函数及其阻塞行为。

linux select 阻塞

一、select函数

select函数原型如下:

int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);

nfds: 集合中所有文件描述符的范围,即所有文件描述符的最大值加1。

readfds: 需要监视的文件描述符集合,用于检查是否有数据可读。

writefds: 需要监视的文件描述符集合,用于检查是否可以写数据。

exceptfds: 需要监视的文件描述符集合,用于检查是否有异常条件发生。

timeout: 等待时间,决定select是阻塞还是非阻塞。

linux select 阻塞

二、阻塞与非阻塞行为

select函数的阻塞与非阻塞行为主要取决于timeout参数的值:

1、阻塞模式

timeout为NULL时,select进入阻塞模式,会一直等待直到至少有一个文件描述符变得可读、可写或有异常发生。

     struct timeval tv;
     tv.tv_sec = 5; // 5秒超时
     tv.tv_usec = 0;
     
     FD_ZERO(&readfds);
     FD_SET(sockfd, &readfds);
     int result = select(sockfd + 1, &readfds, NULL, NULL, &tv);

在这个例子中,如果没有数据到达,select会阻塞最多5秒。

2、非阻塞模式

linux select 阻塞

timeout设置为0时,select立即返回,无论是否有文件描述符准备好,这种模式下,select不会阻塞,主要用于轮询文件描述符状态。

     FD_ZERO(&readfds);
     FD_SET(sockfd, &readfds);
     int result = select(sockfd + 1, &readfds, NULL, NULL, NULL);
     if (result == 0) {
         printf("No data within non-blocking select
");
     }

在这个例子中,select会立即返回,如果有数据可读则返回正值,否则返回0。

3、超时模式

timeout设置为一个大于0的值时,select会在指定的时间后返回,无论是否有文件描述符准备好,这种方式可以防止无限期阻塞。

     struct timeval tv;
     tv.tv_sec = 2; // 2秒超时
     tv.tv_usec = 500000; // 0.5秒
     
     FD_ZERO(&readfds);
     FD_SET(sockfd, &readfds);
     int result = select(sockfd + 1, &readfds, NULL, NULL, &tv);
     if (result == 0) {
         printf("Timeout occurred! No data within 2.5 seconds.
");
     }

三、使用场景与注意事项

1、单 socket 操作

对于单个socket,可以使用阻塞模式,这样可以避免CPU空转,一个简单的客户端程序可以阻塞等待服务器响应。

2、多 socket 操作

对于多个socket,通常使用非阻塞模式配合循环,以实现高效的I/O复用,一个聊天服务器需要同时处理多个客户端连接。

3、结合epoll

在一些高并发场景下,select的性能可能不够理想,此时可以考虑使用更高效的epoll来替代select

四、代码示例

以下是一个完整的示例程序,展示了如何使用select函数在阻塞模式下等待数据的到来:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/time.h>
#include <errno.h>
void error(const char *msg) {
    perror(msg);
    exit(1);
}
int main() {
    int sockfd, newsockfd;
    char buffer[256];
    struct sockaddr_in server_addr, client_addr;
    struct timeval tv;
    fd_set readfds;
    int retval;
    socklen_t clilen;
    // Create socket
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) 
        error("ERROR opening socket");
    // Bind socket to port
    bzero((char *)&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(12345);
    
    if (bind(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) 
        error("ERROR on binding");
    // Listen for connections
    listen(sockfd, 5);
    clilen = sizeof(client_addr);
    // Accept connection
    newsockfd = accept(sockfd, (struct sockaddr *)&client_addr, &clilen);
    if (newsockfd < 0) 
        error("ERROR on accept");
    // Set up the file descriptor set
    FD_ZERO(&readfds);
    FD_SET(newsockfd, &readfds);
    
    // Set a timeout of 5 seconds
    tv.tv_sec = 5;
    tv.tv_usec = 0;
    
    // Use select for blocking wait
    retval = select(newsockfd + 1, &readfds, NULL, NULL, &tv);
    if (retval == -1)
        error("SELECT error");
    else if (retval) {
        printf("Data is available now.
");
        bzero(buffer, 256);
        retval = read(newsockfd, buffer, 255);
        if (retval < 0) error("ERROR reading from socket");
        printf("Here is the message: %s
", buffer);
    } else {
        printf("%d seconds passed and no data received.
", tv.tv_sec);
    }
    
    close(newsockfd);
    close(sockfd);
    return 0;
}

这个示例展示了一个简单的echo服务器,它在阻塞模式下使用select等待客户端的数据,如果5秒内没有收到数据,程序将输出超时信息。

五、FAQs

1、为什么select函数总是返回1?

select返回1表示有一个文件描述符已经准备好,这并不一定意味着有数据可读或可写,具体需要通过宏(如FD_ISSET)来判断哪个文件描述符准备好了。

     int result = select(...);
     if (result > 0 && FD_ISSET(sockfd, &readfds)) {
         // Read data from sockfd
     }

2、如何处理多个文件描述符的读写?

可以使用循环遍历所有文件描述符,检查它们是否在相应的集合中。

     for (int i = 0; i <= maxfd; i++) {
         if (FD_ISSET(i, &readfds)) {
             // Read from file descriptor i
         } else if (FD_ISSET(i, &writefds)) {
             // Write to file descriptor i
         }
     }

各位小伙伴们,我刚刚为大家分享了有关“linux select 阻塞”的知识,希望对你们有所帮助。如果您还有其他相关问题需要解决,欢迎随时提出哦!

原创文章,作者:未希,如若转载,请注明出处:https://www.kdun.com/ask/1338138.html

本网站发布或转载的文章及图片均来自网络,其原创性以及文中表达的观点和判断不代表本网站。如有问题,请联系客服处理。

(0)
未希新媒体运营
上一篇 2024-11-20 12:03
下一篇 2024-11-20 12:05

相关推荐

  • 如何在Linux系统上安装FastDFS?

    fastdfs linux安装步骤包括:下载源码,解压,编译,配置nginx,启动tracker和storage服务。

    2024-12-23
    029
  • DEB是什么格式?探索Linux下的Debian软件包

    DEB格式是Debian系统及其衍生发行版(如Ubuntu)使用的一种软件包格式,通过封装机制打包软件的所有必要文件和信息。

    2024-12-22
    041
  • 如何在Linux系统上更改Chrome浏览器的默认字体?

    在Linux操作系统中,Google Chrome浏览器的字体显示问题是一个常见且令人头疼的问题,Chrome在不同操作系统上的字体渲染引擎不同,这导致了在Linux系统上可能会出现字体显示不正常的情况,本文将详细介绍如何在Linux系统中优化Chrome浏览器的字体显示效果,并提供一些实用的解决方案和技巧,一……

    2024-12-22
    01
  • 服务器好用操作系统有哪些推荐?

    在当今数字化时代,服务器操作系统的选择对于企业和个人用户来说至关重要,不同的操作系统具有不同的特点和优势,适用于不同的应用场景,本文将深入探讨几种主流的服务器操作系统,包括Linux发行版、Windows Server以及FreeBSD等,分析它们的特点、用途和适用场景,以帮助读者做出更合适的选择,一、Linu……

    2024-12-22
    08

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注

产品购买 QQ咨询 微信咨询 SEO优化
分享本页
返回顶部
云产品限时秒杀。精选云产品高防服务器,20M大带宽限量抢购 >>点击进入