Linux 多路转接 —— select

这篇具有很好参考价值的文章主要介绍了Linux 多路转接 —— select。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

传统艺能😎

小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦
Linux 多路转接 —— select
1319365055

🎉🎉非科班转码社区诚邀您入驻🎉🎉
小伙伴们,满怀希望,所向披靡,打码一路向北
一个人的单打独斗不如一群人的砥砺前行
这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!!
社区用户好文均加精(“标兵”文章字数2000+加精,“达人”文章字数1500+加精)
直达: 社区链接点我


Linux 多路转接 —— select

select😍

回顾一下上一篇,select 是系统提供的一个多路转接接口

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 等待时间,返回时表示 timeout 的剩余时间。

参数 timeout 的取值:

  1. NULL/nullptr:select 调用后进行阻塞等待,直到被监视的某个事件就绪。
  2. 0:selec 调用后进行非阻塞等待,无论被监视的事件是否就绪,select 检测后都会立即返回。
  3. 特定的时间值:select 在指定的时间内进行阻塞等待,如果被监视的文件描述符上一直没有事件就绪,过时进行超时返回。

函数调用成功,则返回有事件就绪的文件描述符个数;如果 timeout 时间耗尽,则返回 0。调用失败则返回 -1,同时错误码会被设置。

select 调用失败时,错误码可能被设置为

EBADF:文件描述符为无效的或该文件已关闭
EINTR:此调用被信号所中断
EINVAL:nfds 参数为负值
ENOMEM:核心内存不足

fd_set 结构😒

fd_set 结构与 sigset_t 结构类似,fd_set 本质也是一个位图,用位图中对应的位来表示要监视的文件描述符:

Linux 多路转接 —— select

Linux 多路转接 —— select
调用 select 之前就需要用 fd_set 定义出对应的文件描述符集,然后将需要监视的文件描述符添加到文件描述符集当中,这个添加动作本质是进行位操作,但这个位操作不需要用户自己进行,系统提供了一组专门的接口,用于 fd_set 位图进行各种操作:

void FD_CLR(int fd, fd_set *set); //清除描述词组set中相关fd的位
int FD_ISSET(int fd, fd_set *set); //测试描述词组set中相关fd的位是否为真Linux 多路转接 —— select

void FD_SET(int fd, fd_set *set); //设置描述词组set中相关fd的位
void FD_ZERO(fd_set *set); //清除描述词组set的全部位

timeval 结构🤣

select 函数的最后一个参数是 timeout,这是一个指向 timeval 结构的指针,timeval 用于描述一段时间长度,该结构当中包含两个成员,其中 tv_sec 表示的是秒,tv_usec 表示的是微秒:

Linux 多路转接 —— select

socket 就绪条件😁

读条件🤣

  1. 接收缓冲区中的字节数,≥ 水位标记 SO_RCVLOWAT,此时可以无阻塞的读取该文件描述符,并且返回值大于0。
  2. socket TCP通信中,对端关闭连接,此时对该 socket 读,则返回 0。
  3. 监听的socket上有新的连接请求。
  4. socket 上有未处理的错误。

写就绪😍

  1. 发送缓冲区中的可用字节数,大于等于低水位标记SO_SNDLOWAT,此时可以无阻塞的写,并且返回值大于0。
  2. socket 的写操作被关闭(close或者shutdown),对一个关闭写操作的 socket 进行写操作,会触发SIGPIPE信号。
  3. socket 使用非阻塞connect连接成功或失败之后。
  4. socket 上有未读取的错误。

异常就绪😒

socket 上收到带外数据(带外数据和 TCP 紧急模式相关,TCP报头当中 URG 标志位和 16位 紧急指针搭配使用,就能够发送/接收带外数据)

select 工作流程😘

