离线下载
PDF版 ePub版

qyuhen · 更新于 2018-11-28 11:00:43

进程通信

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

官方建议用 subprocess 代替 os.system、os.spawn、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,将清除定时器。

上一篇: 操作系统 下一篇: 程序框架