9. C++通过epoll+fork的方式实现高性能网络服务器、线程和进程的关系 ...

打印 上一主题 下一主题

主题 503|帖子 503|积分 1509

epoll+fork 实现高性能网络服务器
一般在服务器上,CPU是多核的,上述epoll实现方式只使用了其中的一个核,造成了资源的大量浪费。因此我们可以将epoll和fork结合来实现更高性能的网络服务器。
创建子进程函数–fork( )
要了解线程我们先来了解fork()函数:fork() 函数的功能是在当前的进程创建一个子进程;在多核时代,CPU管理多个进程,一个单核CPU同一时间只能运行一个进程,好比8 核的 CPU 只能同时运行 8 个进程。但是一个进程中可以有多个线程并行运行。
线程

为什么要有线程


起首线程不是一开始就被提出来的技术概念!!而是由汗青的发展而来的,也就是说我们如今研究的是线程的动机是什么!
打个比喻就是一个引用步伐要做很多工作!如web欣赏器,又要显示图片,文字,视频的!
如果这三个动作是顺序执行的,也就是说,一个网页显示完图片再显示文字,再显示视频,那么很显着这对用户来说是体验非常不好的,这样对cpu的利用也不高!
那么此时,就引入了进程的概念!我们希望这些三个动作,也就是文字,图片,视频可以或许“同时”的显示在网页上,那么就是说这三个步伐需要并发或者并行(能并行那是由于有多个cpu)执行,此时,我们的网页就可以”同时“显示这三个内容!由于并发的进程是走走停停,瓜代执行,这个速度很快,快到我们人以为是同时举行的!此时,我们把这些可以或许同时执行的任务成为”执行流“,也就是说,在进程的概念中,执行流就是进程!,这里又文字,图片,视频三个执行流!很显着我们知道进程的创建和切换,也就是说并发执行是很耗时耗费资源的!
以是我们又提出了线程的概念,也就是说我们能否在一个进程中,执行这三执行流,其实可以的!
线程就是在一个进程中的一个执行流!有线程的概念我们就可以在一个进程执行这三个任务,不需要创建多个进程,而且举行进程切换!我们的线程在一个进程中,可以并发或者并行的执行!这样就大大镌汰了资源开销!
从内存块的角度明确线程




  • 好比一个单线程的进程,其实他就等价于一个进程中的任务!和进程区别不大!这个线程(执行流)共享进程的代码段,数据段,打开文件的信息等内容!同时进程的栈空间也是线程的栈空间!
  • 如果有多线程的进程,好比三个线程:分析:这个进程中有三个执行流,这个三个会有三个不同的空间,但是都属于一个进程中,它们有自己的栈空间,可以或许单独的执行自己的任务!但是这三个线程共享一个进程中的代码段,数据段,打开文件的信息等。
  • 共享带来的好处就是访问这些共享资源的代价低,存储资源节省!不再需要进程那样又要多一份空间存储资源!
线程就是cpu调度的单元了,而进程就是资源分配的单元了,由于纵然一个进程只有一个线程,真正执行的还是进程中的线程!
多线程模型

M:1模型
也就是多个用户线程对一个内核线程!

这种模型的好处就是,对于用户来说,它看的多个线程在并行执行!
在实际来说,多个线程占用一个内核线程,这个意思就是,用户线程中有一个线程占用了cpu资源,那么其他的用户线程就不可以执行,只能进入等候状态了!
1:1模型

一个用户线程对于一个内核线程,如果内核线程和用户线程数量不匹配的话,那么就会开多内核线程和用户线程匹配起来

好处就是多个线程真正意义上实现了并发或并行执行;
缺点就是:内核开销很大!
epoll+fork代码

