IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 系统运维 -> 高并发服务器epoll -> 正文阅读

[系统运维]高并发服务器epoll

epoll操作函数

epoll_create:创建监听红黑树

#include <sys/epoll.h>

// 驱使内核创建一棵epoll监听使用的 红黑树
int epoll_create(int size);
	size:指定创建的树所能挂载的fd个数。(仅供内核参考)
	返回值:
		成功:指向红黑树树根的 fd
		失败:-1, errno

epoll_ctl

    // 操作待监听、解除监听的 fd
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event);
	epfd: epoll_create函数返回值。
	op:操作:
		EPOLL_CTL_ADD:添加 fd 到监听红黑树
		EPOLL_CTL_MOD:修改监听红黑树一个 fd 属性。
		EPOLL_CTL_DEL:将 fd 从监听红黑树上摘下
    fd:待操作的 文件描述符
    event:描述fd监听事件(结构体地址)。
    	struct epoll_event {
               uint32_t     events;      /* Epoll events */
               epoll_data_t data;        /* User data variable */
        };
		events成员:fd对应的监听事件:
			EPOLLIN(读)
			EPOLLOUT(写)
			EPOLLERR(异常)
		data成员:(联合体、共用体)
            int          fd;
			void        *ptr;
struct test {
    int fd;
    void *call_back(fd, void *arg);
    ....
} *ptr;
            uint32_t     u32;
            uint64_t     u64;
	返回值:
		成功:0
		失败:-1, errno	

epoll_wait

// 阻塞监听 epfd 红黑树上的fd 是否满足对应监听事件。
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout);
	epfd: epoll_create函数返回值。
    events: 传出【数组】。传出的数组中,包含所有满足监听条件的fd及对应结构体。 
    maxevents:数组容量。
    timeout:超时。
    	-1:阻塞。
    	0:非阻塞
    	>0:超时时间(毫秒)
    返回值:
    	>0: 满足对应监听条件的文件描述符个数。用作 for 循环上限。
		0: 超时到达,没有满足条件的fd。
		-1: 错误,errno.

EPOLL事件有两种模型:
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发。

ET模式和LT模式的区别:

LT 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用 epoll_wait 时,会再次响应应用程序并通知此事件。

ET 模式:当 epoll_wait 检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用 epoll_wait 时,不会再次响应应用程序并通知此事件。

ET 模式在很大程度上减少了 epoll 事件被重复触发的次数,因此效率要比 LT 模式高。epoll 工作在 ET 模式的时候,必须使用非阻塞套接口,以避免由于一个文件句柄的阻塞读/阻塞写操作把处理多个文件描述符的任务饿死。

  • ET:边沿触发。 只支持 非阻塞模式。

    • fd 对应的缓冲区中,剩余数据不会触发 epoll_wait() 返回。
      • 设置方法:
        • int flg = fcntl(cfd, F_GETFL)
        • flg |= O_NONBLOCK;
        • fcntl(cfd, F_SETFL, flg);
        • event.events = EPOLLIN | EPOLLET
  • LT:水平触发(默认)

    • fd 对应的缓冲区中,剩余数据会触发 epoll_wait() 返回。
  • 结论

    • epoll 边沿触发模式固定用法:
      • 非阻塞 + 忙轮询while() + EPOLLIN | EPOLLET

epoll优缺点

  • epoll 的优点主要是一下几个方面:

1)监视的描述符数量不受限制,它所支持的 FD 上限是最大可以打开文件的数目,这个数字一般远大于 2048,举个例子,在 1GB 内存的机器上大约是 10 万左右,具体数目可以 cat /proc/sys/fs/file-max 察看,一般来说这个数目和系统内存关系很大。select() 的最大缺点就是进程打开的 fd 是有数量限制的。这对于连接数量比较大的服务器来说根本不能满足。虽然也可以选择多进程的解决方案( Apache 就是这样实现的),不过虽然 Linux 上面创建进程的代价比较小,但仍旧是不可忽视的,加上进程间数据同步远比不上线程间同步的高效,所以也不是一种完美的方案。