如果想实现一个 select 服务器,那么我们可以大概知道他的工作流程:

  1. 先初始化服务器,创建套接字,绑定和监听套接字
  2. 定义一个 fd_array 数组来保存监听套接字以及已经和客户端建立链接的套接字,监听套接字一开始就要加入数组
  3. 服务器循环进行 select 监测读事件是否就绪,如果就绪就可以执行对应操作
  4. 在 select 之前还应该创建一个 readfds 文件描述符集,将 fd_array 的文件描述符放入 readfds ,select 就会对这些文件描述符对应的读事件进行监视,在最后就会看到 在 readfds 里面会有一条条的记录
  5. select 检测到读事件就绪就会将其对应的文件描述符放进 readfds ,我们就能知道哪些事件已经就绪
  6. 如果是读事件的监听套接字就绪了,就用 accept 从底层全连接队列获取已建立的连接,并将对应连接的套接字添加到 fd_array 里
  7. 如果是读事件和客户端建立连接的套接字,就调用 read 将读取到的信息打印出来
  8. 如果读事件是和客户端建立连接的套接字就绪,也可能是因为客户端关闭了连接,此时服务器应该调用 close 关闭套接字,并将该套接字从 fd_array 数组中清除,因为下一次不需要再监视该读事件了。

因为传入 select 的 readfds、writefds 和 exceptfds都是输入输出型参数,当 select 返回时这些参数中的值已经修改了,因此每次调用 select 都需要重新设置,timeout 也是同理。

进行重新设置,就需要定义一个 fd_array 数组保存与客户端已经建立的若干连接和监听套接字,实际 fd_array 数组当中的文件描述符就是需要让 select 监视读事件的文件描述符。select 服务器只是读取客户端发来的数据,因此只需要让 select 帮我们监视特定文件描述符的读事件,如果要让 select 同时帮我们监视读事件和写事件,则需要分别定义 readfds 和 writefds,并定义两个数组分别保存需要被监视的文件描述符,便于每次调用 select 前对readfds 和 writefds 进行重新设置。

服务器刚开始运行时,fd_array 数组当中只有监听套接字,因此 select 第一次只需要告知监听套接字的读事件是否就绪,但每次调用accept 获取到新连接后,都会将对应的套接字添加到 fd_array 当中,后续 select 就需要监视监听套接字和连接套接字的读事件是否就绪。

由于调用 select 时还需要传入被监视的文件描述符中最大文件描述符值+1,因此每次在遍历 fd_array 对 readfds 进行重新设置时,还需要记录最大文件描述符值。

select 服务器实现😂

socket 类😒

首先编写一个 Socket 类,对套接字相关的接口进行一定程度的封装,为了能够直接调用 Socket 类中封装的函数,我们将这些函数定义成静态成员函数:

#pragma once

#include <iostream>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <cstring>
#include <cstdlib>

class Socket{
public:
	//创建套接字
	static int SocketCreate()
	{
		int sock = socket(AF_INET, SOCK_STREAM, 0);
		if (sock < 0){
			std::cerr << "socket error" << std::endl;
			exit(2);
		}
		//设置端口复用
		int opt = 1;
		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
		return sock;
	}
	//绑定
	static void SocketBind(int sock, int port)
	{
		struct sockaddr_in local;
		memset(&local, 0, sizeof(local));
		local.sin_family = AF_INET;
		local.sin_port = htons(port);
		local.sin_addr.s_addr = INADDR_ANY;
		
		socklen_t len = sizeof(local);

		if (bind(sock, (struct sockaddr*)&local, len) < 0){
			std::cerr << "bind error" << std::endl;
			exit(3);
		}
	}
	//监听
	static void SocketListen(int sock, int backlog)
	{
		if (listen(sock, backlog) < 0){
			std::cerr << "listen error" << std::endl;
			exit(4);
		}
	}
};

SelectServer 类😂

因为当前使用的是云服务器,所以编写的 select 服务器在绑定时不需要显示绑定IP地址,直接将IP地址设置为 INADDR_ANY 就行了,所以类当中只包含监听套接字和端口号两个成员变量。

在构造 SelectServer 对象时,需要指明 select 服务器的端口号,当然也可以在初始化 select 服务器的时候指明。初始化 select 服务器的时候调用 Socket 类当中的函数,依次进行套接字的创建、绑定和监听即可。在析构函数中可以选择调用 close 函数将监听套接字进行关闭,但实际也可以不用,因为服务器运行后一般是不退出的:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5

