【网络编程】Web服务器shttpd源码剖析——线程池调度

news2024/12/23 14:35:59

 

hello !大家好呀! 欢迎大家来到我的网络编程系列之web服务器shttpd源码剖析——线程池调度,在这篇文章中,你将会学习到在Linux内核中如何创建一个自己的并发服务器shttpd,并且我会给出源码进行剖析,以及手绘UML图来帮助大家来理解,希望能让大家更能了解网络编程技术!!!

希望这篇文章能对你有所帮助,大家要是觉得我写的不错的话,那就点点免费的小爱心吧!

               

目录

​一.多线程池作用

​1.1 SHTTPD的多客户支持需求

​1.2 SHTTPD多线程池的实现

​二.多线程状态及其函数

 ​2.1 多线程状态

 ​2.2 多线程创建以及销毁


一.多线程池作用

1.1 SHTTPD的多客户支持需求

SHTTPD支持多个客户端的并发连接,在同一时刻允许多个客户同时成功获得服务器上的网络资源,这是现代服务器的基本属性,SHTTPD启动的时候处理单元初始化了多个线程,当客户增加超过上限时候,会根据现场情况增加处理单元。

 如图:

 当超过四个客户端并发访问时,SHTTPD会将后来的请求放在队列排序中,当处理单元空闲时再响应其他请求。

1.2 SHTTPD多线程池的实现

服务器SHTTPD的多客户端支持模块为此程序的主处理模块。在此模块中进行客户端连接的处理、请求数据的接收、响应数据的发送和服务线程的调度。模块的核心部分采用线程池的服务器模型,如图:

 模块初始化的时候,建立线程池,其中的线程负责接收客户端的请求、解析数据并响应数据。当客户端请求到来的时候,主线程查看当前线程池中是否有空闲的工作线程,当没有工作线程的时候会建立新的工作线程,然后分配任务给空闲的线程。

工作线程轮询接收客户端的请求数据,进行请求数据分析并响应请求,处理完毕后,关闭客户端的连接,等待主线程分发下一个请求。

 多客户端模块的线程处理框架如图所示:

主要分为两个部分:线程调度部分和线程退出部分。线程调度部分负责线程初始化、线程的增减、线程的销毁及线程互斥区的保护。线程退出部分则发送信号给工作线程,使得工作线程能够及时地释放资源。这主要应用于接收到用户的信号 SIGINT 时调用。
 

二.多线程状态及其函数

 2.1 多线程状态

工作线程分为多个状态:初始化状态 , 线程空闲状态, 线程运行状态 , 线程退出中状态和线程退出完毕状态:

线程建立的时候状态为线程初始化状态,此时工作线程不可以接受主线程的任务,刚刚进入线程函数。

线程建立完毕的时候进入线程空闲状态,此时可以接受主线程分配的任务,处理客户端的请求,并进行响应。

线程运行状态为线程正在处理客户端请求的时机,可以由空闲状态转入,转入的条件为主线程分配给此线程一个任务。

在处理完客户端请求时,线程可以转入空闲状态,等待下一次客户端请求任务的分配。

线程退出中的状态主要由接收到SIGINT 信号后调用线程退出函数时引起,此时各个线程在非阻塞的时候会及时响应此状态,释放资源、关闭连接。进入线程退出。

进入退出状态后各个线程都释放完申请的动态资源。

 2.2 多线程创建以及销毁

线程控制管理结构:

struct worker_opts{
    pthread_t th;            //线程的ID号
    int flags;                //线程状态
    pthread_mutex_t mutex;//线程任务互斥
    struct worker_ctl *work;//本线程的总控结构
};

之后使用线程初始化函数,将所有线程初始化:

