我正在开发一个使用指纹扫描仪进行用户识别的应用程序。与指纹扫描仪的交互是在带有 libfprint 的 C 程序中完成的,我在 PHP 中使用 proc_open 调用该程序。指纹注册过程是一个由 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”直到我完成指纹扫描仪的注册过程后才出现,此时指纹扫描仪的所有输出都随之而来。注册.c 应用程序几乎是 libfprint 附带的示例注册.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()
调用(也尝试过fread()
并stream_get_contents()
具有类似行为)阻塞直到 C 程序终止的任何想法?我希望在程序运行时得到输出。如果我切换到 using $cmd = "ping 127.0.0.1";
,它的行为与我预期的一样,在输出到标准输出时输出 ping 的每一行。
更新...已修复
Barmar 在下面是正确的,我只需要关闭输出缓冲....但是在这样做时,我遇到了一些奇怪的不一致,我想记录下来以供其他有类似问题的人使用...修复是
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;
}
不管怎样,现在已经修好了。谢谢!