几年来我一直很高兴地使用scanadf
这些参数。-S script --script-wait
我的脚本名为 ,通过调用、通过管道传输,scan_perpage
将图像数据转换为 pdf 。pnmtops
ps2pdf
然而最近(我怀疑自从我从 Fedora 17 更新到 19 以来),被调用的脚本挂起,因此scanadf
挂起。该脚本在命令处挂起pnmtops
。命令pnmtops
本身正在等待其分叉的“子”pnmtops
输出过滤器完成,但它永远不会完成。
scan_perpage
直接在原始扫描仪页面输出上运行脚本效果很好。直接运行pnmtops
命令也可以正常工作。仅当从scanadf
via运行时-S
,脚本才会挂在 处pnmtops
。
版本:
# rpm -q --info sane-backends
Name : sane-backends
Version : 1.0.23
Release : 13.fc19
# rpm -q --info sane-frontends
Name : sane-frontends
Version : 1.0.14
Release : 16.fc19
# rpm -q --info netpbm
Name : netpbm
Version : 10.61.02
Release : 5.fc19
这是我的脚本的输出scan
,它调用scanadf -vv
:
$ scan -o output.pdf
Scanning...
scanadf: value for --resolution is: 300
scanadf: scanning image of size 2544x3300 pixels at 1 bits/pixel
scanadf: acquiring gray frame
Started script `/usr/local/bin/scan_perpage' as pid=10902
Scanned document scan-0001
pnmtops: Input maxval is 1. Postscript raster will have 1 bits per
sample, so maxval = 1
pnmtops: Image will be 610.56 points wide by 792.00 points high, left
edge 0.72 points from left edge of page, bottom edge 0.00 points from
bottom of page; NOT turned to landscape orientation
pnmtops: output filter spawned: pid 10904
pnmtops: Waiting for PID 10904 to exit
Scanned 1 pages
<the script hangs here>
这是挂起时的进程树:
10897 32072 /bin/sh /usr/local/bin/scan -o output.pdf
10898 10897 scanadf -vvv -d fujitsu -S /usr/local/bin/scan_perpage
--script-wait --resolution 300 --mode Lineart -o scan-%04d
10902 10898 /bin/bash /usr/local/bin/scan_perpage scan-0001
10903 10902 pnmtops -verbose -imagewidth 8.5 -imageheight 11 scan-0001
10904 10903 pnmtops -verbose -imagewidth 8.5 -imageheight 11 scan-0001
进程 10904(分叉的pnmtops
“输出过滤器”)永远不会完成。 strace 表明它正在等待“读取”。
我不明白为什么pnmtops
在调用 via 时会挂起scanadf
,但是当直接在同一文件上调用时,它工作得很好。
另外,如果pnmtops
手动终止子进程,一切都会继续,没有任何问题。
答案1
来自 netpbm 维护者 Bryan Henderson:
我发现并修复了导致此症状的错误。 [...] Netpbm 10.64.02 中已修复。
导致 Pnmtops 有时挂起有时不挂起的环境差异是打开文件的数量。如果调用 Pnmtops 时打开的文件超过 10 个,则会发生挂起。
如果你关心病理是什么:当给它输送信号的管道发出 EOF 信号时,孩子就会退出。当管道发送端的文件描述符的每个副本都关闭时,就会发生这种情况。唯一应该存在的副本是父进程正在写入数据的副本。但子进程必须继承管道两端的文件描述符的副本。如果孩子没有关闭其副本 发送在管道的末端,子进程永远不会在接收端看到 EOF,因此将永远等待。
这意味着子进程必须关闭为其供电的管道发送端的副本。为了做到这一点,并解决其他一些类似的问题,子进程在启动时尝试关闭除了它实际使用的两个文件描述符之外的每个文件描述符。但是 POSIX 没有提供一种方法来了解打开的文件描述符列表,因此子进程只是盲目地关闭 0-9(不包括它需要的两个),因为知道 Pnmtops 不会使用比这更多的文件。错误在于该程序没有考虑进程固有的文件描述符。修复方法是 Pnmtops 在启动时关闭文件描述符 0-9,这样它创建的任何管道的文件描述符编号都会在 0-9 范围内,从而通过盲目的 0-9 关闭来关闭。