与 C 程序通信时,子进程 Popen 参数无效/管道损坏
我有这段代码
所有需要的库都被导入
class VERTEX(Structure):
_fields_ = [("index", c_int),
("x", c_float),
("y", c_float)]
其他东西
这个从顶点 bpy.data.objects[nomeID].data.vertices 列表创建和数组
def writelist_buf(size, nomeID):
Nvert_VERTEX_Array_Type = VERTEX * len(bpy.data.objects[nomeID].data.vertices)
passarr = Nvert_VERTEX_Array_Type()
for i in range(len(passarr)):
vert = bpy.data.objects[nomeID].data.vertices[i]
passarr[i] = VERTEX(vert.index, vert.co[0], vert.co[1])
return passarr
是一个顶点列表。
其他内容
这是在 def 内部,并与 C 程序通信,
input = writelist_buf(size, nomeID)
c_program_and_args = "here is the program with his arguments(it works)"
cproc = Popen(c_program_and_args, stdin=PIPE, stdout=PIPE)
out, err = cproc.communicate(input)
#the program returns 2 integers separed by a space
return [int(i) for i in out.decode.split()]
在 writelist 调用之前声明了先前的数组大小和 nomeID。
经过一番“调试”后,我发现 writelist_buf 传递的类型是“合法的”(它是字节,因为它是用 c_types 创建的数组),但我不断收到 Errno32 Broken Pipe 或 Errno22 Invalid argument...C程序只需读取 stdin 即可检索所有顶点(如下面的 C 代码)。
奇怪的想法是,在我正在编写的代码中“集成”之前,我尝试了一种更简单的代码:这个,并且有用!
from subprocess import Popen, PIPE
from ctypes import *
class VERTEX(Structure):
_fields_ = [("index", c_int),
("x", c_float),
("y", c_float)]
nverts = 5
vlist = [VERTEX(0,1,1), VERTEX(1,2,2), VERTEX(2,3,3), VERTEX(3,4,4), VERTEX(4,5,5)]
array = VERTEX * nverts
input = array()
for i in range(nverts):
input[i] = vlist[i]
print(type(input))
cproc = Popen("pipeinout.exe random arg", stdin=PIPE, stdout=PIPE)
out, err = cproc.communicate(input)
print(out.decode())
和C代码
#include<stdio.h>
#include<stdlib.h>
typedef struct {
int index;
float x;
float y;
} vertex;
int main(int argc, char* argv[]) {
int n=5;
int i;
printf("%s",argv[1]);
vertex* VV;
VV=(vertex*)malloc(sizeof(vertex)*n);
fread(VV,sizeof(vertex),n,stdin);
//fread(&VV,sizeof(VV),1,stdin);//metti nel valore di VV(non a quello che punta) l'indirizzo passato||sizeof(VV) is the size of a pointer
for(i=0;i<n;i++)
printf(" %i , %f , %f\n",VV[i].index,VV[i].x,VV[i].y);
}
I have this code
All the needed libraries are imported
class VERTEX(Structure):
_fields_ = [("index", c_int),
("x", c_float),
("y", c_float)]
Other stuff
This create and array from a list of vertex
def writelist_buf(size, nomeID):
Nvert_VERTEX_Array_Type = VERTEX * len(bpy.data.objects[nomeID].data.vertices)
passarr = Nvert_VERTEX_Array_Type()
for i in range(len(passarr)):
vert = bpy.data.objects[nomeID].data.vertices[i]
passarr[i] = VERTEX(vert.index, vert.co[0], vert.co[1])
return passarr
bpy.data.objects[nomeID].data.vertices is a list of vertices.
Other stuff
This is inside a def, and communicate to a C program the previous array
input = writelist_buf(size, nomeID)
c_program_and_args = "here is the program with his arguments(it works)"
cproc = Popen(c_program_and_args, stdin=PIPE, stdout=PIPE)
out, err = cproc.communicate(input)
#the program returns 2 integers separed by a space
return [int(i) for i in out.decode.split()]
size and nomeID are declared before the writelist call.
After a bit of "debugging" i found that the type passed by the writelist_buf is "legal"(it's bytes, since is an array created with c_types), but i keep receiving a Errno32 Broken Pipe or Errno22 Invalid argument... The C program just make a read in the stdiin to retrive all the vertices(like the C code below)..
The strange think is that before "integrating" inside the code i was working on, i have tried a simpler code: this one, and it works!
from subprocess import Popen, PIPE
from ctypes import *
class VERTEX(Structure):
_fields_ = [("index", c_int),
("x", c_float),
("y", c_float)]
nverts = 5
vlist = [VERTEX(0,1,1), VERTEX(1,2,2), VERTEX(2,3,3), VERTEX(3,4,4), VERTEX(4,5,5)]
array = VERTEX * nverts
input = array()
for i in range(nverts):
input[i] = vlist[i]
print(type(input))
cproc = Popen("pipeinout.exe random arg", stdin=PIPE, stdout=PIPE)
out, err = cproc.communicate(input)
print(out.decode())
And the C code
#include<stdio.h>
#include<stdlib.h>
typedef struct {
int index;
float x;
float y;
} vertex;
int main(int argc, char* argv[]) {
int n=5;
int i;
printf("%s",argv[1]);
vertex* VV;
VV=(vertex*)malloc(sizeof(vertex)*n);
fread(VV,sizeof(vertex),n,stdin);
//fread(&VV,sizeof(VV),1,stdin);//metti nel valore di VV(non a quello che punta) l'indirizzo passato||sizeof(VV) is the size of a pointer
for(i=0;i<n;i++)
printf(" %i , %f , %f\n",VV[i].index,VV[i].x,VV[i].y);
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
从您的评论中,我了解到您将数百万个项目数百次传递给 C 程序。对于您的情况,下面的方法(使用子进程进行管道输入)可能太慢。可能的替代方案是编写 C 扩展(例如,使用 Cython)或使用 ctypes 直接调用 C 函数。您可以提出一个单独的问题,详细描述您的用例,了解哪种方法更可取。
如果您选择了一种方法,请确保它在任何优化之前都能正常工作(编写一些测试,测量性能,并且仅在需要时才进行优化)-- 使其工作、使其正确、使其快速。
另一方面,没有必要在那些已知稍后会被丢弃的方法上投入太多时间 - 快速失败。
如果 C 程序的输出是有界的;代码中的
.communicate()
方法有效(来源):这是问题评论中的代码。在我的机器上,对于较大的
n
值,它可以正常工作:Q&A
pack()
是一个生成器。生成器并不像您所描述的那样工作,例如:请注意每个
yield
都会生成一个值。这里的
yield
在文本中仅出现一次,但它被执行了 10 次,并且每次都会生成一个值(在本例中为整数)。请参阅 Python 教程中的生成器。 “系统程序员的生成器技巧”包含多个关于如何使用生成器从简单到高级的示例。s.pack(*v)
使用 参数解包:该行启动一个新线程,该线程使用来自
args
关键字参数(即output_file=p.stdin)的参数调用
和write()
函数chunks=pack(vertices, n)
。这种情况下的 write() 函数相当于:之后线程退出。
整个输出不存储在任何地方。代码:
从
p.stdout
逐行读取,直到.readline()
返回空字符串b''
并存储当前line
变量中的行(请参阅iter( )
文档)。所以:只打印输出的最后一行。
不,主线程退出。启动的线程不是守护进程。它会一直运行直到完成,即脚本(程序)在完成之前不会退出。
C 程序由
p = Popen(...)
启动。p.stdin.write()
写入 C 程序的stdin
(中间有很多缓冲区,但我们可以暂时忽略它)。该过程与以下相同:对于提供的 C 代码,无需在单独的线程中写入
p.stdin
。我使用线程正是为了避免警告中描述的情况,即,C 程序在脚本完成写入其 stdin 之前产生足够的输出(您的 C 代码在完成读取之前不会写入任何内容,因此不需要线程)。换句话说,在这种情况下,p.wait() 是安全的。
如果没有
p.wait()
,C 程序的 stderr 输出可能会丢失。虽然我只能使用 脚本 在 jython 上重现 stderr 丢失。对于提供的 C 代码来说,这并不重要,因为它没有向 stderr 写入任何内容。From your comments I understand that you pass millions of items hundreds of times to a C program. The approach below (pipe input using subprocess) might be too slow in your case. Possible alternatives could be to write a C extension (e.g., using Cython) or to use
ctypes
to call C functions directly. You could ask a separate question describing your use case in detail about what approach could be preferable.If you've chosen an approach then make sure that it works correctly before any optimization (write some tests, measure performance and only after optimize it if needed) -- Make it work, make it right, make it fast.
On the other hand there is no point to invest too much time in approaches that are known to be thrown away later -- Fail fast.
if the output of the C program is bounded; the
.communicate()
method from your code works (source):Here's the code from the comments to the question. It works without errors for large
n
values on my machine:Q&A
pack()
is a generator. Generators do not work how you've described them, e.g.:Note each
yield
produces a value.Here's the
yield
is present in the text only one time but it is executed 10 times and each time it produces a value (an integer in this case). See Generators in the Python tutorial. "Generator Tricks for Systems Programmers" contains multiple examples on how to use generators from a simple to an advanced usage.s.pack(*v)
calls thepack
method using argument unpacking:This line starts a new thread that calls
write()
function with the arguments from theargs
keyword argument i.e.output_file=p.stdin
andchunks=pack(vertices, n)
. Thewrite()
function in this case is equivalent to:After that the thread exits.
The whole output is not stored anywhere. The code:
reads from
p.stdout
line-by-line until the.readline()
returns empty stringb''
and stores the current line in theline
variable (seeiter()
docs). So:just prints the last line of the output.
No, the main thread exits. The started thread is not daemon. It runs until it completes i.e., the script (the program) doesn't exit until it completes.
The C program is started by
p = Popen(...)
.p.stdin.write()
writes tostdin
of the C program (there are number of buffers in between but we can forget about it for a moment). The process is the same as in:For the provided C code it is not necessary to write to
p.stdin
in a separate thread. I use the thread exactly to avoid the situation described in the warning i.e., C program produces enough output before the script finishes writing to its stdin (your C code doesn't write anything before it finishes reading so the thread is not necessary).In other words
p.wait()
is safe in this case.Without
p.wait()
stderr output from the C program might be lost. Though I can reproduce the stderr loss only on jython with the scripts. Yet again for the provided C code it doesn't matter due to it is not writing to stderr anything.