Linux技巧:sleep与select命令详解

linux sleep slect

时间:2024-12-09 13:06


Linux下的Sleep与Select:高效并发控制的艺术 在Linux系统编程中,处理并发任务和事件驱动编程时,`sleep`和`select`是两个不可或缺的系统调用

    它们虽然看似简单,但在构建高效、响应迅速的应用程序时扮演着至关重要的角色

    本文将深入探讨这两个机制的工作原理、使用场景以及它们如何协同工作以实现复杂的并发控制逻辑,旨在帮助开发者更好地理解和应用这些工具

     一、sleep:时间等待的艺术 `sleep`命令是Linux中最基础的时间延迟机制之一,它使程序暂停执行指定的时间长度

    这个命令不仅可以在shell脚本中使用,也可以通过系统调用`sleep()`在C/C++等编程语言中实现

    `sleep`的时间单位可以是秒(默认)、分钟、小时或天,具体取决于传递给它的参数格式

     1.1 基本用法 在shell中,`sleep`的基本用法如下: sleep NUMBER【SUFFIX】 - `NUMBER`:等待的时间量

     - `SUFFIX`:可选的时间单位,如`s`(秒,默认)、`m`(分钟)、`h`(小时)、`d`(天)

     例如,让脚本暂停5秒: sleep 5 在C语言中,`sleep()`函数定义在` int sleep(unsigned int seconds); 该函数会使调用进程挂起指定的秒数

     1.2 使用场景 `sleep`广泛应用于需要定时执行任务的场景,如: - 轮询:定期检查某个条件是否满足,如文件是否存在、网络连接是否建立

     - 延时启动:在启动服务或执行操作前等待一段时间,确保依赖条件已就绪

     模拟:在测试或模拟中模拟延迟,观察系统行为

     尽管`sleep`简单易用,但在高精度或需要同时处理多个事件的场景下,它显得力不从心

    这时,`select`系统调用就显得尤为重要

     二、select:多路复用I/O的基石 `select`系统调用是Linux下实现多路复用I/O(Multiplexed I/O)的关键机制之一,允许一个进程同时监视多个文件描述符(通常是套接字或管道),查看它们是否准备好进行读、写或发生异常

    这种机制极大地提高了网络编程的效率,尤其是在处理大量并发连接时

     2.1 基本原理 `select`的工作原理基于三个集合:读集合、写集合和异常集合

    调用`select`时,进程会传入这三个集合的指针以及一个超时时间,然后`select`会阻塞(或超时后返回),直到至少有一个文件描述符就绪或超时发生

     在C语言中,`select`函数定义如下: include include include int select(int nfds, fd_setreadfds, fd_set writefds, fd_setexceptfds, struct timeval timeout); - `nfds`:监听的文件描述符数量中的最大值加1

     - `readfds`:指向读文件描述符集合的指针

     - `writefds`:指向写文件描述符集合的指针

     - `exceptfds`:指向异常文件描述符集合的指针

     - `timeout`:指定等待的超时时间,为`NULL`时`select`将无限等待

     2.2 使用流程 使用`select`的一般步骤如下: 1.初始化文件描述符集合:使用FD_ZERO清空集合,`FD_SET`添加感兴趣的文件描述符

     2.调用select:传入文件描述符集合和超时时间

     3.检查返回结果:select返回就绪的文件描述符数量,使用`FD_ISSET`检查哪些文件描述符已就绪

     2.3 示例代码 以下是一个简单的服务器使用`select`监听多个客户端连接的示例: include include include include include include include defineMAX_CLIENTS 100 defineBUFFER_SIZE 1024 int main() { intserver_fd,new_socket; structsockaddr_in address; int addrlen = sizeof(address); fd_set readfds; charbuffer【BUFFER_SIZE】= {0}; // 创建套接字 if((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == { perror(socketfailed); exit(EXIT_FAILURE); } // 绑定地址和端口 address.sin_family = AF_INET; address.sin_addr.s_addr = INADDR_ANY; address.sin_port = htons(8080); if(bind(server_fd, (struct sockaddr)&address, sizeof(address))<0) { perror(bindfailed); close(server_fd); exit(EXIT_FAILURE); } // 监听连接 if(listen(server_fd, < { perror(listen); close(server_fd); exit(EXIT_FAILURE); } // 将服务器套接字加入监听集合 FD_ZERO(&readfds); FD_SET(server_fd, &readfds); while(1) { fd_set rfds = readfds; // 复制当前监听集合 int retval =select(server_fd + 1, &rfds, NULL, NULL, NULL); if(retval == -{ perror(select()); } else if(retval) { if(FD_ISSET(server_fd, &rfds)) { // 接受新连接 if((new_socket = accept(server_fd, (struct sockaddr)&address, (socklen_t)&addrlen))<{ perror(accept); }else { FD_SET(new_socket, &readfds); // 将新连接加入监听集合 printf(New connection , socket fd is %d , ip is : %s , port : %d n , new_socket , inet_ntoa(address.sin_addr) , ntohs(address.sin_port)); } }else { // 处理已就绪的客户端连接 for(int i = 0; i < MAX_CLIENTS;i++){ if (FD_ISSET(i, &rfds)){ int valread =read(i, buffer,BUFFER_SIZE); printf(Received: %s , buffer); send(i , Hello from server , strlen(Hello from server) , 0); if(valread == { // 连接关闭 getpeername(i ,(struc