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-09-24 16:10

相关推荐

  • 福建DDOS防御租用的价格是多少?

    福建DDoS防御租用价格背景介绍随着互联网的迅猛发展,网络攻击的频率和强度也在不断增加,DDoS(分布式拒绝服务)攻击由于其高效性和破坏性,成为最常见的一种网络攻击方式,在福建地区,为了应对这种威胁,很多企业和个人都需要租用DDoS防御服务,本文将详细介绍福建DDoS防御租用的价格及相关细节,DDoS防御的重要……

    2024-11-20
    00
  • ASP 模板标签是什么?如何使用它们来构建动态网页?

    ASP 模板标签是一种用于在网页中嵌入动态内容的技术。

    2024-11-20
    06
  • 如何搭建福建800g高防IP服务器?

    福建800g高防ip服务器是一种具备高性能、高安全性的互联网服务器,主要用于抵御大规模的网络攻击,如DDoS攻击和CC攻击,这种服务器通过配置强大的硬件防火墙和专业的流量清洗系统,能够有效过滤恶意流量,保障服务器的稳定运行,以下是关于如何搭建福建800g高防IP服务器的详细步骤:一、选择合适的服务商1、评估需求……

    2024-11-20
    06
  • 福建30g高防服务器如何配置与搭建?

    福建30g高防服务器的搭建是一个涉及多个步骤和技术细节的过程,以下是详细的实施步骤和考虑因素: 选择合适的服务提供商需要选择一个提供30G DDoS防护能力的服务提供商,在选择时,应考虑以下因素:防护能力:确保服务提供商能提供至少30G的DDoS防护能力,服务质量:了解服务提供商的信誉、客户评价以及技术支持水平……

    2024-11-20
    00

发表回复

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

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