class SelectServer{
private:
	int _listen_sock; //监听套接字
	int _port; //端口号
public:
	SelectServer(int port)
		: _port(port)
	{}
	void InitSelectServer()
	{
		_listen_sock = Socket::SocketCreate();
		Socket::SocketBind(_listen_sock, _port);
		Socket::SocketListen(_listen_sock, BACK_LOG);
	}
	~SelectServer()
	{
		if (_listen_sock >= 0){
			close(_listen_sock);
		}
	}
};

运行服务器😊

服务器初始化完毕就应该周期性的执行某种动作了,而服务器要做的就是不断调用 select ,当事件就绪时执行对应某种动作即可。

首先,在 select 服务器开始死循环调用 select 函数,需要先定义一个 fd_array 数组,先把所有位置初始化为无效,并将监听套接字添加到该数组当中,fd_array 数组中保存的就是需要被select监视读事件是否就绪的文件描述符

此后,服务器就不断调用 select 监视读事件是否就绪,每次调用 select 之前都需要重新设置 readfds,具体设置过程就是遍历 fd_array 数组的文件描述符添加到 readfds 中,并同时记录最大的文件描述符值 maxfd,因为后续调用 select 时需要将maxfd+1作为第一个参数传入。

当 select 函后,如果返回值为0,则说明 timeout 时间耗尽,直接进行下一次 select 调用即可;如果返回值为 -1,则说明 select 调用失败,此时让服务器准备进行下一次 select 调用,但实际应该进一步判断错误码,根据错误码来判断是否应该继续调用 select 。如果 select 返回值大于 0,则说明 select 函数调用成功,此时已经有文件描述符的读事件就绪,接下来就应该对就绪事件进行处理:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class SelectServer{
private:
	int _listen_sock; //监听套接字
	int _port; //端口号
public:
	void Run()
	{
		fd_set readfds; //读文件描述符集
		int fd_array[NUM]; //被监视读事件是否就绪的文件描述符
		ClearFdArray(fd_array, NUM, DFL_FD); //将数组中的所有位置设置为无效
		fd_array[0] = _listen_sock; //将监听套接字添加到fd_array数组中的第0个位置
		for (;;){
			FD_ZERO(&readfds); //清空readfds
			//将fd_array数组当中的文件描述符添加到readfds当中,并记录最大的文件描述符
			int maxfd = DFL_FD;
			for (int i = 0; i < NUM; i++){
				if (fd_array[i] == DFL_FD) //跳过无效的位置
					continue;
				FD_SET(fd_array[i], &readfds); //将有效位置的文件描述符添加到readfds当中
				if (fd_array[i] > maxfd) //更新最大文件描述符
					maxfd = fd_array[i];
			}
			switch (select(maxfd + 1, &readfds, nullptr, nullptr, nullptr)){
				case 0:
					std::cout<<"timeout..."<<std::endl;
					break;
				case -1:
					std::cerr << "select error" << std::endl;
					break;
				default:
					//正常的事件处理
					std::cout<<"有事件发生..."<<std::endl;
					//HandlerEvent(readfds, fd_array, NUM);
					break;
			}//end switch
		}//end for
	}
private:
	void ClearFdArray(int fd_array[], int num, int default_fd)
	{
		for (int i = 0; i < num; i++){
			fd_array[i] = default_fd;
		}
	}
};

事件处理😍

当 select 检测到文件描述符的读事件就绪并成功返回后,就应该对事件进行处理了,这里编写一个 HandlerEvent 函数

在处理时需要遍历 fd_array 数组的文件描述符,依次判断各个文件描述符对应的读事件是否就绪,如果就绪则需要进行事件处理。当一个读事件就绪后,还需要进一步判断该文件描述符是否是监听套接字,如果是监听套接字就应该调用 accept 函数将底层的连接获取上来。但是只调用 accept 将连接获取上来还不够,为了下一次调用 select 时能让 select 帮我们监视新连接的读事件是否就绪,在连接获取上来后还应该将该连接对应的文件描述符添加到 fd_array 数组当中,这样在下一次调用 select 前对 readfds 重新设置时就能将该文件描述符添加进去了。

如果是与客户端建立连接的读事件就绪,那么就应该调用 read 函数读取客户端发来的数据,读取成功则进行打印。如果读取失败或者客户端关闭连接,那么 select 服务器应该调用 close 函数关闭对应连接,但此时只关闭连接也是不够的,还应该将该连接对应的文件描述符从 fd_array 数组当中清除,否则后续调用的 select 还会继续监视该连接的读事件是否就绪。

