subprocess 模块及其提供的 Popen 类,使用该构造器在一个进程中创建新的子进程
args:要执行的命令或可执行文件的路径。一个由字符串组成的序列(通常是列表),列表的第一个元素是可执行程序的路径,剩下的是传给这个程序的参数,如果没有要传给这个程序的参数,args 参数可以仅仅是一个字符串。
bufsize:控制 stdin, stdout, stderr 等参数指定的文件的缓冲,和打开文件的 open()函数中的参数 bufsize 含义相同。
executable:如果这个参数不是 None,将替代参数 args 作为可执行程序;
stdin:指定子进程的标准输入;
stdout:指定子进程的标准输出;
stderr:指定子进程的标准错误输出;
  对于 stdin, stdout 和 stderr 而言,如果他们是 None(默认情况),那么子进程使用和父进程相同的标准流文件。

  父进程如果想要和子进程通过 communicate() 方法通信,对应的参数必须是 subprocess.PIPE(见下文例4);

  当然 stdin, stdout 和 stderr 也可以是已经打开的 file 对象,前提是以合理的方式打开,比如 stdin 对应的文件必须要可读等。 

preexec_fn:默认是None,否则必须是一个函数或者可调用对象,在子进程中首先执行这个函数,然后再去执行为子进程指定的程序或Shell。
close_fds:布尔型变量,为 True 时,在子进程执行前强制关闭所有除 stdin,stdout和stderr外的文件;
shell:布尔型变量,明确要求使用shell运行程序,与参数 executable 一同指定子进程运行在什么 Shell 中——如果executable=None 而 shell=True,则使用 /bin/sh 来执行 args 指定的程序;也就是说,Python首先起一个shell,再用这个shell来解释指定运行的命令。
cwd:代表路径的字符串,指定子进程运行的工作目录,要求这个目录必须存在;
env:字典,键和值都是为子进程定义环境变量的字符串;
universal_newline:布尔型变量,为 True 时,stdout 和 stderr 以通用换行(universal newline)模式打开,
startupinfo:见下一个参数;
creationfalgs:最后这两个参数是Windows中才有的参数,传递给Win32的CreateProcess API调用。
  同 Linux 中创建子进程类似,父进程创建完子进程后,并不会自动等待子进程执行,父进程在子进程之前推出将导致子进程成为孤儿进程,孤儿进程统一由 init 进程接管,负责其终止后的回收工作。

  如果父进程在子进程之后终止,但子进程终止时父进程没有进行最后的回收工作,子进程残留的数据结构称为僵尸进程。大量僵尸进程将耗费系统资源,因此父进程及时等待和回收子进程是必要的,除非能够确认自己比子进程先终止,从而将回收工作过渡给 init 进程。

  这个等待和回收子进程的操作就是wait()函数,下文中将会介绍。

实例:
def webso_to_node(secret='supersecret', port1='8000', port2='8001'):
    # prepare source code for the websocket relay.
    ws_relay_path, _ = os.path.split(os.path.realpath(__file__))
    ws_relay_file = os.path.join(ws_relay_path, "websocket-relay.js")

    if not os.path.exists(ws_relay_file):
        relay_code = '''var fs=require("fs"),http=require("http"),WebSocket=require("ws");process.argv.length<3&&(console.log("Usage: node websocket-relay.js <secret> [<stream-port> <websocket-port>]"),process.exit());var STREAM_SECRET=process.argv[2],STREAM_PORT=process.argv[3]||8081,WEBSOCKET_PORT=process.argv[4]||8082,RECORD_STREAM=!1,socketServer=new WebSocket.Server({port:WEBSOCKET_PORT,perMessageDeflate:!1});socketServer.connectionCount=0,socketServer.on("connection",function(e,o){socketServer.connectionCount++,console.log("New WebSocket Connection: ",(o||e.upgradeReq).socket.remoteAddress,(o||e.upgradeReq).headers["user-agent"],"("+socketServer.connectionCount+" total)"),e.on("close",function(e,o){socketServer.connectionCount--,console.log("Disconnected WebSocket ("+socketServer.connectionCount+" total)")})}),socketServer.broadcast=function(e){socketServer.clients.forEach(function(o){o.readyState===WebSocket.OPEN&&o.send(e)})};var streamServer=http.createServer(function(e,o){if(e.url.substr(1).split("/")[0]!==STREAM_SECRET&&(console.log("Failed Stream Connection: "+e.socket.remoteAddress+":"+e.socket.remotePort+" - wrong secret."),o.end()),o.connection.setTimeout(0),console.log("Stream Connected: "+e.socket.remoteAddress+":"+e.socket.remotePort),e.on("data",function(o){socketServer.broadcast(o),e.socket.recording&&e.socket.recording.write(o)}),e.on("end",function(){console.log("close"),e.socket.recording&&e.socket.recording.close()}),RECORD_STREAM){var t="recordings/"+Date.now()+".ts";e.socket.recording=fs.createWriteStream(t)}}).listen(STREAM_PORT);console.log("Listening for incomming MPEG-TS Stream on http://0.0.0.0:"+STREAM_PORT+"/<secret>"),console.log("Awaiting WebSocket connections on ws://0.0.0.0:"+WEBSOCKET_PORT+"/");'''
        with open(ws_relay_file, 'w') as f:
            f.write(relay_code)
    return subprocess.Popen('node {} {} {} {}'. \
                            format(ws_relay_file, secret, port1, port2).split(' '), shell=False)