159 Linux C++ 通讯架构实战14,epoll 函数代码实战

ngx_epoll_init函数的调用

    //(3.2)ngx_epoll_init函数的调用(要在子进程中执行)
    //四章,四节 project1.cpp:nginx中创建worker子进程;
    //nginx中创建worker子进程
    //官方nginx ,一个master进程,创建了多个worker子进程;
    // master process ./nginx 
    // worker process
    //(i)ngx_master_process_cycle()        //创建子进程等一系列动作
    //(i)    ngx_setproctitle()            //设置进程标题    
    //(i)    ngx_start_worker_processes()  //创建worker子进程   
    //(i)        for (i = 0; i < threadnums; i++)   //master进程在走这个循环,来创建若干个子进程
    //(i)            ngx_spawn_process(i,"worker process");
    //(i)                pid = fork(); //分叉,从原来的一个master进程(一个叉),分成两个叉(原有的master进程,以及一个新fork()出来的worker进程
    //(i)                //只有子进程这个分叉才会执行ngx_worker_process_cycle()
    //(i)                ngx_worker_process_cycle(inum,pprocname);  //子进程分叉
    //(i)                    ngx_worker_process_init();  //ngx_epoll_init函数的调用
    //(i)                        sigemptyset(&set);  
    //(i)                        sigprocmask(SIG_SETMASK, &set, NULL); //允许接收所有信号
    //(i)                        g_socket.ngx_epoll_init();  //初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
    //(i)                            m_epollhandle = epoll_create(m_worker_connections); 
    //(i)                            ngx_epoll_add_event((*pos)->fd....);
    //(i)                                epoll_ctl(m_epollhandle,eventtype,fd,&ev);
    //(i)                    ngx_setproctitle(pprocname);          //重新为子进程设置标题为worker process
    //(i)                    for ( ;; ) {

                                        ngx_process_events_and_timers(); //处理网络事件和定时器事件

                              }. ....                   //子进程开始在这里不断的死循环

    //(i)    sigemptyset(&set); 
    //(i)    for ( ;; ) {}.                //父进程[master进程]会一直在这里循环

ngx_epoll_init 函数内容

该函数从完成的事情有:

1.epoll_create  创建红黑树

2.m_pconnections = new ngx_connection_t[m_connection_n];  创建连接池 并初始化连接池

3.遍历所有监听socket【监听端口】,我们为每个监听socket增加一个 连接池中的连接

for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos){

c = ngx_get_connection((*pos)->fd); //从连接池中获取一个空闲连接对象

c->listening = (*pos);   //连接对象 和监听对象关联,方便通过连接对象找监听对象

(*pos)->connection = c;  //监听对象 和连接对象关联,方便通过监听对象找连接对象

        //对监听端口的读事件设置处理方法,因为监听端口是用来等对方连接的发送三路握手的,所以监听端口关心的就是读事件

        c->rhandler = &CSocekt::ngx_event_accept;

        //往监听socket上增加监听事件,从而开始让监听端口履行其职责【如果不加这行,虽然端口能连上,但不会触发ngx_epoll_process_events()里边的epoll_wait()往下走】if(ngx_epoll_add_event((*pos)->fd,       //socekt句柄1,0,             //读,写【只关心读事件,所以参数2:readevent=1,而参数3:writeevent=0】0,               //其他补充标记EPOLL_CTL_ADD,   //事件类型【增加,还有删除/修改】c                //连接池中的连接 ) == -1) {exit(2); //有问题,直接退出,日志 已经写过了}

}

4.ngx_epoll_add_event 函数 本质上就是epoll_ctl。并设置了回调函数。

#和网络相关
[Net]
#监听的端口数量,一般都是1个,当然如果支持多于一个也是可以的
ListenPortCount = 2
#ListenPort+数字【数字从0开始】,这种ListenPort开头的项有几个,取决于ListenPortCount的数量,
ListenPort0 = 80
ListenPort1 = 443#epoll连接的最大数【是每个worker进程允许连接的客户端数】,实际其中有一些连接要被监听socket使用,实际允许的客户端连接数会比这个数小一些
worker_connections = 1024