代码如下:

#pragma once

#include "socket.hpp"
#include <sys/select.h>

#define BACK_LOG 5
#define NUM 1024
#define DFL_FD - 1

class SelectServer{
private:
	int _listen_sock; //监听套接字
	int _port; //端口号
public:
	void HandlerEvent(const fd_set& readfds, int fd_array[], int num)
	{
		for (int i = 0; i < num; i++){
			if (fd_array[i] == DFL_FD){ //跳过无效的位置
				continue;
			}
			if (fd_array[i] == _listen_sock&&FD_ISSET(fd_array[i], &readfds)){ //连接事件就绪
				//获取连接
				struct sockaddr_in peer;
				memset(&peer, 0, sizeof(peer));
				socklen_t len = sizeof(peer);
				int sock = accept(_listen_sock, (struct sockaddr*)&peer, &len);
				if (sock < 0){ //获取连接失败
					std::cerr << "accept error" << std::endl;
					continue;
				}
				std::string peer_ip = inet_ntoa(peer.sin_addr);
				int peer_port = ntohs(peer.sin_port);
				std::cout << "get a new link[" << peer_ip << ":" << peer_port << "]" << std::endl;

				if (!SetFdArray(fd_array, num, sock)){ //将获取到的套接字添加到fd_array当中
					close(sock);
					std::cout << "select server is full, close fd: " << sock << std::endl;
				}
			}
			else if (FD_ISSET(fd_array[i], &readfds)){ //读事件就绪
				char buffer[1024];
				ssize_t size = read(fd_array[i], buffer, sizeof(buffer)-1);
				if (size > 0){ //读取成功
					buffer[size] = '\0';
					std::cout << "echo# " << buffer << std::endl;
				}
				else if (size == 0){ //对端连接关闭
					std::cout << "client quit" << std::endl;
					close(fd_array[i]);
					fd_array[i] = DFL_FD; //将该文件描述符从fd_array中清除
				}
				else{
					std::cerr << "read error" << std::endl;
					close(fd_array[i]);
					fd_array[i] = DFL_FD;
				}
			}
		}
	}
private:
	bool SetFdArray(int fd_array[], int num, int fd)
	{
		for (int i = 0; i <num; i++){
			if (fd_array[i] == DFL_FD){ //该位置没有被使用
				fd_array[i] = fd;
				return true;
			}
		}
		return false; //fd_array数组已满
	}
};

当调用 accept 从底层获取上来连接后,不能立即调用 read 读取连接当中的数据,因为此时新连接中的数据可能并没有就绪,如果直接调用read 可能需要进行阻塞等待,我们应该将等待过程交给 select 来完成,因此获取完连接后应该直接将该连接对应的文件描述符添加到 fd_array 数组当中就行了,当该连接的读事件就绪时 select 会告知我们,再进行数据读取就不会被阻塞了。

添加文件描述符到 fd_array 数组当中,本质就是遍历 fd_array 数组,找到一个没有被使用的位置将该文件描述符添加进去即可。但有可能 fd_array 数组中全部的位置都已经被占用了,那么文件描述符就会添加失败,此时就只能将刚刚获取上来的连接对应的套接字进行关闭,因为此时服务器没有能力处理这个连接。

至此 select 服务器编写完毕,用 telnet 工具连接我们的服务器,此时通过 telnet 向服务器发送的数据就能够被服务器读到并且打印输出了。
此外,虽然当前 select 服务器是一个单进程的服务器,但它却可以同时为多个客户端提供服务,根本原因就是因为 select 函数调用后会告知 select 服务器哪个客户端对应的连接事件就绪了,此时 select 服务器就可以读取发来的数据,读取完后又会调用 select 等待某个客户端连接的读事件就绪。

一些问题😁

当前的 select 服务器还存在一些问题:

select 服务器如果要向客户端发送数据,不能直接调用write函数,因为调用write函数时实际也分为 “等” 和“拷贝” 两步,我们也应该将 “等” 的这个过程交给 select ,因此在每次调用 select 之前,除了需要重新设置 readfds 还需要重新设置writefds,并且还需要一个数组来保存需要被监视写事件是否就绪的文件描述符,就绪时我们才能够调用 write 向客户端发送数据。