2)I/O 的效率不会随着监视 fd 的数量的增长而下降。select(),poll() 实现需要自己不断轮询所有 fd 集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而 epoll 其实也需要调用 epoll_wait() 不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,调用回调函数,把就绪 fd 放入就绪链表中,并唤醒在 epoll_wait() 中进入睡眠的进程。虽然都要睡眠和交替,但是 select() 和 poll() 在“醒着”的时候要遍历整个 fd 集合,而 epoll 在“醒着”的时候只要判断一下就绪链表是否为空就行了,这节省了大量的 CPU 时间。这就是回调机制带来的性能提升。

3)select(),poll() 每次调用都要把 fd 集合从用户态往内核态拷贝一次,而 epoll 只要一次拷贝,这也能节省不少的开销。

代码参考

#include <sys/epoll.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
 
int main(int argc, char *argv[])
{
 
	int ret;
	int fd;
	
	ret = mkfifo("test_fifo", 0666); // 创建有名管道
	if(ret != 0){
		perror("mkfifo:");
	}
	
	fd = open("test_fifo", O_RDWR); // 读写方式打开管道
	if(fd < 0){
		perror("open fifo");
		return -1;
	}
	
	ret = 0;
	struct epoll_event event;	// 告诉内核要监听什么事件
	struct epoll_event wait_event;
	
	
	int epfd = epoll_create(10); // 创建一个 epoll 的句柄,参数要大于 0, 没有太大意义
	if(-1 == epfd ){
		perror ("epoll_create");
		return -1;
    }
	
	event.data.fd = 0; 	   // 标准输入
	event.events = EPOLLIN; // 表示对应的文件描述符可以读
	
	// 事件注册函数,将标准输入描述符 0 加入监听事件
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, 0, &event);
	if(-1 == ret){
		perror("epoll_ctl");
		return -1;
    }
	
	event.data.fd = fd; 	// 有名管道
	event.events = EPOLLIN; // 表示对应的文件描述符可以读
	
	// 事件注册函数,将有名管道描述符 fd 加入监听事件
	ret = epoll_ctl(epfd, EPOLL_CTL_ADD, fd, &event);
	if(-1 == ret){
		perror("epoll_ctl");
		return -1;
    }
	
	ret = 0;
	
	while(1){			
		// 监视并等待多个文件(标准输入,有名管道)描述符的属性变化(是否可读)
		// 没有属性变化,这个函数会阻塞,直到有变化才往下执行,这里没有设置超时
		ret = epoll_wait(epfd, &wait_event, 2, -1);
		//ret = epoll_wait(epfd, &wait_event, 2, 1000);
		
		if(ret == -1){ // 出错
			close(epfd);
			perror("epoll");
		}else if(ret > 0){ // 准备就绪的文件描述符
		
			char buf[100] = {0};
			
			if( ( 0 == wait_event.data.fd ) 
			&& ( EPOLLIN == wait_event.events & EPOLLIN ) ){ // 标准输入
			
				read(0, buf, sizeof(buf));
				printf("stdin buf = %s\n", buf);
				
			}else if( ( fd == wait_event.data.fd ) 
			&& ( EPOLLIN == wait_event.events & EPOLLIN ) ){ // 有名管道
			
				read(fd, buf, sizeof(buf));
				printf("fifo buf = %s\n", buf);		
			}		
		}else if(0 == ret){ // 超时
			printf("time out\n");
		}
	}
	
	close(epfd);
	
	return 0;
}

  系统运维 最新文章
配置小型公司网络WLAN基本业务(AC通过三层
如何在交付运维过程中建立风险底线意识,提
快速传输大文件,怎么通过网络传大文件给对
从游戏服务端角度分析移动同步(状态同步)
MySQL使用MyCat实现分库分表
如何用DWDM射频光纤技术实现200公里外的站点
国内顺畅下载k8s.gcr.io的镜像
自动化测试appium
ctfshow ssrf
Linux操作系统学习之实用指令(Centos7/8均
上一篇文章      下一篇文章      查看所有文章
加:2022-04-26 12:16:02  更:2022-04-26 12:17:54 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/15 19:35:59-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码