//初始化函数【fork()子进程之前干这个事】
//成功返回true,失败返回false
bool CSocekt::Initialize()
{ReadConf();  //读配置项bool reco = ngx_open_listening_sockets();  //打开监听端口return reco;
}//专门用于读各种配置项
void CSocekt::ReadConf()
{CConfig *p_config = CConfig::GetInstance();m_worker_connections = p_config->GetIntDefault("worker_connections",m_worker_connections); //epoll连接的最大项数m_ListenPortCount    = p_config->GetIntDefault("ListenPortCount",m_ListenPortCount);       //取得要监听的端口数量return;
}

    //连接池: 数组,元素数量就是worker_connections【1024】,每个数组元素类型为 ngx_connection_t【结构】; ---结构数组;
     //为什么要引入这个数组:  2个监听套接字, 用户连入进来,每个用户多出来一个套接字;
       //把 套接字数字跟一块内存捆绑,达到的效果就是将来我通过这个套接字,就能够把这块内存拿出来;

初始化 连接池

当执行完成如下的代码后,参考图

    do {i--;   //注意i是数字的末尾,从最后遍历,i递减至数组首个元素//好从屁股往前来---------c[i].data = next;         //设置连接对象的next指针,注意第一次循环时next = NULL;c[i].fd = -1;             //初始化连接,无socket和该连接池中的连接【对象】绑定c[i].instance = 1;        //失效标志位设置为1【失效】,此句抄自官方nginx,这句到底有啥用,后续再研究c[i].iCurrsequence = 0;   //当前序号统一从0开始//----------------------next = &c[i]; //next指针前移} while (i); //循环直至i为0,即数组首地址m_pfree_connections = next;            //设置空闲连接链表头指针,因为现在next指向c[0],注意现在整个链表都是空的m_free_connection_n = m_connection_n;  //空闲连接链表长度,因为现在整个链表都是空的,这两个长度相等;

ngx_get_connection()重要函数:从连接池中找空闲连接;注意在这段代码中 应该只是给 监听fd 一个空闲连接对象。

	for(pos = m_ListenSocketList.begin(); pos != m_ListenSocketList.end(); ++pos){c = ngx_get_connection((*pos)->fd); //从连接池中获取一个空闲连接对象

理论上,后面应该要给每一个connectfd也要给一个 空闲连接对象。

    //c)ngx_epoll_add_event() ************
    //    epoll_ctl();
    //d)ev.data.ptr = (void *)( (uintptr_t)c | c->instance);    把一个指针和一个位 合二为一,塞到一个void *中去,
         //后续能够把这两个值全部取出来,如何取,取出来干嘛,后续再说;

    //ps -eo pid,ppid,sid,tty,pgrp,comm,stat,cmd | grep -E 'bash|PID|nginx'

    //如下命令用root权限执行
    //sudo su       获得root权限
    //lsof -i:80    列出哪些进程在监听80端口
    //netstat -tunlp | grep 80

    //总结:
    //a)epoll_create();epoll_ctl();
    //b)连接池技巧ngx_get_connection(),ngx_free_connection();学习这种编程方法;
    //c)同时传递 一个指针和一个二进制数字技巧;

    //(3.2)ngx_epoll_init函数的调用(要在子进程中执行)
    //四章,四节 project1.cpp:nginx中创建worker子进程;
    //nginx中创建worker子进程
    //官方nginx ,一个master进程,创建了多个worker子进程;
    // master process ./nginx 
    // worker process
    //(i)ngx_master_process_cycle()        //创建子进程等一系列动作
    //(i)    ngx_setproctitle()            //设置进程标题    
    //(i)    ngx_start_worker_processes()  //创建worker子进程   
    //(i)        for (i = 0; i < threadnums; i++)   //master进程在走这个循环,来创建若干个子进程
    //(i)            ngx_spawn_process(i,"worker process");
    //(i)                pid = fork(); //分叉,从原来的一个master进程(一个叉),分成两个叉(原有的master进程,以及一个新fork()出来的worker进程
    //(i)                //只有子进程这个分叉才会执行ngx_worker_process_cycle()
    //(i)                ngx_worker_process_cycle(inum,pprocname);  //子进程分叉
    //(i)                    ngx_worker_process_init();
    //(i)                        sigemptyset(&set);  
    //(i)                        sigprocmask(SIG_SETMASK, &set, NULL); //允许接收所有信号
    //(i)                        g_socket.ngx_epoll_init();  //初始化epoll相关内容,同时 往监听socket上增加监听事件,从而开始让监听端口履行其职责
    //(i)                            m_epollhandle = epoll_create(m_worker_connections); 
    //(i)                            ngx_epoll_add_event((*pos)->fd....);
    //(i)                                epoll_ctl(m_epollhandle,eventtype,fd,&ev);
    //(i)                    ngx_setproctitle(pprocname);          //重新为子进程设置标题为worker process
    //(i)                    for ( ;; ) {}. ....                   //子进程开始在这里不断的死循环

    //(i)    sigemptyset(&set); 
    //(i)    for ( ;; ) {}.                //父进程[master进程]会一直在这里循环

上述,我们已经完成了epoll_init,对于 listenfd的 epoll_ctl

那么epoll_wait是什么时候呢?以及对于connectfd的 epoll_ctl以及对于 connectfd的epoll_wait在哪里呢?

ngx_process_events_and_timers(); 这个函数就是做epoll_wait的 //处理网络事件和定时器事件

    g_socket.ngx_epoll_process_events(-1); //-1表示卡着等待把。 -1表示阻塞等待,为什么要阻塞等待,这是 epoll模型决定的,多路IO复用,listenfd wait的时候是阻塞等待。

    //等待事件,事件会返回到m_events里,最多返回NGX_MAX_EVENTS个事件【因为我只提供了这些内存】;//阻塞timer这么长时间除非:a)阻塞时间到达 b)阻塞期间收到事件会立刻返回c)调用时有事件也会立刻返回d)如果来个信号,比如你用kill -1 pid测试//如果timer为-1则一直阻塞,如果timer为0则立即返回,即便没有任何事件//返回值:有错误发生返回-1,错误在errno中,比如你发个信号过来,就返回-1,错误信息是(4: Interrupted system call)//       如果你等待的是一段时间,并且超时了,则返回0;//       如果返回>0则表示成功捕获到这么多个事件【返回值里】int events = epoll_wait(m_epollhandle,m_events,NGX_MAX_EVENTS,timer);

如果是读事件,注意这里是的读事件,是listenfd的读事件,对应的是 客户端有连接过来了。

对应的回调函数是:   ngx_event_accept; 该设定在前面

        if(revents & EPOLLIN)  //如果是读事件{//一个客户端新连入,这个会成立//c->r_ready = 1;               //标记可以读;【从连接池拿出一个连接时这个连接的所有成员都是0】            (this->* (c->rhandler) )(c);    //注意括号的运用来正确设置优先级,防止编译出错;【如果是个新客户连入//如果新连接进入,这里执行的应该是CSocekt::ngx_event_accept(c)】            //如果是已经连入,发送数据到这里,则这里执行的应该是 CSocekt::ngx_wait_request_handler}

        c->rhandler = &CSocekt::ngx_event_accept;

void CSocekt::ngx_event_accept(lpngx_connection_t oldc) 函数调用:

通过accept4函数得到 connectfd。注意这里都是NON_BLOCK,取到值

        if(use_accept4){s = accept4(oldc->fd, &mysockaddr, &socklen, SOCK_NONBLOCK); //从内核获取一个用户端连接,最后一个参数SOCK_NONBLOCK表示返回一个非阻塞的socket,节省一次ioctl【设置为非阻塞】调用}else{s = accept(oldc->fd, &mysockaddr, &socklen);}

设置copnnectfd的 回调函数为 ngx_wait_request_handler函数

通过ngx_epoll_add_event,将 connectfd 加入红黑树。 这时候如果客户端有数据发送过来,就会调用 ngx_wait_request_handler 函数

        newc->rhandler = &CSocekt::ngx_wait_request_handler;  //设置数据来时的读处理函数,其实官方nginx中是ngx_http_wait_request_handler()//客户端应该主动发送第一次的数据,这里将读事件加入epoll监控if(ngx_epoll_add_event(s,                 //socket句柄1,0,              //读,写EPOLLET,          //其他补充标记【EPOLLET(高速模式,边缘触发ET)】EPOLL_CTL_ADD,    //事件类型【增加,还有删除/修改】                                    newc              //连接池中的连接) == -1)

也就是说,当客户端有数据发送过来的时候,会调用 ngx_wait_Request_handler函数。在这个函数中

1. 收包,这里就要涉及到一个问题,当我们

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/795396.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

深入解析Python的lxml库:高效处理XML和HTML的利器

更多Python学习内容&#xff1a;ipengtao.com Python中的lxml库是一个强大的XML和HTML处理库&#xff0c;它基于libxml2和libxslt库&#xff0c;提供了高效的XML解析和处理功能。本文将详细介绍lxml库的安装、特性、基本功能、高级功能、实际应用场景和总结&#xff0c;帮助读者…

phpstorm设置头部注释和自定义注释内容

先说设置位置&#xff1a; PhpStorm中文件、类、函数等注释的设置在&#xff1a;setting-》Editor-》FIle and Code Template-》Includes-》PHP Function Doc Comment下设置即可&#xff0c;其中方法的默认是这样的&#xff1a; /** ${PARAM_DOC} #if (${TYPE_HINT} ! "…

Linux第4课 Linux的基本操作

文章目录 Linux第4课 Linux的基本操作一、图形界面介绍二、终端界面介绍 Linux第4课 Linux的基本操作 一、图形界面介绍 本节以Ubuntu系统的GUI为例进行说明&#xff0c;Linux其他版本可自行网搜。 图形系统进入后&#xff0c;左侧黄框内为菜单栏&#xff0c;右侧为桌面&…

[HackMyVM]靶场Birthday

难度:Hard kali:192.168.56.104 靶机:192.168.56.149 端口扫描 ┌──(root㉿kali2)-[~/Desktop] └─# nmap 192.168.56.149 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-04-06 10:39 CST Nmap scan report for 192.168.56.149 Host is up (0.00016s latency). N…

硬件-1、体系架构

cpu 处理器 arm处理器的七种工作模式 arm寄存器 两张图是一样的&#xff0c;r0---r12是通用寄存器。其他寄存器可参考图一&#xff0c;cpu架构。 程序状态寄存器psr&#xff08;cpsr/spsr&#xff09; 程序异常处理 理解示例 当使用swi&#xff08;软中断指令&#xff09;指令…

【VMware Workstation】启动虚拟机报错“此主机支持 AMD-V,但 AMD-V 处于禁用状态”

问题出现步骤&#xff1a; 打开虚拟机&#xff1a; 然后报错&#xff1a; “此主机支持 AMD-V&#xff0c;但 AMD-V 处于禁用状态。 如果已在 BIOS/固件设置中禁用 AMD-V&#xff0c;或主机自更改此设置后从未重新启动&#xff0c;则 AMD-V 可能被禁用。 (1) 确认 BIOS/固件设…

机器学习KNN最邻近分类算法

文章目录 1、KNN算法简介2、KNN算法实现2.1、调用scikit-learn库中KNN算法 3、使用scikit-learn库生成数据集3.1、自定义函数划分数据集3.2、使用scikit-learn库划分数据集 4、使用scikit-learn库对鸢尾花数据集进行分类5、什么是超参数5.1、实现寻找超参数5.2、使用scikit-lea…

Zabbix6 - Centos7部署Grafana可视化图形监控系统配置手册手册

Zabbix6 - Centos7部署Grafana可视化图形监控系统配置手册手册 概述&#xff1a; Grafana是一个开源的数据可视化和监控平台。其特点&#xff1a; 1&#xff09;丰富的可视化显示插件&#xff0c;包括热图、折线图、饼图&#xff0c;表格等&#xff1b; 2&#xff09;支持多数据…

【MySQL】增删改查操作(基础)

文章目录 1、新增操作&#xff08;Create&#xff09;1.1单行数据全列插入1.2多行数据指定列插入 2、查询操作&#xff08;Retrieve&#xff09;2.1全列查询2.2指定列查询2.3指定列查询2.4别名&#xff08;as&#xff09;2.5去重&#xff08;distinct&#xff09;2.6排序&#…

机器学习实战18-机器学习中XGBClassifier分类器模型的应用实战,以及XGBClassifier分类器的调优策略

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下机器学习实战18-机器学习中XGBClassifier分类器模型的应用实战&#xff0c;以及XGBClassifier分类器的调优策略。XGBClassifier是基于eXtreme Gradient Boosting (XGBoost)算法的分类器模型&#xff0c;在机器学习领…

[Semi-笔记]Switching Temporary Teachers for Semi-Supervised Semantic Segmentation

目录 概要创新一&#xff1a;Dual Temporary Teacher挑战&#xff1a;解决&#xff1a; 创新二&#xff1a;Implicit Consistency Learning&#xff08;隐式一致性学习&#xff09;挑战&#xff1a;解决&#xff1a; 实验结果小结论文地址代码地址 分享一篇2023年NeurIPS的文章…

python 利用xpath 爬取一周天气

需求&#xff1a; 爬取 中国天气网指定城市一周的天气&#xff0c;以天津为例 实现&#xff1a; 1&#xff0c;先找到一周的数据位置。 divs html.xpath("//div[classhanml]") 2&#xff0c;再遍历每天。 trs div.xpath("./div/div[2]/table//tr[position…

PC发送指令给单片机控制LED(与上一篇文章相反)

此时要重新配置寄存器 &#xff0c;实现电脑往单片机传输数据 1、配置SCON寄存器的REN 即 REN 1 2、有TI&#xff08;发送中断&#xff09;就有RI&#xff08;接收中断&#xff09; 3、优化 发现发送 o 时&#xff0c;D5亮灯会有延迟 下面就是做到真正的无延迟的全双工通信 …

深入理解计算机系统 家庭作业 2.85

A 7111.01.11*V E2,M1.11,f0.11 位表示: exp:10000...001其中0有k-2个.frac:1100...000其中0有n-2个 B 有个默认条件就是E>n, En,M1.111...(小数部分n个1),f0.1111(n个1),V exp:111...11其中1有n-1个.frac:111...111其中1有n个 C有个默认条件就是没有符号位.最小的规格…

JS详解-设计模式

工厂模式&#xff1a; 单例模式&#xff1a; // 1、定义一个类class SingleTon{// 2、添加私有静态属性static #instance// 3、添加静态方法static getInstance(){// 4、判断实例是否存在if(!this.#instance){// 5、实例不存在&#xff0c;创建实例this.#instance new Single…

Android 关于apk反编译d2j-dex2jar classes.dex失败的几种方法

目录 确认路径正确直接定位到指定目录确定目录正确&#xff0c;按如下路径修改下面是未找到相关文件正确操作 确认路径正确 &#xff0c;即d2j-dex2jar和classes.dex是否都在一个文件夹里&#xff08;大部分的情况都是路径不正确&#xff09; 直接定位到指定目录 路径正确的…

第12届蓝桥杯省赛 ---- C/C++ C组

文章目录 1. ASC2. 空间3. 卡片4. 相乘5. 路径6.时间显示7.最少砝码8. 杨辉三角形9. 左孩子右兄弟 第12届蓝桥杯省赛&#xff0c;C/C C组真题&#xff0c;第10题不是很清楚&#xff0c;题解不敢乱放&#x1f601;&#x1f601;&#x1f601; 1. ASC 额。。。。 #include <i…

Java NIO Selector选择器源码分析

文章目录 前言Selector类结构Selector抽象类AbstractSelectorSelectorImplWindowsSelectorImpl三种SelectionKey集合 前言 Java NIO&#xff08;New I/O&#xff09;的Selector选择器是一个用于多路复用&#xff08;Multiplexing&#xff09;的I/O操作的关键组件。它允许一个单…

【题目】【网络系统管理】2021年全国职业院校技能大赛模块B--样题(九)

2021年全国职业院校技能大赛 网络系统管理&#xff08;样题9&#xff09;模块B&#xff1a;Windows环境 全国职业院校技能大赛执委会.技术专家组 2021年03月 竞赛简介 请认真阅读以下指引&#xff01; 比赛共4个小时&#xff0c;你必须自行决定如何分配你的时间。 当比赛结…

java-权限修饰符、代码块

一、权限修饰符概念 权限修饰符是用来控制一个成员被访问的范围&#xff0c;可以用来修饰成员变量、方法、构造方法、内部类 二、权限修饰符的分类 举例&#xff1a; 1、private 2、空着不写 3、protected 4、public 三、权限修饰符的使用规则 实际开发中&#xff0c;一般使…