这个代码就是每次fork一个进程,然后在每个线程内里可以用epoll申请多个进程来举行监听。
  1. #include <stdio.h>
  2. #include <unistd.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <sys/socket.h>
  6. #include <sys/epoll.h>
  7. #include <netinet/in.h>
  8. #include <iostream>
  9. #include <fcntl.h>
  10. #include <sys/types.h>
  11. #include <sys/wait.h>
  12. //端口
  13. #define PORT 8888
  14. #define MESSAGE_LEN 1024
  15. #define MAX_EVENTS 20
  16. #define TIMEOUT 500
  17. #define MAX_PROCESS 4
  18. int main(int argc,char* argv[]){
  19.     int ret=-1;
  20.     int on=1;
  21.     int backlog=10;//缓冲区大小
  22.     int socket_fd,accept_fd;
  23.     struct sockaddr_in localaddr,remoteaddr;
  24.     char in_buff[MESSAGE_LEN]={0,};
  25.     int epoll_fd;
  26.     struct epoll_event ev,events[MAX_EVENTS];//epoll中event的结构体
  27.     int event_number;
  28.     int flags = 1;
  29.     pid_t pid=-1;
  30.     socket_fd=socket(AF_INET,SOCK_STREAM,0);
  31.     if(socket_fd==-1){
  32.         std::cout<<"Failed to create socket!"<<std::endl;
  33.         exit(-1);
  34.     }
  35.     //创建了socket之后我们要设置成异步的
  36.     flags = fcntl(socket_fd,F_GETFL,0);
  37.     //然后设置成非阻塞
  38.     fcntl(socket_fd,F_SETFL,flags | O_NONBLOCK);
  39.     ret=setsockopt(socket_fd,SOL_SOCKET,SO_REUSEADDR,&on,sizeof(on));
  40.     if(ret==-1){
  41.         std::cout<<"Failed to set socket options!"<<std::endl;
  42.     }
  43.     localaddr.sin_family=AF_INET;//地址族
  44.     localaddr.sin_port=htons(PORT);//端口号
  45.     localaddr.sin_addr.s_addr=INADDR_ANY;//这个就是0
  46.     bzero(&(localaddr.sin_zero), 8);
  47.     ret= bind(socket_fd,(struct sockaddr *)&localaddr,sizeof(struct sockaddr));//绑定
  48.     if(ret==-1){//绑定失败
  49.         std::cout<<"Failed to bind addr!"<<std::endl;
  50.         exit(-1);
  51.     }
  52.     ret = listen(socket_fd,backlog);//第二个是缓冲区大小,因为同一时间只能处理一个,其他都放在缓冲区
  53.     if(ret==-1){
  54.         std::cout<<"failed to listen socket!"<<std::endl;
  55.         exit(-1);
  56.     }
  57.     for(int i=0;i<MAX_PROCESS;i++){//这个一般是cup数*2+1
  58.         if(pid!=0){//pid==0代表着子进程,第一次等于-1,就是父进程,然后fork一个子进程
  59.             pid=fork();//父进程,fork出来一个子进程
  60.         }
  61.     }
  62.     if(pid==0){
  63.         //创建epoll,再每个进程下面都可以创建epoll,每个进程自己使用自己的epoll
  64.         epoll_fd = epoll_create(256);
  65.         //先将侦听的socket_fd添加进去,然后再将与数据通讯的客户端的socket_fd添加进去
  66.         ev.events=EPOLLIN;//对于侦听的这个事件来说就是输入,就是in,这个一般不变成边缘触发,为了保证所有来的都能连上
  67.         ev.data.fd=socket_fd;//这个就是文件描述符socket
  68.         epoll_ctl(epoll_fd,EPOLL_CTL_ADD,socket_fd,&ev);
  69.         while(1){//等待连接
  70.             event_number = epoll_wait(epoll_fd,events,MAX_EVENTS,TIMEOUT);//发生事件的个数
  71.             for(int i=0;i<event_number;i++){//有多少个文件描述符发生事件了
  72.                 if(events[i].data.fd==socket_fd){//如果这个是侦听的socket发生事件了,那么说明是来了新的连接
  73.                     std::cout<<"listen event..."<<std::endl;
  74.                     socklen_t addr_len=sizeof(struct sockaddr);
  75.                     accept_fd = accept(socket_fd,
  76.                                        (struct sockaddr *)&remoteaddr,
  77.                                        &addr_len);
  78.                     //设置成非阻塞
  79.                     //创建了socket之后我们要设置成异步的
  80.                     flags = fcntl(accept_fd,F_GETFL,0);
  81.                     //然后设置成非阻塞
  82.                     fcntl(accept_fd,F_SETFL,flags | O_NONBLOCK);
  83.                     ev.events=EPOLLIN | EPOLLET;//|上边缘触发
  84.                     ev.data.fd=accept_fd;
  85.                     epoll_ctl(epoll_fd,EPOLL_CTL_ADD,accept_fd,&ev);//将accept_fd添加到epoll中去
  86.                 }else if(events[i].events&EPOLLIN){//这里只介绍读的
  87.                     do{
  88.                         memset(in_buff, 0, sizeof(in_buff));
  89.                         //接收消息
  90.                         ret = recv(events[i].data.fd,(void *)in_buff,MESSAGE_LEN,0);
  91.                         if(ret==0){
  92.                             close(events[i].data.fd);
  93.                         }
  94.                         if(ret==MESSAGE_LEN){//缓冲区满了
  95.                             std::cout<<"maybe have data..."<<std::endl;
  96.                         }
  97.                     }while(ret<-1&&errno==EINTR);
  98.                     if(ret<0){
  99.                         switch(errno){
  100.                             case EAGAIN:
  101.                                 break;
  102.                             dafault:
  103.                                 break;
  104.                         }
  105.                     }
  106.                     if(ret>0){//打印信息
  107.                         std::cout<<"receive messaage:"<<in_buff<<std::endl;
  108.                         //返回消息
  109.                         send(events[i].data.fd,(void*)in_buff,MESSAGE_LEN,0);
  110.                     }
  111.                 }
  112.             }
  113.         }
  114.     }else{//pid!=0,父进程
  115.         do{//这时候父进程等待所有的子进程完成
  116.             pid=waitpid(-1,NULL,0);
  117.         }while(pid!=-1);
  118.     }
  119.     std::cout<<"quit servet...\n"<<std::endl;
  120.     close(socket_fd);
  121.     return 0;
  122. }
复制代码
异步变乱的惊群现象

参考文献https://blog.csdn.net/m0_46606290/article/details/120939528

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

灌篮少年

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表