void Worker_Init()
{
    DBGPRINT("LCW==>Worker_Init");
    int i = 0;
    //初始化总控参数
    wctls = (struct worker_ctl*)malloc( sizeof(struct worker_ctl)*conf_para.MaxClient);//开辟空间
    memset(wctls,0, sizeof(*wctls)*conf_para.MaxClient);//清零
    //初始化一些参数
    for(i=0;i<conf_para.MaxClient;i++)
    {
        //opt&connn结构和worker_ctl结构形成回指针
        wctls[i].opts.work = &wctls[i];
        wctls[i].conn.work = &wctls[i];
        //opts结构部分的初始化
        wctls[i].opts.flags = WORKER_DETACHED;
        //wctls[i].opts.mutex = PTHREAD_MUTEX_INITIALIZER;
        pthread_mutex_init(&wctls[i].opts.mutex,NULL);//初始化互斥锁
        pthread_mutex_lock(&wctls[i].opts.mutex);
        //conn部分的初始化
        //con_req&con_res与conn结构形成回指
        wctls[i].conn.con_req.conn = &wctls[i].conn;
        wctls[i].conn.con_res.conn = &wctls[i].conn;
        wctls[i].conn.cs = -1;//客户端socket连接为空
        //conn.con_req部分初始化:请求结构
        wctls[i].conn.con_req.req.ptr = wctls[i].conn.dreq;
        wctls[i].conn.con_req.head = wctls[i].conn.dreq;
        wctls[i].conn.con_req.uri = wctls[i].conn.dreq;
        //conn.con_res部分初始化:响应结构
        wctls[i].conn.con_res.fd = -1; 
        wctls[i].conn.con_res.res.ptr = wctls[i].conn.dres;

    }    
    for (i = 0; i < conf_para.InitClient;i++)
    {
        //增加规定个数工作线程
        Worker_Add(i);
    }
    DBGPRINT("LCW<==Worker_Init\n");
}

 然后使用调度函数对空闲线程进行调度使用:

******************************************************
函数名:worker(void *arg)
参数:worker_ctl *wctls
功能:线程处理函数
*******************************************************/
static void* worker(void *arg)
{
    DBGPRINT("LCW==>worker\n");
    struct worker_ctl *ctl = (struct worker_ctl *)arg;//为何不直接传这个类型过来?
    struct worker_opts *self_opts = &ctl->opts;//定义一个选项结构
    pthread_mutex_unlock(&thread_init);//解锁互斥
    self_opts->flags = WORKER_IDEL;//初始化线程为空闲,等待任务
    //如果主控线程没有让此线程退出,则循环处理任务
    for(;self_opts->flags != WORKER_DETACHING;)//while(self_opts->flags != WORKER_DETACHING)
    {
        //DBGPRINT("work:%d,status:%d\n",(int)self_opts->th,self_opts->flags );
        //查看是否有任务分配
        int err = pthread_mutex_trylock(&self_opts->mutex);//互斥预锁定
        //pthread_mutex_trylock()是pthread_mutex_lock() 的非阻塞版本
        if(err)
        {
            //DBGPRINT("NOT LOCK\n");
            sleep(1);
            continue;
        }
        else
        {
            //有任务,do it
            DBGPRINT("Do task\n");
            self_opts->flags = WORKER_RUNNING;//执行标志
            do_work(ctl);
            close(ctl->conn.cs);//关闭套接字
            ctl->conn.cs = -1;
            if(self_opts->flags == WORKER_DETACHING)
                break;
            else
                self_opts->flags = WORKER_IDEL;
        }
    }
    //主控发送退出命令
    //设置状态为已卸载
    self_opts->flags = WORKER_DETACHED;
    workersnum--;//工作线程-1

    DBGPRINT("LCW<==worker\n");
    return NULL;
}
/******************************************************
函数名:WORKER_ISSTATUS(int status)
参数:欲查询的线程状态
功能:查询线程状态
*******************************************************/
int WORKER_ISSTATUS(int status)
{
    int i = 0;
    for(i = 0; i<conf_para.MaxClient;i++)
    {
        if(wctls[i].opts.flags == status)
            return i;//返回符合的线程
    }
    return -1;//没有符合的线程状态
}

 最后使用线程销毁函数,收回资源:

/******************************************************
函数名:Worker_Destory()
参数:
功能:销毁线程
*******************************************************/
void Worker_Destory()
{
    DBGPRINT("LCW==>Worker_Destory\n");
    int i = 0;
    int clean = 0;

    for(i=0;i<conf_para.MaxClient;i++)
    {
        DBGPRINT("thread %d,status %d\n",i,wctls[i].opts.flags );
        if(wctls[i].opts.flags != WORKER_DETACHED)//如果状态不是已经卸载
            Worker_Delete(i);
    }

    while(!clean)
    {
        clean = 1;
        for(i = 0; i<conf_para.MaxClient;i++)
        {
            DBGPRINT("thread %d,status %d\n",i,wctls[i].opts.flags );
            if(wctls[i].opts.flags == WORKER_RUNNING || wctls[i].opts.flags == WORKER_DETACHING)
                clean = 0;
        }
        if(!clean)
            sleep(1);
    }
    DBGPRINT("LCW<==Worker_Destory\n");
}

