ホームページ > バックエンド開発 > PHPチュートリアル > PHP 拡張機能の開発に失敗した

PHP 拡張機能の開発に失敗した

WBOY
リリース: 2016-06-23 13:44:54
オリジナル
915 人が閲覧しました

失敗した PHP 拡張機能開発の旅

By warezhou 2014.11.19


Origin

継続的な反復の後、私たちの部門のコルーチンバージョンネットワークフレームワーク(CoSvr)フレーム)がついにリリースされました!これは元々は素晴らしいことでしたが、新しいサービスが継続的にアクセスされるにつれて、多くの固有の欠陥が徐々に表面化しました。 「過負荷保護」をサポートしていません

「ホットリスタート」をサポートしていません

  • 「64ビット」をサポートしていません
  • ... ...
  • 上級バックエンド開発者にとって、上記の問題はほとんどのそれらを識別するのは難しく、それらが問題になる理由は、「カエルをお湯でゆでる」ようなものです。反復プロセスにはマクロなビジョンが欠けており、導入されるビジネス機能が多すぎるため、全体的なアーキテクチャが不合理になります。最近の「コルーチン バージョン」は、元々は私の個人的なアマチュア作品で、ビジネス コードを楽しく書き、作業をすぐに終了するために、最下層はオリジナルの SvrFrame を直接再利用しました。結果はご想像のとおり、基盤が強力ではありません。大地が揺れる!最も極端な 64Bit を例に挙げると、誰もが数秒で理解できると思います。
  • 多くの調査と議論を経て、最終的に次のような方向性を出しました:
  • 社内オープンソース SPP3.0 フレームワークを導入し、基本的な周辺機能を吸収し、二次的なビジネス開発を行う

    SPP を拡張し、PHP をサポートする埋め込みプログラミング用のスクリプト言語であり、C 拡張機能の形で PHP にコルーチン機能を提供します (今後、PHPer は非同期コードを喜んで作成できるようになり、母はもう私のコールバックについて心配する必要がなくなりました!)

    難しい? 長くなりましたが、本題に入りましょう: C++/PHP 混合プログラミングを実装するにはどうすればよいでしょうか?

  • 免責事項:
  • 私は途中の修道士なので、丸一週間 PHP 拡張機能の開発に携わっていないため、「WHY」については触れず、「HOW」については記録目的のみにとどめることができれば幸いです。専門家なら許してくれるでしょう!
  • オープニング

    組み込み

    PHP 業界における C++/PHP の組み合わせは一般に「パフォーマンス」の考慮事項に基づいており、特定のパフォーマンスのボトルネック (PB シリアル化など) を解決するために PHP コード内で C/C++ 拡張機能を呼び出します。 )。

    C/C++ 開発者としては、「パフォーマンス」よりも「開発効率」の誘惑が明らかに大きいため、私たちのアイデアは、PHP をスクリプト言語として使用して、ビジネス ロジックを迅速に開発し、それを SPP フレームワークに挿入して実行することです。 。

    1. RTLD_GLOBAL モードで PHP ダイナミック ライブラリを開きます

    void *php_handler = dlopen("libphp5.so", RTLD_LAZY | RTLD_GLOBAL);if (!php_handler) {    base->log_.LOG_P_PID(LOG_FATAL, "%s\n", dlerror());    return -1; }   dlclose(php_handler);
    ログイン後にコピー

    2. php_embed_init を通じて初期化します

    php_embed_module.php_ini_path_override = "../php/php.ini";php_embed_init(0, NULL);
    ログイン後にコピー

    3. zend_eval_string は PHP スクリプトを導入します

    すごいです

    4. call_user_function による PHP 関数のコールバック

    zend_first_try {    char exec_str[256];    snprintf(exec_str, sizeof(exec_str), "include '%s';", "../php/demo_handler.php");    if (int ret = zend_eval_string(exec_str, NULL, exec_str TSRMLS_CC)) {        base->log_.LOG_P_PID(LOG_FATAL, "zend_eval_string fail. ret=%d\n", ret);        return -1;     }    base->log_.LOG_P_PID(LOG_DEBUG, "zend_eval_string succ.\n");} zend_catch {    base->log_.LOG_P_PID(LOG_FATAL, "zend_eval_string catch.\n");} zend_end_try ();
    ログイン後にコピー

    5. php_embed_shutdown によるクリーンアップ

    zval z_funcname;ZVAL_STRING(&z_funcname, "EchoDemo::init", 1);zval *zp_svr;MAKE_STD_ZVAL(zp_svr);ZVAL_LONG(zp_svr, (long)base);zval *zp_etc;MAKE_STD_ZVAL(zp_etc);ZVAL_STRING(zp_etc, etc, 1);zval z_retval;zval *z_params[] = {zp_svr, zp_etc};int call_ret = call_user_function(CG(function_table), NULL, &z_funcname, &z_retval, sizeof(z_params) / sizeof(z_params[0]), z_params TSRM convert_to_long(&z_retval);int func_ret = Z_LVAL_P(&z_retval);zval_ptr_dtor(&zp_etc);zval_dtor(&z_funcname);zval_dtor(&z_retval);if (call_ret < 0 || func_ret < 0) {    base->log_.LOG_P_PID(LOG_FATAL, "call_user_function fail. call_ret=%d func_ret=%d\n", call_ret, func_ret);    return -1;}
    ログイン後にコピー

    PHP

    PHP C 拡張機能の開発に関する記事はインターネット上にあります。興味のある読者は記事末尾の付録を詳しく読んでみてください。

    1. PHP ソース コード パッケージをダウンロードし、手動でコンパイルします。上記の組み込み使用と連携するには、?enable-embed オプション

    php_embed_shutdown(TSRMLS_C);
    ログイン後にコピー

    2 をオンにする必要があります。 PHP ソース コード パッケージの ext ディレクトリに入り、ext_skel ツールがプラグイン シェルフ コードを生成します

    ./configure --enable-embedmakemake install(可选)
    ログイン後にコピー

    3. config.m4 を編集し、PHP_ARG_WITH または PHP_ARG_ENABLE オプションをオンにします (正直に言うと、違いはまだ明確ではありません。アドバイスをお願いします)、C++ サポート、依存関係パスなどを追加します。

    cd ext./ext_skel --extname=demo
    ログイン後にコピー

    4.demo.cpp を編集し、拡張定義と実装 (関数、クラス、変数など) を追加します。 ) ここに挙げるのは関数定義の例だけです。クラスに興味のある読者は、付録に従って調べてください。ここで示した sendrecv 関数の定義は比較的代表的なもので、3 番目のパラメーター rsp は、受信したデータを PHP 呼び出し元に返す責任があります

    PHP_ARG_ENABLE(demo, whether to enable demo support,    [  --enable-demo           Enable demo support])if test "$PHP_DEMO" != "no"; then  PHP_REQUIRE_CXX()  PHP_ADD_LIBRARY(stdc++, 1, EXTRA_LDFLAGS)  PHP_ADD_INCLUDE(/root/spp/module/include/)  PHP_ADD_INCLUDE(/root/spp/module/include/spp_incl/)  PHP_NEW_EXTENSION(demo, demo.cpp, $ext_shared)fi
    ログイン後にコピー
    ZEND_BEGIN_ARG_INFO_EX(arginfo_sendrecv, 0, 0, 7)    ZEND_ARG_INFO(0, req)    ZEND_ARG_INFO(0, req_len)    ZEND_ARG_INFO(1, rsp)    ZEND_ARG_INFO(0, rsp_len)    ZEND_ARG_INFO(0, ip)    ZEND_ARG_INFO(0, port)    ZEND_ARG_INFO(0, timeout)ZEND_END_ARG_INFO()PHP_FUNCTION(sendrecv){    char *req = NULL;    int req_str_len = 0;    long req_len = 0;    zval *rsp = NULL;    long rsp_len = 0;    char *ip = NULL;    int ip_str_len = 0;    long port = 0;    long timeout = 0;if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "slzlsll", &req, &req_str_len,&req_len, &rsp, &rsp_len, &ip, &ip_str_len, &port, &timeout) == FAILURE) {        return;}       struct sockaddr_in addr;    memset(&addr, 0, sizeof(addr));    addr.sin_family = AF_INET;    addr.sin_addr.s_addr = inet_addr(std::string(ip, ip_str_len).c_str());    addr.sin_port = htons(port);    char *rsp_buf = (char *)emalloc(rsp_len);    int rsp_buf_len = rsp_len;    if (int ret = mt_udpsendrcv(&addr, req, req_len > req_str_len ? req_str_len : req_len, rsp_buf, rsp_buf_len, timeout)) {        efree(rsp_buf);        RETURN_LONG(ret);    }    zval_dtor(rsp);    ZVAL_STRINGL(rsp, rsp_buf, rsp_buf_len, 0);    RETURN_LONG(0);}
    ログイン後にコピー

    5。私は個人的には動的コンパイルを好みます (静的コンパイルには php ソース コードの再コンパイルが必要ですが、これは非常に時間がかかり、手間がかかります)。 6. php.ini ファイルを編集し、新しい拡張機能を追加すると、PHP コードで新しい拡張機能を呼び出すことができます

    クライマックス

    いよいよ、EchoDemo をいくつかプレイしてみました。 telnetとsaw エコーが一行ずつ表示されるので非常に気持ちいいです。

    const zend_function_entry demo_functions[] = {    PHP_FE(sendrecv, arginfo_sendrecv)    PHP_FE_END  /* Must be the last line in demo_functions[] */};
    ログイン後にコピー

    ここで最も優れているのは、プロセス関数の sendrecv への拡張呼び出しです。非同期ネットワーク インタラクションは、バックグラウンドでコルーチンを介して実装されています。同期 CGI のようなロジック コードを作成できるだけでなく、それを簡単に楽しむこともできます。非同期の高い同時実行性。

    願いは美しいけど現実は残酷!

    私は突然アイデアを思いつきました。パフォーマンスのストレス テストを行って、ネイティブ C++ コードと比較してどの程度パフォーマンスが低下するかを確認してみましょう。 1KB の単一リクエストが適用され、1w/s の圧力が適用されました。しばらくすると、コアダンプが抑制されました。

    メモリリーク?コルーチンスタックオーバーフロー? ...

    期間中色々紆余曲折あり、GDB、コルーチンのスタックサイズ変更、Google、PHPerの相談…

    すぐに夜になり、確認すべきことは確認した、質問は全て完了それは尋ねる必要がありますが、私にできることは何もありません。わかりました。お茶を飲みに立ち寄ってください。「call_user_function は再入可能ですか?」このレベルを考えると、コルーチンの性質を理解している兄弟ならすぐに理解できると思います。「くそー、Zend を実装するとき、呼び出し元のスレッドがユーザー モード スケジューリングのためにコルーチンを実行することをどのようにして知ることができるのでしょうか。これではすべてが可能です。」ブラックボックス。 !グローバル変数、静的変数...

    さて、sendrecv などのコルーチンベースの拡張機能を削除して、テストを再負荷します。単一のワーカーは、ストレスなく 3w/s のエコーを処理できます。

    エンディング

    今回の最大の魅力の一つは結局実現には至りませんでしたが、考えることは一人で行動するよりも100倍効率的であるという視点を改めて確認できたのでとても満足しています

    特に難しい問題に対処する場合、頭のないハエが飛び回るのは報われないことがよくあります。このとき、落ち着いて既存の知識の蓄えを収集することに全力を尽くすことができれば、もしかしたらインスピレーションが訪れるかもしれません。

    今後の可能性のある方向性: PHP はバージョン 5.5 から yield を導入しました。Zend の yield サポートの詳細を掘り下げれば、それを C フレームワークとうまく統合できるかもしれないと感じていますが、これには大きな穴があるといつも感じています。それは埋めることができません。他の要因を差し置いても、私はやはり Golang のような言語を選択して goroutine の利点を直接享受したいと思うかもしれません (笑)。 付録

    PHP 拡張機能の開発とカーネル アプリケーション

    http://www.walu.cc/phpbook/preface.md

    PHP 拡張機能をコンパイルする 2 つの方法

    http://521-wf.com/archives/ 227 .html

    C++ を使用して PHP 拡張機能を開発する方法 (パート 1)

    http://521-wf.com/archives/241.html

    C++ を使用して PHP 拡張機能を開発する方法 (パート 2)

    http ://521-wf.com/archives/245.html

    PHP 拡張機能での C++ クラスのラップ


    http://devzone.zend.com/1435/wrapping-c-classes-in-a-php-extension /

    🎜
    ソース:php.cn
    このウェブサイトの声明
    この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
    人気のチュートリアル
    詳細>
    最新のダウンロード
    詳細>
    ウェブエフェクト
    公式サイト
    サイト素材
    フロントエンドテンプレート