IO多路复用

1.什么是多路复用?

多路复用是指通过一个线程同时监听多个IO事件的就绪状态。在传统的阻塞IO模型中,每个IO操作都需要一个独立的线程来处理,当有大量的IO操作时,会导致线程数量的增加,从而带来线程切换和上下文切换的开销。而多路复用通过使用一个线程来监听多个IO事件,避免了线程数量的增加,减少了线程切换和上下文切换的开销。

2.IO事件就绪通知:

IO多路复用机制通过操作系统提供的系统调用(如select、poll、epoll等)来监听多个IO事件的就绪状态。当有任何一个IO事件就绪时,内核会通知,告知哪些IO事件已经准备好可以进行读取或写入操作。

3.应用场景
  1. 网络编程:在网络编程中,服务器通常需要同时处理多个客户端的连接请求和数据传输。使用IO多路复用可以轻松管理多个网络套接字,监听并处理可读或可写的事件。这种方式可以实现高并发的服务器,提高系统的性能和扩展性。

  2. 高性能服务器:对于需要处理大量并发连接的服务器,如聊天服务器、实时通信服务器、游戏服务器等,使用IO多路复用可以有效地管理和处理多个客户端连接,提高服务器的吞吐量和响应速度。

  3. 文件操作:在文件操作中,当需要同时读取或写入多个文件时,可以使用IO多路复用来监听文件描述符的可读或可写事件,从而避免使用多线程或多进程的方式,提高文件操作的效率。

  4. 定时器和事件调度:IO多路复用可以用于实现定时器和事件调度功能。通过将定时器和事件的触发时间注册到IO多路复用机制中,可以在特定的时间点触发相应的事件,执行相应的操作。

创建方法:

1.创建文件描述符集合

2.添加文件描述符到集合中

3.通知内核开始监测

4.根据返回值做出对应操作

1.select  遵循未被使用原则

使用一个文件描述符集合来监听多个IO事件的就绪状态。应用程序需要将需要监听的文件描述符添加到集合中,然后调用select函数进行监听。当有文件描述符就绪时,select函数会返回,并告知哪些文件描述符已经准备好进行读取或写入操作,将其对应下标置1,然后应用程序可以通过遍历文件描述符集合来处理就绪的IO事件。

函数原型:int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
 功能:监听文件描述符集合
 参数:
nfds:监测的文件描述符上限值(最大文件描述符的值+1)
readfds:读文件描述符集合
writefds:写文件描述符集合
exceptfds:异常条件的描述符集合
timeout:设置超时时间
NULL:一直等待
返回值: 成功返回产生事件文件描述符个数
             失败返回-1 
             定时时间到达仍没有事件产生返回0 
1. void FD_CLR(int fd, fd_set *set);
   将fd从文件描述符集合中清除
2. int  FD_ISSET(int fd, fd_set *set);
   判断文件描述符fd是否仍在文件描述符集合中
3. void FD_SET(int fd, fd_set *set);
   将fd加入文件描述符集合中
4. void FD_ZERO(fd_set *set);
   文件描述符集合清0 

使用示例:使用select 函数实现tcp并发服务器

int init_tcp_ser(const char *ip ,unsigned short port)
{	
	int sockfd = socket(AF_INET, SOCK_STREAM, 0);
	if (-1 == sockfd)
	{
		perror("fail socket");
		return -1;
	}
	struct sockaddr_in ser;
	ser.sin_family = AF_INET;
	ser.sin_port = htons(port);
	ser.sin_addr.s_addr = inet_addr(ip);
	int ret = bind(sockfd, (struct sockaddr *)&ser, sizeof(ser));
	if (-1 == ret)
	{
		perror("fail bind");
		return -1;
	}

	ret = listen(sockfd, 128);
	if (-1 == ret)
	{
		perror("fail listen");
		return -1;
	}
	
	return sockfd;
}


int main(int argc, const char *argv[])
{
	pid_t pid = 0;
	int connfd = 0;
	pthread_t tid;
	int maxfd = 0;

	char buff[1024] = {0};


	int sockfd = init_tcp_ser("192.168.1.106", 50000);
	if (-1 == sockfd)
	{
		return -1;
	}
	
	fd_set rdfds;
	fd_set tmpfds;

	FD_ZERO(&rdfds);
	
	FD_SET(sockfd, &rdfds);
	maxfd = sockfd > maxfd ? sockfd : maxfd;

	while (1)
	{
		tmpfds = rdfds;
		int cnt = select(maxfd+1, &tmpfds, NULL, NULL, NULL);
		if (cnt < 0)
		{
			perror("fail select");
			return -1;
		}
		if (FD_ISSET(sockfd, &tmpfds))
		{
			int connfd = accept(sockfd, NULL, NULL);
			if (connfd < 0)
			{
				perror("fail accept");
				continue;
			}
			FD_SET(connfd, &rdfds);
			maxfd = connfd > maxfd ? connfd : maxfd;
		}
		
		for (int i = sockfd+1; i < maxfd; i++)
		{
			if (FD_ISSET(i, &tmpfds))
			{
				memset(buff, 0, sizeof(buff));
				ssize_t size = recv(i, buff, sizeof(buff), 0);
				if (size < 0)
				{
					perror("fail recv");
					FD_CLR(i, &rdfds);
					close(i);
					continue;
				}
				else if (0 == size)
				{
					FD_CLR(i, &rdfds);
					close(i);
					continue;	
				}
				printf("cli---> %s\n", buff);
				strcat(buff, "------ok!");
				size = send(i, buff, strlen(buff), 0);
				if (size < 0)
				{
					perror("fail recv");
					FD_CLR(i, &rdfds);
					close(i);
					continue;		
				}
			}
		}


	}

	return 0;
}

2.poll
epoll

epoll模型:
    1)epoll_create 创建epoll文件描述符集合
    2)epoll_ctl  添加关注的文件描述符
    3)epoll_wait 监控io事件
    4)epoll_ctl  从事件集合中删除完成的文件描述符
    1)epoll_create
      函数原型: int epoll_create(int size);
      功能:创建一个监听事件表(内核中)
      参数:size:监听事件最大个数
      返回值:
        成功返回非负值:表示epoll事件表对象(句柄)
        失败返回-1 
     2)epoll_ctl
     函数原型: int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
     功能:在监听事件表中新增一个事件
      参数:
        epfd:事件表文件描述符 
        op:EPOLL_CTL_ADD        新增事件
           EPOLL_CTL_MOD        修改事件 
           EPOLL_CTL_DEL        删除事件
        fd:文件描述符 
        events:事件相关结构体
      返回值:
        成功返回0 
        失败返回-1 

3)epoll_wait
      函数原型:int epoll_wait(int epfd, struct epoll_event *events,int maxevents, int timeout);
           功能:监听事件表中的事件,并将产生的事件存放到结构体数组中
      参数:
    epfd:事件表文件描述符
    events:存放结果事件结构体数组空间首地址 
    maxevents:最多存放事件个数
    timeout:超时时间
    -1:阻塞等待直到有事件发生 
      返回值:
        成功返回产生事件个数
        失败返回-1 
        超时时间到达没有事件发生返回0 
 

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值