zoukankan      html  css  js  c++  java
  • Linux高性能服务器编程:高性能服务器程序框架

    服务器有三个主要模块:

    (1)I/O处理单元

    (2)逻辑单元

    (3)存储单元

    1.服务器模型

    C/S模型

    逻辑:服务器启动后,首先创建一个或多个监听socket,并调用bind函数将其绑定到服务器感兴趣的端口上,然后调用listen函数等待客户连接。

    服务器运行稳定后,客户端就可以调用connect函数向服务器发起连接了。

    P2P模型

    P2P模型使得每台机器在消耗服务的同时也给别人提供服务,这样资源能够充分、自由的共享。

    2.服务器编程框架

    包含:

    I/O处理单元  请求队列   逻辑单元   请求队列  网络存储单元

    I/O处理单元:处理客户连接,读写网络数据

    逻辑单元:业务进程或线程

    网络存储单元:本地数据库、文件或缓存

    请求队列:各单元之间的通信方式

    3. I/O模型

    阻塞I/O执行的系统调用可能因为无法立即完成而被操作系统挂起,知道等待的事件发生为止。可能阻塞的系统调用包括:accept,send,recv和connect。

    非阻塞I/O执行的系统调用则总是立即返回,而不管时间是否发生。如果事件没有立即发生,这些系统调用就返回-1,和出错的情况一样。必须根据errno来区分这两种情况。

    对accept,send和recv而言,事件未发生时errno通常被设置成EAGAIN或者EWOULDBLOCK:对connect而言,errno则被设置成EINPROGRESS。

    显然,只有在事件已经发生的情况下操作非阻塞I/O,才能提高程序的效率。I/O通知机制有:I/O复用和SIGIO信号。

    I/O复用:应用程序通过I/O复用函数向内核注册一组事件,内核通过I/O复用函数把其中就绪的事件通知给应用程序。Linux上常用I/O复用函数是selcet、poll和epoll_wait。I/O复用本身是阻塞的,能提高效率的原因是

    它们具有同时监听多个I/O事件的能力。

    SIGIO信号:可以为一个目标文件描述符指定宿主进程,那么被指定的宿主进程将捕获到SIGIO信号。这样,当目标文件描述符上有事件发生时,SIGIO信号的信号处理函数将被触发,可以在该信号处理函数中对目标文件描述符执行非阻塞I/O操作了。

    理论上说,阻塞I/O,I/O复用和信号驱动I/O都是I/O同步模型。在这三种模型中,I/O的读写操作,都是I/O事件发生之后,由应用程序来完成的。

    对异步I/O而言,用户可以直接对I/O执行读写操作,这些操作告诉内核用户读写缓冲区的位置,以及I/O操作完成之后内核通知应用程序的方式,异步I/O的读写操作总是立即返回,而不论I/O是否是阻塞的,因为真正的读写操作已经由内核接管。

    也就是说,同步I/O模型要求用户代码自行执行I/O操作(将数据从内核缓冲区读入用户缓冲区,或将数据从用户缓冲区写入内核缓冲区),而异步I/O机制则由内核来执行I/O操作(数据在内核缓冲区和用户缓冲区之间的移动是由内核在“后台”完成的)。

    同步I/O向应用程序通知的是I/O就绪事件,而异步I/O向应用程序通知的是I/O完成事件。

    4.两种高效的事件处理模式

    使用同步I/O模型实现Reactor模式:

    (1)主线程往epoll内核事件表中 注册socket上的读就绪事件。

    (2)主线程调用epoll_wait等待socket上有数据可读。

    (3)当socket上有数据可读时,epoll_wait通知主线程,主线程将socket事件放入请求队列中。

    (4)睡眠在请求队列上的某个工作线程被唤醒,从socket读取数据,并处理客户请求,然后往epoll内核事件表中注册该事件的写就绪事件。

    (5)主线程调用epoll_wait等待socket可写。

    (6)当socket可写时,epoll_wait通知主线程。主线程将socket可写事件放入请求队列。

    (7)睡眠在请求队列上的某个工作线程被唤醒,它往socket上写入服务器处理客户请求的结果。

    Proactor模式

    Proactor模式将所有I/O操作都交给主线程和内核来处理,工作线程仅仅负责业务逻辑。

    半同步/半异步:

    同步线程用于处理客户逻辑,异步线程用于处理I/O事件。

    异步线程由主线程充当。负责监听所有socket上的事件。若监听socket上有可读事件发生,即有新的连接到来,主线程就接受之以得到新的连接socket,然后往epoll内核事件表中注册该socket上的读写事件。

    如果连接socket上有读写事件发生,即有新的客户请求到来或有数据要发送到客户端,主线程就将该连接socket插入请求队列中。所有工作线程都睡眠在请求队列上,当有任务带来时,将通过竞争获得任务的接管权。

    缺点:

    主线程和工作线程共享请求队列。主线程往请求队列中添加任务,或者工作线程从请求队列中取出任务,都需要对请求队列加锁保护,从而拜拜浪费CPU时间。

    每个工作线程在同一时间只能处理一个客户请求。

  • 相关阅读:
    数据结构(三)栈与递归
    机器学习(二)------ 回归
    数据结构(二)线性表
    数据结构(一)数据结构基础
    机器学习 (一)------分类
    操作系统概述
    NumPy函数库基础
    总线与输入输出子系统
    FT VIEW SE高效开发之全局对象
    STUDIO 5000 V32新CRACK和新功能
  • 原文地址:https://www.cnblogs.com/sssblog/p/12376464.html
Copyright © 2011-2022 走看看