0

ユーザーの識別に指紋スキャナーを使用するアプリに取り組んでいます。指紋スキャナーとの対話は、PHP で proc_open を使用して呼び出した libfprint を使用する C プログラムで行われます。指紋登録プロセスは、libprintf によってステート マシンとして処理される多段階プロセスです。ライブラリと C プログラムの間で制御が渡され、C プログラムがユーザーにフィードバック (ステージの成功、失敗、およびその理由) を提供できるようになります...

PHP でプロセス リソースからデータを読み取ろうとすると、問題が発生します。サンプル PHP コードは次のとおりです。

<?php
ob_implicit_flush(true);
$cmd = "/home/pi/projects/PingPongSet/enroll";

$descriptorspec = array(
   0 => array("pipe", "r"),   // stdin is a pipe that the child will read from
   1 => array("pipe", "w"),   // stdout is a pipe that the child will write to
   2 => array("pipe", "w")    // stderr is a pipe that the child will write to
);
flush();
$process = proc_open($cmd, $descriptorspec, $pipes);
echo 'opened'."\n";
if (is_resource($process)) {
    sleep(1);
    echo "blah\n";
    var_export(fgets($pipes[1]));
    echo "blah213\n";
    while ($s = fgets($pipes[1])) {
        print $s;
        flush();
    }
}

?>

var_export を追加して、ブロッキングが発生している場所を絞り込みました。「opened」と「blah」の出力は正常に取得されますが、「blah123」は、指紋スキャナーでの登録プロセスが完了するまで表示されません。この時点で、指紋スキャナーからのすべての出力がそれに伴います。enroll.c アプリは、libfprint に付属するサンプルの enroll.c とほぼ同じですが、特定のステージが成功したときにサウンドを再生するようにいくつかの変更が加えられています。そのプログラムの「重要な」部分は次のとおりです。

struct fp_print_data *enroll(struct fp_dev *dev) {
    struct fp_print_data *enrolled_print = NULL;
    int r;

    do {
        struct fp_img *img = NULL;

        sleep(1);
        printf("\nScan your finger now.\n");

        r = fp_enroll_finger_img(dev, &enrolled_print, &img);
        printf("\nFinger scanned.\n");
        if (img) {
            fp_img_save_to_file(img, "enrolled.pgm");
            printf("Wrote scanned image to enrolled.pgm\n");
            fp_img_free(img);
        }
        if (r < 0) {
            printf("Enroll failed with error %d\n", r);
            play_error();
            return NULL;
        }

        switch (r) {
        case FP_ENROLL_COMPLETE:
            printf("Enroll complete!\n");
            break;
        case FP_ENROLL_FAIL:
            printf("Enroll failed, something wen't wrong :(\n");
            play_error();
            return NULL;
        case FP_ENROLL_PASS:
            printf("Enroll stage passed. Yay!\n");
            play_success();
            break;
        case FP_ENROLL_RETRY:
            printf("Didn't quite catch that. Please try again.\n");
            play_error();
            break;
        case FP_ENROLL_RETRY_TOO_SHORT:
            printf("Your swipe was too short, please try again.\n");
            play_error();
            break;
        case FP_ENROLL_RETRY_CENTER_FINGER:
            printf("Didn't catch that, please center your finger on the "
                "sensor and try again.\n");
            play_error();
            break;
        case FP_ENROLL_RETRY_REMOVE_FINGER:
            printf("Scan failed, please remove your finger and then try "
                "again.\n");
            play_error();
            break;
        }
    } while (r != FP_ENROLL_COMPLETE);

    if (!enrolled_print) {
        fprintf(stderr, "Enroll complete but no print?\n");
        return NULL;
    }

    printf("Enrollment completed!\n\n");
    play_success();
    return enrolled_print;
}

C プログラムが終了した後の PHP アプリの出力は次のとおりです。

opened
blah 
-----NOTE: THE STUFF BELOW HERE DOESN'T DISPLAY UNTIL AFTER enroll TERMINATES-----
'Found device claimed by Digital Persona U.are.U 4000/4000B/4500 driver
'blah213
Opened device. It's now time to enroll your finger.


Scan your finger now.
uru4000:info [init_run_state] Versions 0040 and 0014

Finger scanned.
Wrote scanned image to enrolled.pgm
Enroll stage passed. Yay!

Scan your finger now.

Finger scanned.
Wrote scanned image to enrolled.pgm
Enroll stage passed. Yay!

Scan your finger now.

Finger scanned.
Wrote scanned image to enrolled.pgm
Enroll stage passed. Yay!

Scan your finger now.

Finger scanned.
Wrote scanned image to enrolled.pgm
Enroll stage passed. Yay!

Scan your finger now.

Finger scanned.
Wrote scanned image to enrolled.pgm
Enroll complete!
Enrollment completed!

Closing device

fgets()Cプログラムが終了するまで呼び出しがブロックされる理由についてのアイデアはfread()ありますか? stream_get_contents()プログラムの実行中に出力が得られることを期待しています。using に切り替えると$cmd = "ping 127.0.0.1";、期待どおりに動作し、ping の各行が stdout に出力されるように出力されます。

更新...修正

以下のバーマーは正しかったので、出力バッファリングをオフにする必要がありました....しかし、そうすることで、同様の問題を抱えている他の人のために文書化したい奇妙な矛盾に遭遇しました...修正は

setbuf(stdout, NULL);

このコメント によれば、これは次と同等である必要があります

setvbuf(stdout, NULL, _IONBF, 0);

しかし、を使用するsetvbuf(stdout, NULL, _IONBF, 0);と、C プログラムはすべてを正常に出力し、setvbuf(stdout, NULL, _IONBF, 0); . ではsetbuf(stdout, NULL);、すべてが完璧に機能しました。

私が非常に興味深いと思うのは、次のようなプログラムでは、出力バッファリングをオフにすることなく、PHP スクリプトが stdio を問題なく取得できたことです...そして両方のスクリプトに stdio が含まれていたため、両方とも出力バッファリングを有効にする必要がありました.. .

#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include <alsa/pcm.h>

int main() {
  int val;

  printf("ALSA library version: %s\n", SND_LIB_VERSION_STR);

  printf("\nPCM stream types:\n");
  for (val = 0; val <= SND_PCM_STREAM_LAST; val++)
    printf("  %s\n",
      snd_pcm_stream_name((snd_pcm_stream_t)val));

  return 0;
}

とにかく、それは今修正されました。ありがとう!

4

1 に答える 1

0

Barmar は、問題の原因については正しかった: stdio がバッファリングされていた。これは私のためにそれを修正したものです:

setbuf(stream, NULL);
于 2014-09-20T17:06:38.630 に答える