子进程Popen与C程序通信时无效参数/断开管道

时间:2011-02-12 17:20:24

标签: python arrays pipe subprocess

我有这段代码

导入所有需要的库

class VERTEX(Structure):
 _fields_ = [("index", c_int),
            ("x", c_float),
            ("y", c_float)]

其他东西

这是从顶点列表

创建和数组
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是一个顶点列表。

其他东西

这是在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()]

size和nomeID在写入列表之前声明。

经过一些“调试”后,我发现writelist_buf传递的类型是“合法的”(它是字节,因为是用c_types创建的数组),但我一直收到Errno32 Broken Pipe或Errno22 Invalid argument .. C程序只是在stdiin中读取所有顶点(如下面的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);
}

1 个答案:

答案 0 :(得分:2)

根据您的评论,我了解您将数百万件物品传递给C程序数百次。在您的情况下,下面的方法(使用子进程的管道输入)可能太慢。可能的替代方案可能是编写C扩展(例如,使用Cython)或使用ctypes直接调用C函数。您可以单独询问一个单独的问题,详细说明您的用例可能更适合的方法。

如果你选择了一种方法,那么在任何优化之前确保它正常工作(编写一些测试,测量性能,并且只有在需要时才进行优化) - Make it work, make it right, make it fast

另一方面,没有必要在以后被抛弃的方法上投入太多时间 - 快速失败

如果C程序的输出有界;代码中的.communicate()方法有效(source):

import struct, sys    
from subprocess import Popen, PIPE

vertex_struct = struct.Struct('i f f')

def pack(vertices, n):    
    yield struct.pack('i', n)
    for v in vertices:
        yield vertex_struct.pack(*v)

def main():
    try: n = int(sys.argv[1])
    except IndexError:
        n = 100
    vertices = ((i,i+1,i+2) for i in range(n))

    p = Popen(["./echo_vertices", "random", "arg"], stdin=PIPE, stdout=PIPE)
    out, _ = p.communicate(b''.join(pack(vertices, n)))

    index, x, y = vertex_struct.unpack(out)
    assert index == (n-1) and int(x) == n and int(y) == (n+1)

if __name__ == '__main__':
    main()

这是问题评论中的the code。对于我的计算机上的大n值,它可以正常运行:

import struct, sys
from subprocess import Popen, PIPE
from threading import Thread

def pack(vertices, n):
    yield struct.pack('i', n)
    s = struct.Struct('i f f')
    for v in vertices:
        yield s.pack(*v)

def write(output_file, chunks):
    for chunk in chunks:
        output_file.write(chunk)
    output_file.close()

def main():
    try: n = int(sys.argv[1])
    except IndexError:
        n = 100
    vertices = ((i,i+1,i+2) for i in range(n))

    p = Popen(["./echo_vertices", "random", "arg"], stdin=PIPE, stdout=PIPE)

    Thread(target=write, args=[p.stdin, pack(vertices, n)]).start()

    for line in iter(p.stdout.readline, b''):
        pass
    p.stdout.close()
    sys.stdout.buffer.write(line)
    p.wait()

if __name__ == '__main__':
    main()

Q&安培; A

  

问:我真的不明白这个包   函数(我知道收益率回报   一个可迭代的可迭代对象   只有一次,但在你的代码中   使用2产量,所以我不明白它   回报。

pack()是一个生成器。生成器不能像你描述的那样工作,例如:

>>> def f():
...     yield 1
...     yield 2
... 
>>> for i in f():
...     print(i)
...     
1
2

注意每个yield都会产生一个值。

>>> def g(n):
...     for i in range(n):
...         yield i
... 
>>> list(g(10))
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

这里yield只出现在文本中一次,但每次执行10次,每次产生一个值(在这种情况下为整数)。请参阅Python教程中的Generators"Generator Tricks for Systems Programmers"包含有关如何使用从简单到高级用法的生成器的多个示例。


  

问:另外我不知道是什么   (* v)表示第10行

s.pack(*v)使用argument unpacking调用pack方法:

>>> def h(a, b):
...     print(a, b)
... 
>>> h(*[1, 'a'])
1 a
>>> h(*range(2))
0 1
>>> h(0, 1)
0 1

  

问:我不明白   第25行中的线程工作,

Thread(target=write, args=[p.stdin, pack(vertices, n)]).start()

此行启动一个新线程,该线程使用write()关键字参数(即argsoutput_file=p.stdin)中的参数调用chunks=pack(vertices, n)函数。在这种情况下,write()函数等同于:

p.stdin.write(struct.pack('i', n))
p.stdin.write(s.pack(0, 1, 2))
p.stdin.write(s.pack(1, 2, 3))
...
p.stdin.write(s.pack(n-1, n, n+1))
p.stdin.close()

之后线程退出。


  

问: ......等等   程序的读取输出..它   不存储在变量中,是吗?

整个输出不存储在任何地方。代码:

for line in iter(p.stdout.readline, b''):
    pass

逐行从p.stdout读取,直到.readline()返回空字符串b''并将当前行存储在line变量中(请参阅iter()文档)。所以:

sys.stdout.buffer.write(line)

只打印输出的最后一行。


  

问: 1)启动线程后,   python脚本等待它完成,   正确?

不,主线程退出。启动的线程不是守护进程。它一直运行直到它完成,即脚本(程序)在完成之前不会退出。


  

问: 2)我明白你的阅读方式   从C程序的标准输出,但我   当你开始它时不要得到.Afa i   理解,用写函数我们   写入缓冲区(或类似的东西)   ram中的文件)我们想要的数据,和   当我们运行c程序时,它可以读取   从它我们写的数据。但是当我们   在你的代码中启动C程序? :)

C程序由p = Popen(...)启动。

p.stdin.write()写入C程序的stdin(中间有多个缓冲区,但我们暂时可以忘记它)。该过程与以下内容相同:

$ echo abc | some_program

  

问: 3)最后一件事:为什么要等待   P +有一个警告   http://docs.python.org/library/subprocess.html?#subprocess.Popen.wait

对于提供的C代码,不必在单独的线程中写入p.stdin。我完全使用线程来避免警告中描述的情况,即C程序在脚本完成写入stdin之前产生足够的输出(你的C代码在完成读取之前没有写任何东西,所以线程不是必需的)。 / p>

换句话说,p.wait()在这种情况下是安全的。

没有来自C程序的p.wait() stderr输出可能会丢失。虽然我只能在the scripts的jython上重现stderr损失。然而,对于提供的C代码,它并不重要,因为它没有写入stderr任何东西。

相关问题