进程通信
subprocess
执行程序,获取返回码或输出信息。
- call: 返回 ExitCode。
- check_call: 如果 ExitCode = 0,抛出 CalledProcessError 异常。
- check_output: 返回输出信息。ExitCode = 0 抛出异常。
命令行参数可以用 shlex.split 分解成列表。
>>> from subprocess import *
>>> from shlex import split
>>> s = check_output(split("ls -l"))
>>> print s
total 0
drwx------+ 4 yuhen staff 136 1 11 07:40 Desktop
drwx------+ 10 yuhen staff 340 1 4 01:53 Documents
drwx------+ 4 yuhen staff 136 1 11 08:35 Downloads
drwx------@ 56 yuhen staff 1904 1 11 08:28 Library
drwx------+ 3 yuhen staff 102 9 22 15:20 Movies
drwx------+ 5 yuhen staff 170 1 9 19:37 Music
drwx------+ 5 yuhen staff 170 1 3 21:14 Pictures
drwxr-xr-x+ 4 yuhen staff 136 9 15 16:21 Public
如果需要获取 ExitCode,又不想看到输出信息。可以将 stdout 重定位到 /dev/null。
>>> null = open(os.devnull, "w")
>>> call(split("ls -l"), stdout = null, stderr = null)
0
、os.popen、popen2.、commands.这个传统用法。基于以后向 Python 3 迁移的需要,还是放弃所有打上 obsolete 标记的库。
>>> from subprocess import Popen, PIPE
>>> Popen('find . -name "*.py" | xargs ls -l', shell=True).wait()
-rwxr-xr-x 1 yuhen staff 286 5 29 19:24 ./main.py
-rw-r--r-- 1 yuhen staff 76 6 7 17:49 ./test.py
可以用 PIPE 改变输入输出对象。
>>> p = Popen('find . -name "*.py" | xargs ls -l', shell=True, stdout=PIPE)
>>> p.pid
71474
>>> print p.stdout.read()
-rwxr-xr-x 1 yuhen staff 286 5 29 19:24 ./main.py
-rw-r--r-- 1 yuhen staff 76 6 7 17:49 ./test.py
>>> p.wait()
0
除使用简便函数外,还可以创建 Popen 对象以获取更细节的控制。subprocess 不能控制终端和 TTY 交互程序,建议使用第三方库 Fabric 或 pexpect。进程信息可以用 psutil 获取。
22.2 signal
信号是软中断,提供了一种异步事件通知机制。Python 默认已经安装了一些信号处理器,比如 SIGPIPE 被忽略,SIGINT 引发 KeyboardInterrupt 异常,捕获 SIGTERM 调用退出函数。
常用信号
- SIGINT: 用户中断 (ctrl + c)。
- SIGTERM: 由 kill() 发送,进程终止。
- SIGCHLD: 子进程终止。
- SIGHUP: 终端会话终止。
- SIGSTP: 进程暂停 (ctrl + z)。
- SIGALRM: 告警。
注意: 信号 SIGKILL、SIGSTOP 不能被捕获。
signal
仅能在主线程调用 signal() 注册信号处理器函数,它会移除当前处理动作。可用 getsignal() 获取,在需要时重新注册。有两个特殊的处理器:SIG_IGN 忽略信号,SIG_DFL 默认处理。
试着用 SIGINT 代替 KeyboardInterrupt 异常来处理用户中断。
from signal import *
from time import time, sleep
def sig_handler(signum, frame):
print "exit"
exit(0)
def main():
signal(SIGINT, sig_handler)
while True:
sleep(1)
print time()
if __name__ == "__main__":
main()
输出:
$ ./main.py
1357987332.33
1357987333.33
1357987334.33
^Cexit
中断信号被拦截,我们可以自主决定是否终止进程。在 GDB 里,用 SIGINT 来处理调试中断。也有一些软件用 SIGUSR1、SIGUSR2 作为外部通知事件,比如重启什么的。信号处理会被带入 fork()创建的子进程。
pause
函数 pause() 会使进程休眠,直到进程接收到信号。信号要么被处理,要么终止进程。
def sig_handler(signum, frame):
print "sig:", signum
def main():
signal(SIGUSR1, sig_handler)
while True:
print time()
pause()
如果收到 SIGUSR1 信号,则进程苏醒后显示时间,然后再次休眠。如是其他信号,进程终止。
alarm
在 n 秒后发送一个 SIGALRM 告警信号。或用 0 秒取消所有尚未到期的告警。
signal(SIGALRM, sig_alarm) # 捕获信号
alarm(2) # 2 秒后发送告警信号。仅一次。
timer
用来设置在 seconds 秒后发出信号,并在此以后每隔 interval 秒重复发出信号。参数 which 决定了发出何种信号。
- ITIMER_REAL: SIGALRM
- ITIMER_VIRTUAL: SIGVTALRM
- ITIMER_PROF: SIGPROF
signal(SIGALRM, sig_alarm)
setitimer(ITIMER_REAL, 2, 5) # 2 秒后首次发出信号,随后每隔 5 秒发一次。
将 seconds 设置为 0,将清除定时器。
多进程( multi-process)
多线程(multi-thread)
1. import multiprocessing
2.
3. def thread_func():
4. print "thread in"
5. while True:
6. pass
7.
8. if __name__ == "__main__":
9. t1 = multiprocessing.Process(target = thread_func)
10. t1.start()
11.
12. t2 = multiprocessing.Process(target = thread_func)
13. t2.start()
14.
15. t1.join()
16. t2.join()
1. from threading import Thread
2.
3. def thread_func():
4. print "thread in"
5. while True:
6. pass
7.
8. if __name__ == "__main__":
9. t1 = Thread(target = thread_func)
10. t1.start()
11.
12. t2 = Thread(target = thread_func)
13. t2.start()
14.
15. t1.join()
16. t2.join()