当然,这只是一部分源码,但这些是最主要的线程池框架,许多小的细节函数大家可以自己实现或者查资料,找我也是可以的哦!(欢迎私信!!!) 

   好啦!到这里这篇文章就结束啦,关于实例代码中我写了很多注释,如果大家还有不懂得,可以评论区或者私信我都可以哦!! 感谢大家的阅读,我还会持续创造网络编程相关内容的,记得点点小爱心和关注哟!   

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

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

相关文章

Nodejs 第六十四章(SSO单点登录)

单点登录 单点登录&#xff08;Single Sign-On&#xff0c;简称SSO&#xff09;是一种身份认证和访问控制的机制&#xff0c;允许用户使用一组凭据&#xff08;如用户名和密码&#xff09;登录到多个应用程序或系统&#xff0c;而无需为每个应用程序单独提供凭据 SSO的主要优…

EI、Scopus检索 | 2024年第四届机械、航空航天与汽车工程国际会议

会议简介 Brief Introduction 2024年第四届机械、航空航天与汽车工程国际会议&#xff08;CMAAE 2024&#xff09; 会议时间&#xff1a;2024年11月8 -10日 召开地点&#xff1a;中国北京 大会官网&#xff1a;www.cmaae.org 航空航天是当今世界最具挑战性和广泛带动性的高技术…

HCIP的学习(10)

OSPF不规则区域划分 区域划分 非骨干与骨干区域直接相连骨干区域唯一 限制规则&#xff1a; 非骨干区域之间不允许直接相互发布区域间路由信息OSPF区域水平分割&#xff1a;从非骨干区域收到的路由信息&#xff0c;ABR设备能接收到不能使用&#xff08;从某区域传出的路由&…

Tomcat漏洞利用工具-TomcatVuln

检测漏洞清单 CVE-2017-12615 PUT文件上传漏洞 tomcat-pass-getshell 弱认证部署war包 弱口令爆破 CVE-2020-1938 Tomcat 文件读取/包含项目地址 https://github.com/errors11/TomcatVuln TomcatVuln put文件上传 ajp协议漏洞 默认读取web.xml文件&#xff0c;漏洞利用…

使用yolov5训练自己的目标检测模型

使用yolov5训练自己的目标检测模型 使用yolov5训练自己的目标检测模型1. 项目的克隆2. 项目代码结构3. 环境的安装和依赖的安装4. 数据集和预训练权重的准备4.1利用labelimg标注数据和数据的准备4.1.1 **labelimg介绍:**4.1. 2 labelimg的安装 4.2 使用labelimg4.2.1 数据准备4…

【EI、CPCI稳定检索】2024年现代化教育、知识和信息管理国际学术会议(ICMEKIM 2024)

2024 International Conference on Modern Education, Knowledge and Information Management (ICMEKIM 2024) ●会议简介 2024年现代教育、知识与信息管理国际学术会议将聚焦于教育的最新趋势和信息管理技术的创新发展。本次会议将汇集全球教育专家、学者及行业领袖&#xf…

Vue2 基础学习-案例实践

数据管理信息的增删改查的实践 主要应用&#xff1a; 数据插值&#xff1a; {{xxx}}双向绑定&#xff1a;v-model点击事件函数&#xff1a;click列表xxx的增删改实现 xxx.push(row) 增加xxx.splice(id,1) 删除 一行{x,y} xxx[id]; 编辑 <!DOCTYPE html> <html la…

4月18日N皇后+解数独

51.N皇后 按照国际象棋的规则&#xff0c;皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。 n 皇后问题 研究的是如何将 n 个皇后放置在 nn 的棋盘上&#xff0c;并且使皇后彼此之间不能相互攻击。 给你一个整数 n &#xff0c;返回所有不同的 n 皇后问题 的解决方案…

LangChain入门:18.使用ReAct 框架进行生成推理痕迹和任务特定行动来实现更大的协同作用

在这篇技术博文中&#xff0c;我们将深入探讨LangChain框架中的ReAct对话模型&#xff0c;以及如何利用它构建高效的智能对话系统。ReAct模型通过反应堆&#xff08;Reactor&#xff09;处理对话中的各种情况&#xff0c;实现了对复杂对话场景的有效解构。结合思维链&#xff0…

