Table of Contents
系统进程
今天在看《Beginning Linux Programming》中的进程相关部分,讲到Linux几个进程相关的系统函数: system
, exec
, fork
, wait
. Python的 os
模块实现了对应的函数封装了这些系统调用: os.system
, os.exec
, os.fork
, os.wait
.
1. os.system(command)
# 在一个程序内部启动另一个程序,从而创建一个新进程
# os.system 在一个shell中执行command命令,这是一个对C函数system()的python实现,具有相同的限制条件。在Unix系统中,返回值是命令执行后的退出状态值。由于POSIX没有为C函数system()的返回值指定明确的含义,所以os.system()的返回值依# 赖具体的系统。在Windowns中,返回值由系统环境变量决定: COMSPEC, 此环境变量依据不同系统版本不同
# 因为os.system的局限性,官方文档建议用 subprocess模块的功能来代替此函数
2. os.exec系列
os.execl(path, arg0, arg1, ...)
os.execle(path, arg0, arg1, ..., env)
os.execlp(file, arg0, arg1, ...)
os.execlpe(file, arg0, arg1, ..., env)
os.execv(path, args)
os.execve(path, args, env)
os.execvp(file, args)
os.execvpe(file, args, env)
# 这些函数都执行一个新的程序,然后用新的程序替换当前子进程的进程空间,而该子进程从新程序的main函数开始执行。在Unix下,该新程序的进程id是原来被替换的子进程的进程id。原来的程序不再运行。在原来子进程中打开的所有描述符默认都是可用的,不会被关闭。
# execv*系列的函数表示其接受的参数是以一个list或者是一个tuple表示的参数表
# execl*系列的函数表示其接受的参数是一个个独立的参数传递进去的。
# exec*p*系列函数表示在执行参数传递过去的命令时使用PATH环境变量来查找命令
# exec*e系列函数表示在执行命令的时候读取该参数指定的环境变量作为默认的环境配置,最后的env参数必须是一个mapping对象,可以是一个dict类型的对象。
3. os.fork (只在Unix有效)
# 要想让进程同时执行多个函数,可以使用线程或者从原程序中创建一个完全分离的进程。可以通过fork创建一个新进程。这个调用复制当前进程,在系统的进程表上创建一个新表项,新表项中的许多属性与当前进程相同。但新进程有自己的数据空间、环境和文件描述符
# os.fork出一个子进程,在子进程中返回0,在父进程中返回子进程ID,如果发生错误,则抛出OSError异常
# 注意:在一些平台下如FreeBSD,Cygwin和OS/2 EMX系统中使用该函数会有问题。
4. os.wait (只在Unix有效)
# 当fork启动一个子进程时,子进程有了自己的生命周期并独立运行,有时候希望知道一个子进程何时结束,这时可以通过wait函数让父进程等待子进程运行结束
# os.wait 等待任何一个子进程结束,返回一个tuple,包括子进程的进程ID和退出状态信息:一个16位的数字,低8位是杀死该子进程的信号编号,而高8位是退出状态(如果信号编号是0),其中低8位的最高位如果被置位,则表示产生了一个core文件。
# 相关的的有os.waitpid(pid, options)
举例
os.fork
#!/usr/bin/python
#coding=utf-8
import os
def child():
print('hello from child', os.getpid(), os.getppid()) #前者获取进程id, 或者获取父进程id
os._exit(0) # 退出进程, 通常只用在fork() 产生的子进程中
def parent():
pid = os.fork() # 返回的pid在父进程中为子进程PID,在资进程中为0, 所以下面根据pid判断是哪个进程
if pid == 0:
child()
print 'fork child process error!' #如果打印该字符串,说明调用child()出错
else:
print('hello from parent', os.getpid(), pid)
parent()
我电脑上运行结果如下:
-> python test.py
('hello from parent', 1952, 1953)
('hello from child', 1953, 1952)
os.exec 和 os.system
os.execlp的测试代码如下:
#!/usr/bin/python
#coding=utf-8
import os
def main():
print "Running ps with execlp"
os.execlp("ps", 'ps', 'ax')
print "Done."
main()
运行结果:
-> python test4.py
Running ps with execlp
PID TT STAT TIME COMMAND
1 ?? Ss 0:12.43 /sbin/launchd
42 ?? Ss 0:03.05 /usr/libexec/UserEventAgent (System)
43 ?? Us 0:05.24 /usr/sbin/syslogd
...
481 s000 S+ 0:00.14 -zsh
1803 s001 Ss+ 0:00.49 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
1806 s002 Ss 0:00.10 /bin/zsh -i
2070 s002 R+ 0:00.02 ps ax
os.system的测试代码如下:
#!/usr/bin/python
#coding=utf-8
import os
def main():
print "Running ps with execlp"
os.system("ps ax")
print "Done."
main()
运行结果如下:
-> python test4.py
Running ps with execlp
PID TT STAT TIME COMMAND
1 ?? Ss 0:12.71 /sbin/launchd
42 ?? Ss 0:03.11 /usr/libexec/UserEventAgent (System)
...
481 s000 S+ 0:00.14 -zsh
1803 s001 Ss+ 0:00.50 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
1806 s002 Ss 0:00.10 /bin/zsh -i
2113 s002 S+ 0:00.02 /usr/local/Cellar/python/2.7.11/Frameworks/Python.framework/Versio
2114 s002 R+ 0:00.00 ps ax
Done.
以上两个例子对比可以看到 exec系列
的调用使得原进程接下的代码都不会运行。
os.wait
#! /usr/bin/python
#coding=utf-8
import os
import sys
def child_process():
'''child process'''
print 'child process is running'
sys.exit(0) # 常用的退出进程
def parent_process():
'''parent process'''
print 'parent process is running'
print 'waiting for child process'
exit_stat = os.wait() # 返回值是一个pid 和退出状态的元组
print "waited child process's PID = %d" % (exit_stat[0])
sys.exit(0)
def main():
'''main function'''
try:
pid = os.fork()
if pid > 0:
'''parent process'''
parent_process()
else:
child_process()
except OSError, e:
print os.strerror(e.errno)
main()
运行结果:
-> python test4.py
parent process is running
waiting for child process
child process is running
waited child process's PID = 2152
总结
本文简单介绍了系统进程相关的 os.system
, os.exec
, os.fork
, os.wait
系统调用。相关的其它调用如:
os.kill
, os.ncie
, os.popen
, os.spawn系列
可以参照官方文档。另外实际上python多进程编程更加推荐 multiprocessing
模块提供的相关功能,将在以后的文章中探究。