其次没有定制协议。代码中读取数据时并没有按照某种规则进行读取,此时就可能造成粘包问题,根本原因就是因为我们没有定制协议,比如HTTP协议规定在读取底层数据时读取到空行就表明读完了一个HTTP报头,此时再根据HTTP报头当中的 Content-Length 属性得知正文的长度,最终就能够读取到一个完整的HTTP报文。

没有对应的输入输出缓冲区。代码中直接将读取的数据存储到了字符数组 buffer 当中,这是不严谨的,因为本次数据读取可能并没有读取到一个完整的报文,此时服务器就不能进行数据的分析处理,应该将读取到的数据存储到一个输入缓冲区当中,当读取到一个完整的报文后再让服务器进行处理。此外服务器的响应数据也不应该直接调用 write 发送给客户端,应该先存储到一个输出缓冲区当中,因为数据可能很庞大,无法一次发送完毕,可能需要进行分批发送。

select 优点😍

当然,这也是所有多路转接接口的优点:

  1. 可以同时等待多个文件描述符,并且只负责等待,实际的IO操作由 accept、read、write 等接口来完成,这些接口在进行IO操作时不会被阻塞。
  2. select 可同时等待多个文件描述符,将“等”的时间重叠提高了IO的效率。

select 缺点😊

  1. 每次调用 select 都需要手动设置 fd 集合,非常不便。
  2. 每次调用 select 都需要把 fd 集合从用户态拷贝到内核态,这个开销在 fd 很多时会很大。
  3. 每次调用 select 都需要遍历传递进来的所有 fd,这个开销在 fd 很多时也很大。
  4. select 可监控的文件描述符数量太少。

这里我细嗦一下 select 的文件描述符数量,调用 select 时的 readfds、writefds以及exceptfds 都是fd_set结构,fd_set 结构本质是一个位图,它用每一个比特位来标记一个文件描述符,因此 select 可监控的文件描述符个数是取决于 fd_set 的比特位个数。

我们可以计算一下 fd_size 的比特位大小:

#include <iostream>
#include <sys/types.h>

int main()
{
	std::cout << sizeof(fd_set)* 8 << std::endl;
	return 0;
}

结果很明显,可监控的文件描述符数量为 1024 个 \color{red} {结果很明显,可监控的文件描述符数量为 1024 个} 结果很明显,可监控的文件描述符数量为1024

Linux 多路转接 —— select

适用场景🤣

多路转接接口的 select,poll epoll 都有自己适用的场景,在不恰当的场合使用某个接口只会适得其反。

多路转接接口一般适用于多连接,且多连接中只有少部分连接比较活跃。因为少量连接比较活跃,也就意味着几乎所有的连接在进行IO操作时,都需要花费大量时间来等待事件就绪,此时使用多路转接接口就可以将这些等的事件进行重叠,提高IO效率。对于多连接中大部分连接都很活跃的场景,就不适合多路转接了。因为每个连接都很活跃,也就意味着任何时刻事件基本都是就绪的,此时根本不需要动用多路转接接口来帮我们进行等待,毕竟使用多路转接接口需要花费系统时间和空间资源。

多连接中只有少量连接是比较活跃的,比如聊天工具,我们登录QQ后大部分时间其实是没有聊天的,此时服务端不可能调用一个 read 来阻塞等待读事件就绪。

多连接中大部分连接都很活跃,比如企业当中进行数据备份时,两台服务器之间不断在交互数据,这时的连接是特别活跃的,几乎不需要等的过程,也就没必要使用多路转接接口了。文章来源地址https://www.toymoban.com/news/detail-482634.html

