Python信号处理

来源:互联网 发布:淘宝话费充值店铺介绍 编辑:程序博客网 时间:2024/05/18 06:41

终止信号

通常我们认为,在try语句中,finally一定会执行。

# coding: utf-8import timeimport osimport loggingtry:    print 'start try, sleep 30s...'    print 'pid: %s' % os.getpid()    time.sleep(30)    print 'end try'except Exception, e:    print 'catch exception'    logging.exception(e)except KeyboardInterrupt, e:    logging.exception(e)finally:    print 'oh, finally'

上面这段程序,用Ctrl-c来终止程序时,可以捕获到一个KeyboardInterrupt,finally也会执行。

start try, sleep 30s...pid: 9900oh, finallyERROR:root:Traceback (most recent call last):  File "/Users/bowen/python/learn-python/exceptions/finally_test.py", line 9, in <module>    time.sleep(30)KeyboardInterrupt

但是如果在命令行,采用 kill -9 PID方式终止程序,程序会强制退出,finally后面的代码段也不会执行。

那么两种情况不同是什么原因呢?
本质区别在于python进程对于信号的处理。信号(signal)是进程之间通讯的方式,是一种软件中断。一个进程一旦接收到信号就会打断原来的程序执行流程来处理信号。
几个常用信号:

SIGINT    终止进程  中断进程  (control+c)SIGTERM   终止进程  软件终止信号SIGKILL   终止进程  杀死进程SIGALRM   闹钟信号SIGCHLD   子进程终止向父进程发送的信号

control+c会发送SIGINT信号,并产生KeyboardInterrupt异常。kill -9会发送SIGKILL信号。SIGTERM和SIGKILL都是进程结束信号。

SIGKILL信号是无法在程序内部捕获的,一旦发送SIGKILL信号给进程,Linux就将进程停止在那里。Python自己并不检查SIGKILL,而是直接把底层标准C的运行时错误返回。在Python代码中,SIGKILL无法捕捉,而且无法忽略。一般关于sigkill的日志在/var/log/messages里,如果非deamon程序在终端也是有日志体现的。

SIGTERM比较友好,进程能捕捉这个信号,根据需要来关闭程序。在关闭程序之前,可以结束打开的记录文件和完成正在做的任务。在某些情况下,假如进程正在进行作业而且不能中断,那么进程可以忽略这个SIGTERM信号。

发送信号一般有两种原因:
1. (被动式) 内核检测到一个系统事件。例如子进程退出会像父进程发送SIGCHLD信号.键盘按下control+c会发送SIGINT信号。
2. (主动式) 通过系统调用kill来向指定进程发送信号。

python提供的信号:

>>> import signal>>> dir(signal)['NSIG', 'SIGABRT', 'SIGALRM', 'SIGBUS', 'SIGCHLD', 'SIGCLD', 'SIGCONT', 'SIGFPE', 'SIGHUP', 'SIGILL', 'SIGINT', 'SIGIO', 'SIGIOT', 'SIGKILL', 'SIGPIPE', 'SIGPOLL', 'SIGPROF', 'SIGPWR', 'SIGQUIT', 'SIGRTMAX', 'SIGRTMIN', 'SIGSEGV', 'SIGSTOP', 'SIGSYS', 'SIGTERM', 'SIGTRAP', 'SIGTSTP', 'SIGTTIN', 'SIGTTOU', 'SIGURG', 'SIGUSR1', 'SIGUSR2', 'SIGVTALRM', 'SIGWINCH', 'SIGXCPU', 'SIGXFSZ', 'SIG_DFL', 'SIG_IGN', '__doc__', '__name__', 'alarm', 'default_int_handler', 'getsignal', 'pause', 'signal']

操作系统规定了进程收到信号以后的默认行为。但是,我们可以通过绑定信号处理函数来修改进程收到信号以后的行为,有两个信号是不可更改的:SIGTOP和SIGKILL。

处理信号

子进程向父进程发送一个signal.SIGTERM信号的例子。

# coding: utf-8import osimport signalimport timedef catch_signal(a, b):    print 'catch signal, %s' % os.getpid()signal.signal(signal.SIGTERM, catch_signal)try:    pid = os.fork()    if pid == 0:        print 'child process, %s' % os.getpid()        time.sleep(1)        # Kill a process with a signal.        os.kill(os.getppid(), signal.SIGTERM)    else:        print 'parent process, %s' % os.getpid()        print 'wait child ...'        os.wait()except Exception, e:    print e    passprint 'end %s' % os.getpid()

运行结果:

parent process, 14901wait child ...child process, 14902end 14902catch signal, 14901[Errno 4] Interrupted system callend 14901

注意

如果一个进程收到一个SIGUSR1信号,然后执行信号绑定函数,第二个SIGUSR2信号又来了,第一个信号没有被处理完毕的话,第二个信号就会丢弃。所以,尽量不要在多线程中使用信号。

0 0
原创粉丝点击