当前位置:   article > 正文

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

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

 

 16b9d0dfc990426e968798e2f5a7628b.png

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

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

03d6d5d7168e4ccb946ff0532d6eb8b9.gif               

目录

​一.多线程池作用

​1.1 SHTTPD的多客户支持需求

​1.2 SHTTPD多线程池的实现

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

 ​2.1 多线程状态

 ​2.2 多线程创建以及销毁


 

 

16b9d0dfc990426e968798e2f5a7628b.png一.多线程池作用

502d327b2b174a5c905179353b0e7b14.png1.1 SHTTPD的多客户支持需求

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

 如图:

d4fd9e6f00444f0f84abf217a69249c4.jpeg

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

 

502d327b2b174a5c905179353b0e7b14.png1.2 SHTTPD多线程池的实现

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

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

 

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

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

ae80827225a849e38806d74139095d07.jpeg

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

16b9d0dfc990426e968798e2f5a7628b.png二.多线程状态及其函数

 502d327b2b174a5c905179353b0e7b14.png2.1 多线程状态

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

ba6c9166be2d40cdbc6889f9b5c61259.jpeg

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

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

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

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

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

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

 502d327b2b174a5c905179353b0e7b14.png2.2 多线程创建以及销毁

线程控制管理结构:

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

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

  1. void Worker_Init()
  2. {
  3. DBGPRINT("LCW==>Worker_Init");
  4. int i = 0;
  5. //初始化总控参数
  6. wctls = (struct worker_ctl*)malloc( sizeof(struct worker_ctl)*conf_para.MaxClient);//开辟空间
  7. memset(wctls,0, sizeof(*wctls)*conf_para.MaxClient);//清零
  8. //初始化一些参数
  9. for(i=0;i<conf_para.MaxClient;i++)
  10. {
  11. //opt&connn结构和worker_ctl结构形成回指针
  12. wctls[i].opts.work = &wctls[i];
  13. wctls[i].conn.work = &wctls[i];
  14. //opts结构部分的初始化
  15. wctls[i].opts.flags = WORKER_DETACHED;
  16. //wctls[i].opts.mutex = PTHREAD_MUTEX_INITIALIZER;
  17. pthread_mutex_init(&wctls[i].opts.mutex,NULL);//初始化互斥锁
  18. pthread_mutex_lock(&wctls[i].opts.mutex);
  19. //conn部分的初始化
  20. //con_req&con_res与conn结构形成回指
  21. wctls[i].conn.con_req.conn = &wctls[i].conn;
  22. wctls[i].conn.con_res.conn = &wctls[i].conn;
  23. wctls[i].conn.cs = -1;//客户端socket连接为空
  24. //conn.con_req部分初始化:请求结构
  25. wctls[i].conn.con_req.req.ptr = wctls[i].conn.dreq;
  26. wctls[i].conn.con_req.head = wctls[i].conn.dreq;
  27. wctls[i].conn.con_req.uri = wctls[i].conn.dreq;
  28. //conn.con_res部分初始化:响应结构
  29. wctls[i].conn.con_res.fd = -1;
  30. wctls[i].conn.con_res.res.ptr = wctls[i].conn.dres;
  31. }
  32. for (i = 0; i < conf_para.InitClient;i++)
  33. {
  34. //增加规定个数工作线程
  35. Worker_Add(i);
  36. }
  37. DBGPRINT("LCW<==Worker_Init\n");
  38. }

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

  1. ******************************************************
  2. 函数名:worker(void *arg)
  3. 参数:worker_ctl *wctls
  4. 功能:线程处理函数
  5. *******************************************************/
  6. static void* worker(void *arg)
  7. {
  8. DBGPRINT("LCW==>worker\n");
  9. struct worker_ctl *ctl = (struct worker_ctl *)arg;//为何不直接传这个类型过来?
  10. struct worker_opts *self_opts = &ctl->opts;//定义一个选项结构
  11. pthread_mutex_unlock(&thread_init);//解锁互斥
  12. self_opts->flags = WORKER_IDEL;//初始化线程为空闲,等待任务
  13. //如果主控线程没有让此线程退出,则循环处理任务
  14. for(;self_opts->flags != WORKER_DETACHING;)//while(self_opts->flags != WORKER_DETACHING)
  15. {
  16. //DBGPRINT("work:%d,status:%d\n",(int)self_opts->th,self_opts->flags );
  17. //查看是否有任务分配
  18. int err = pthread_mutex_trylock(&self_opts->mutex);//互斥预锁定
  19. //pthread_mutex_trylock()是pthread_mutex_lock() 的非阻塞版本
  20. if(err)
  21. {
  22. //DBGPRINT("NOT LOCK\n");
  23. sleep(1);
  24. continue;
  25. }
  26. else
  27. {
  28. //有任务,do it
  29. DBGPRINT("Do task\n");
  30. self_opts->flags = WORKER_RUNNING;//执行标志
  31. do_work(ctl);
  32. close(ctl->conn.cs);//关闭套接字
  33. ctl->conn.cs = -1;
  34. if(self_opts->flags == WORKER_DETACHING)
  35. break;
  36. else
  37. self_opts->flags = WORKER_IDEL;
  38. }
  39. }
  40. //主控发送退出命令
  41. //设置状态为已卸载
  42. self_opts->flags = WORKER_DETACHED;
  43. workersnum--;//工作线程-1
  44. DBGPRINT("LCW<==worker\n");
  45. return NULL;
  46. }
  47. /******************************************************
  48. 函数名:WORKER_ISSTATUS(int status)
  49. 参数:欲查询的线程状态
  50. 功能:查询线程状态
  51. *******************************************************/
  52. int WORKER_ISSTATUS(int status)
  53. {
  54. int i = 0;
  55. for(i = 0; i<conf_para.MaxClient;i++)
  56. {
  57. if(wctls[i].opts.flags == status)
  58. return i;//返回符合的线程
  59. }
  60. return -1;//没有符合的线程状态
  61. }

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

  1. /******************************************************
  2. 函数名:Worker_Destory()
  3. 参数:
  4. 功能:销毁线程
  5. *******************************************************/
  6. void Worker_Destory()
  7. {
  8. DBGPRINT("LCW==>Worker_Destory\n");
  9. int i = 0;
  10. int clean = 0;
  11. for(i=0;i<conf_para.MaxClient;i++)
  12. {
  13. DBGPRINT("thread %d,status %d\n",i,wctls[i].opts.flags );
  14. if(wctls[i].opts.flags != WORKER_DETACHED)//如果状态不是已经卸载
  15. Worker_Delete(i);
  16. }
  17. while(!clean)
  18. {
  19. clean = 1;
  20. for(i = 0; i<conf_para.MaxClient;i++)
  21. {
  22. DBGPRINT("thread %d,status %d\n",i,wctls[i].opts.flags );
  23. if(wctls[i].opts.flags == WORKER_RUNNING || wctls[i].opts.flags == WORKER_DETACHING)
  24. clean = 0;
  25. }
  26. if(!clean)
  27. sleep(1);
  28. }
  29. DBGPRINT("LCW<==Worker_Destory\n");
  30. }

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

 

 

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

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/繁依Fanyi0/article/detail/522891
推荐阅读
相关标签
  

闽ICP备14008679号