zoukankan      html  css  js  c++  java
  • [openssl] openssl async模块框架分析

    [classic_tong: https://www.cnblogs.com/hugetong/p/14379347.html]

    这一部分内容的前面还有个第一部分,可以作为接下来内容的前提或者背景进行阅读:[openssl] intel qat场景下的openssl框架

    引言

    openssl1.1.1版本新增了async功能。是为了更好支持qat卡新增的功能。

    详见:[openssl] intel qat场景下的openssl框架  有关性能部分的讨论。

    API

    回顾前文我们讨论了openssl的基本框架,包含:app,ssl,crypto,engines四部分。

    async模块是crypto component中的一部分。它目前仅被ssl模块引用,被封装在SSL的api调用下面

    sync模块的主要API:

    /* 提供给应用程序的接口, */
    int
    ASYNC_init_thread(size_t max_size, size_t init_size); void ASYNC_cleanup_thread(void); int ASYNC_start_job(ASYNC_JOB **job, ASYNC_WAIT_CTX *ctx, int *ret, int (*func)(void *), void *args, size_t size);
    /* 提供给应用程序用于epoll的接口。 */ ASYNC_WAIT_CTX
    *ASYNC_get_wait_ctx(ASYNC_JOB *job); int ASYNC_WAIT_CTX_get_fd(ASYNC_WAIT_CTX *ctx, const void *key, OSSL_ASYNC_FD *fd, void **custom_data); int ASYNC_WAIT_CTX_get_all_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *fd, size_t *numfds); int ASYNC_WAIT_CTX_get_changed_fds(ASYNC_WAIT_CTX *ctx, OSSL_ASYNC_FD *addfd, size_t *numaddfds, OSSL_ASYNC_FD *delfd, size_t *numdelfds); /* 对ENGINE的接口,在ENGINE中调用 */ ASYNC_WAIT_CTX *ASYNC_WAIT_CTX_new(void); void ASYNC_WAIT_CTX_free(ASYNC_WAIT_CTX *ctx); int ASYNC_WAIT_CTX_set_wait_fd(ASYNC_WAIT_CTX *ctx, const void *key, OSSL_ASYNC_FD fd, void *custom_data, void (*cleanup)(ASYNC_WAIT_CTX *, const void *, OSSL_ASYNC_FD, void *));
    int ASYNC_pause_job(void);

    SSL模块的主要API

    /* SSL的基本IO接口 */
    SSL *SSL_new(SSL_CTX *ctx); void SSL_free(SSL *ssl);
    int SSL_accept(SSL *ssl);
    int SSL_connect(SSL *ssl);
    int SSL_read(SSL *ssl, void *buf, int num);
    int SSL_peek(SSL *ssl, void *buf, int num);
    int SSL_write(SSL *ssl, const void *buf, int num);

    /* SSL 用于异步处理的 socket fd */
    int SSL_get_fd(const SSL *s);

    /* SSL 用于异步处理的加解密设备 fd */
    int SSL_waiting_for_async(SSL *s);
    int SSL_get_all_async_fds(SSL *s, OSSL_ASYNC_FD *fds, size_t *numfds);
    int SSL_get_changed_async_fds(SSL *s, OSSL_ASYNC_FD *addfd,
    size_t *numaddfds, OSSL_ASYNC_FD *delfd, size_t *numdelfds);

    ENGINE注册进openssl的接口

    ENGINE_set_id(e, engine_qat_id)
    ENGINE_set_name(e, engine_qat_name)
    ENGINE_set_RSA(e, qat_get_RSA_methods())
    ENGINE_set_DSA(e, qat_get_DSA_methods())
    ENGINE_set_DH(e, qat_get_DH_methods())
    ENGINE_set_EC(e, qat_get_EC_methods())
    ENGINE_set_pkey_meths(e, qat_pkey_methods)
    ENGINE_set_RSA(e, multibuff_get_RSA_methods())
    ENGINE_set_pkey_meths(e, multibuff_x25519_pkey_methods)
    ENGINE_set_ciphers(e, qat_ciphers)

    上下文切换

    async为了达到并行的目的,在单线程中实现了协程。请读:[openssl] 协程

    async模块中,将其抽象为job,job为一次运行即结束的。多个job同时运行,使用协程进行调度。

    job有个pool,这个pool是堆内存,地址存储在每线程的全局变量里,使用api pthread_getspecfic()

    核心的协程切换在这个函数里:https://github.com/openssl/openssl/blob/master/crypto/async/arch/async_posix.h::async_fibre_swapcontext()

    封装

    这一小节讲,上面的三部分接口是怎么结合起来的。主要包括ssl怎么与async结合,engine怎么对async支持。

    ssl与async

    ssl内部组织了一个状态机,将握手,读写等操作抽象为两个job,ssl_io_intern(读写), ssl_do_handshake_intern(握手), 统一通过api ASYNC_start_job()进行job调度。

    JOB有 ASYNC_ERR, ASYNC_PAUSE, ASYNC_FINISH, ASYNC_NO_JOBS, 四个状态。状态表征了job的运行情况。

    async的基本模型是,1 cpu进行用户应用的前期计算,2  cpu进行ssl框架内的前期计算, 3  write数据进qat卡,4 qat卡计算,5 read数据出qat卡,6  cpu进行ssl框架内的后期计算,7 cpu进行用户应用的后期计算。

    async的目标就是让cpu在阶段3进行有效计算。

    为了达到这个目标,async模块将流程2356划分为一个job,17作为主流程(主协程?主job)。4被卸载到了硬件卡上,不占用cpu。协程的本质是对cpu的分时复用,所以,我们讨论job的时候,就是在讨论cpu的时间片分配。

    ssl调用了async_start_job之后,便进入了job 2356流程,当job执行完23之后,会调用api ASYNC_pause_job() 切换回主job,并将job状态设置为ASYNC_PAUSE, 这个时候CPU会交还给主job完全其他计算工作,qat并行的进行他自己的计算。

    主job知道另一个job没有进入结束状态,所以它利用SSL_waiting_for_async()接口获得的一个eventfd,并将它epoll()起来。当qat卡计算完成(另一个问题,cpu怎么知道qat卡计算完成了?)会执行一个回调,这个回调会写入eventfd,从而

    主job再次进入ssl之前的api,继续job的56流程。完成6之后,再调用ASYNC_pause_job()切换主job,并将job状态设置为ASYNC_FINISH. 主job完成流程7.  结束。

    async与engine

    接上面的问题, cpu怎么知道qat卡完成了计算?  

    答案:文章[openssl] intel qat场景下的openssl框架中提到了四种模式,其中模式inline启动了一个线程,该线程在不停的调用qatdriver的polling api,轮训获取qat的计算状态,得到相应结果后,写入eventfd,唤醒job。

    其他基于timer的模式,机制类似。

    前文提到的流程2(一部分)356(一部分),都被封装在了以插件的形式注册的加解密函数中,如前文的api表格所示。

    这部分处理除了负责读写硬件,还有另外两个职责,1, job的切回,也就是pause。2,eventfd的唤醒。

    千言万语抵不过一张图,通过下图理解一下:

    性能

    最后回到上一篇提到的性能问题。

    先回顾一下关于qat性能的讨论。 性能的提升有两个角度。

    1.  用户调用openssl时使用异步IO,就是使用epoll,select等,完成多个SSL的操作,这个与常规典型的异步IO使用的方法一下,不在赘述。

    2.  一个ssl流程里都包括,算随机数,做秘钥协商,非对称加密,对每个block的对称加密,对每个block的对称解密,这一个序列的每一个步都可以由qat卡完成计算。

        他们每一个被抽象成一个job,共同并行。这个并行包括了多个qat卡bank和cpu,他们多者之间的并行。

        也就是说,如上图所示的同一个SSL_accept(1st)的调用里,可以有多个ASYNC_start_job().

        而性能优化的角度1是:如上图的event_loop()的地方,可以有多个SSL_accept(1st)在等待完成。

    --------

  • 相关阅读:
    MySQL-多条件拼接语句
    MongoDB-C#驱动基本操作
    MongoDB-C#驱动帮助
    MongoDB-权限配置
    MongoDB-安装
    C#_实用
    提高C#代码质量-规范
    C#_Express-ickd接口
    Eclipse背景颜色修改
    Java中可变长参数的方法
  • 原文地址:https://www.cnblogs.com/hugetong/p/14379347.html
Copyright © 2011-2022 走看看