到了这里,关于Linux 多路转接 —— select的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 多路转接方案:select poll epoll 介绍和对比

    内存和外设的交互叫做IO,网络IO就是将数据在内存和网卡间拷贝。 IO本质就是等待和拷贝,一般等待耗时往往远高于拷贝耗时。所以提高IO效率就是尽可能减少等待时间的比重。 IO模型 简单对比解释 阻塞IO 阻塞等待数据到来 非阻塞IO 轮询等待数据到来 信号驱动 信号递达时

    2024年02月08日
    浏览(38)
  • 多路转接高性能IO服务器|select|poll|epoll|模型详细实现

    那么这里博主先安利一下一些干货满满的专栏啦! Linux专栏 https://blog.csdn.net/yu_cblog/category_11786077.html?spm=1001.2014.3001.5482 操作系统专栏 https://blog.csdn.net/yu_cblog/category_12165502.html?spm=1001.2014.3001.5482 手撕数据结构 https://blog.csdn.net/yu_cblog/category_11490888.html?spm=1001.2014.3001.5482 去仓库获

    2024年02月15日
    浏览(50)
  • Linux 多路转接 —— poll

    小编是双非本科大二菜鸟不赘述,欢迎米娜桑来指点江山哦 1319365055 🎉🎉非科班转码社区诚邀您入驻🎉🎉 小伙伴们,满怀希望,所向披靡,打码一路向北 一个人的单打独斗不如一群人的砥砺前行 这是和梦想合伙人组建的社区,诚邀各位有志之士的加入!! 社区用户好文

    2024年02月10日
    浏览(40)
  • 【Linux】多路转接 -- epoll

    epoll系统调用和select以及poll是一样的,都是可以让我们的程序同时监视多个文件描述符上的事件是否就绪。 epoll在命名上比poll多了一个poll,这个e可以理解为extend, epoll就是为了同时处理大量文件描述符而改进的poll。 epoll在2.5.44内核中被引进,它几乎具备了select和poll的所有

    2024年02月14日
    浏览(39)
  • 【Linux】I/O多路转接技术

    前面我们学习的非阻塞IO,虽然能够在数据不就绪的时候处理其他事情,但是还是有一些不方便,而且每次都要为了一个文件描述符而进行等待,所以为了提高IO效率我们还要学习IO多路转接技术。 select 是系统提供的一个多路转接接口。 函数原型: 功能: select 系统调用可以

    2024年02月04日
    浏览(21)
  • 【Linux】IO多路转接——poll接口

    目录 poll初识 poll函数 poll服务器 poll的优点 poll的缺点 poll也是系统提供的一个多路转接接口。 poll系统调用也可以让我们的程序同时监视多个文件描述符上的事件是否就绪,和select的定位是一样的,适用场景也是一样的。 poll函数 poll函数的函数原型如下: 参数说明: fds:一

    2024年02月12日
    浏览(29)
  • 【Linux】IO多路转接技术Epoll的使用

    ​ 在学习 epoll 之前,我们首先了解一下Linux中的多路复用技术: 在Linux系统中, IO多路复用 是一种重要的技术,它允许一个进程同时监视多个文件描述符,一旦某个描述符准备好进行读取(通常是读就绪或写就绪),内核会通知该进程进行相应的读写操作。这样,我们可以

    2024年04月27日
    浏览(54)
  • 多路转接-epoll/Reactor(2)

    上次说到了poll,它存在效率问题,因此出现了改进的poll----epoll。 目前epoll是公认的效率最高的多路转接的方案。  epoll_create: 这个参数其实已经被废弃了。 这个值只要大于0就可以了。  这是用来创建一个epoll模型的。 创建成功了就返回一个文件描述符。失败了返回-1 epo

    2024年04月13日
    浏览(26)
  • 【网络】多路转接——poll | epoll

    🐱作者:一只大喵咪1201 🐱专栏:《网络》 🔥格言: 你只管努力,剩下的交给时间! 书接上文五种IO模型 | select。 poll 也是一种多路转接的方案,它专门用来解决 select 的两个问题: 等待fd有上限的问题。 每次调用都需要重新设置 fd_set 的问题。 如上图所示便是 poll 系统调

    2024年02月10日
    浏览(31)
  • 服务器(I/O)之多路转接

    1、阻塞等待:在内核将数据准备好之前,系统调用会一直等待。所有的套接字,默认都是阻塞方式。 2、非阻塞等待:如果内核没有将数据准备好,系统调用仍然会返回,并且会返回EWUOLDBLOCK或者EAGAIN错误码。 3、信号驱动:内核将数据准备好的时候,使用SIGIO信号通知应用程

    2024年02月09日
    浏览(51)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包