基于Java+Vue的校园交友系统(源码+文档+包运行)

一.系统概述 选题背景&#xff1a; 在大学校园中&#xff0c;学生们面临着新的环境和人际关系的挑战。有些学生可能感到孤独或者希望扩展自己的社交圈子&#xff0c;寻找志同道合的朋友或者潜在的伴侣。因此&#xff0c;设计一款校园交友平台具有重要意义。 研究意义&#xff1…

「51媒体」权重高新闻源央级媒体邀约资料有哪些?

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 权重高的央级媒体邀约资源包括了中国一些最具影响力和权威性的新闻机构。具体如下&#xff1a; 人民日报&#xff1a;作为中国共产党中央委员会的机关报&#xff0c;人民日报具有极高的权…

Spring学习(三)——AOP

AOP是在不改原有代码的前提下对其进行增强 AOP(Aspect Oriented Programming)面向切面编程&#xff0c;在不惊动原始设计的基础上为其进行功能增强&#xff0c;前面咱们有技术就可以实现这样的功能即代理模式。Java设计模式——代理模式-CSDN博客 基础概念 连接点&#xff08…

SAM5716B 法国追梦DREAM 音频DSP芯片

法国追梦/DERAM SAM5504/5704/5716/5808音频DSP芯片,开发板&#xff0c;方案 可用于电子鼓、电子琴、电吉他、效果器、均衡器、啸叫抑制器等电声产品领域 全系列芯片&#xff1a; SAM2634 SAM2695 SAM5504B SAM5704B SAM5708B SAM5808B SAM5716B SAM5916B... 原厂开发…

【Qt】Qt Hello World 程序

文章目录 1、Qt Hello World 程序1.1 使用按钮实现1.1.1 使用可视化方式实现 1.1.2 纯代码方式实现 label创建堆&#xff08;内存泄漏&#xff09;或者栈问题Qt基础类&#xff08;Qstring、Qvector、Qlist&#xff09;乱码问题零散知识 1、Qt Hello World 程序 1.1 使用按钮实…

算法学习笔记:Bi-LSTM和Bi-GRU

这篇文章的作为前几篇RNN\LSTM\RNN的后续之作&#xff0c;主要就是补充一个这两个哥的变体&#xff0c;想详细了解RNN\LSTM\GRU的详细理论和公式推导以及代码的请前往下面链接&#xff1a; 算法学习笔记&#xff1a;循环神经网络&#xff08;Recurrent Neural Network)-CSDN博…

udemy视频教程下载:AI和ChatGPT提示工程精通指南

欢迎来到 ChatGPT 大师班&#xff01; 这个 ChatGPT 大师班&#xff1a;AI 和提示工程指南是您通往 AI 未来的全通道通行证。 以下是您的学习旅程&#xff1a; 理解和掌握 ChatGPT&#xff1a;您将深入了解 AI 和语言模型&#xff0c;重点是 ChatGPT。我们设计了这个部分&am…

前端三大件速成 01 HTML

文章目录 一、前端基础知识二、标签1、什么是标签2、标签的属性3、常用标签&#xff08;1&#xff09;声明&#xff08;2&#xff09;注释&#xff08;3&#xff09;html 根标签&#xff08;3&#xff09;head标签&#xff08;4&#xff09;body标签 三、特殊字符四、其他标签1…

java方法递归

简介 案例&#xff1a;阶乘 // 计算一个数的阶乘 public static int factorial(int n) {if (n 1) {return 1;}return n * factorial(n - 1); }案例 猴子吃桃子 // 猴子吃桃子问题 // 第一天吃了一半多一个 第十天剩一个 求第一天有多少个桃子 // 因为 f(x1) f(x)/2 - 1 // 所…

STL库 —— priority_queue 的编写

目录 一、 优先级队列的介绍 二、优先级队列的使用 2.1 建大堆 less 2.2 建小堆 greater 2.3 详解 greater 与 less 三、 priority_queue 的模拟实现 3.1 编写框架 3.2 编写简单函数 3.2 进堆 向上调整 3.3 出堆 向下调整 四、完整代码 一、 优先级队列的介绍 1.…

【Python系列】非异步方法调用异步方法

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…