Python的os.kill在Windows上包含了两个不相关的API.当sig参数为CTRL_C_EVENT或CTRL_BREAK_EVENT时,它会调用GenerateConsoleCtrlEvent.在这种情况下,pid参数是进程组ID.如果后一个调用失败,并且对于所有其他sig值,则调用OpenProcess然后调用TerminateProcess.在这种情况下,pid参数是进程ID,sig值作为退出代码传递.终止Windows进程类似于将SIGKILL发送到POSIX进程.通常应该避免这种情况,因为它不允许过程干净地退出.

请注意,os.kill的文档错误地声称“kill()还会使进程句柄被杀死”,这从来都不是真的.它调用OpenProcess来获取进程句柄.

对于跨平台代码,使用WinAPI CTRL_C_EVENT和CTRL_BREAK_EVENT而不是SIGINT和SIGBREAK的决定是不幸的.它还没有定义GenerateConsoleCtrlEvent在传递不是进程组ID的进程ID时所执行的操作.在采用进程ID的API中使用此函数充其量是可疑的,并且可能非常错误.

根据您的特殊需求,您可以编写一个适配器函数,使os.kill对于跨平台代码更加友好.例如:

import os

import sys

import time

import signal

if sys.platform != 'win32':

kill = os.kill

sleep = time.sleep

else:

# adapt the conflated API on Windows.

import threading

sigmap = {signal.SIGINT: signal.CTRL_C_EVENT,

signal.SIGBREAK: signal.CTRL_BREAK_EVENT}

def kill(pid, signum):

if signum in sigmap and pid == os.getpid():

# we don't know if the current process is a

# process group leader, so just broadcast

# to all processes attached to this console.

pid = 0

thread = threading.current_thread()

handler = signal.getsignal(signum)

# work around the synchronization problem when calling

# kill from the main thread.

if (signum in sigmap and

thread.name == 'MainThread' and

callable(handler) and

pid == 0):

event = threading.Event()

def handler_set_event(signum, frame):

event.set()

return handler(signum, frame)

signal.signal(signum, handler_set_event)

try:

os.kill(pid, sigmap[signum])

# busy wait because we can't block in the main

# thread, else the signal handler can't execute.

while not event.is_set():

pass

finally:

signal.signal(signum, handler)

else:

os.kill(pid, sigmap.get(signum, signum))

if sys.version_info[0] > 2:

sleep = time.sleep

else:

import errno

# If the signal handler doesn't raise an exception,

# time.sleep in Python 2 raises an EINTR IOError, but

# Python 3 just resumes the sleep.

def sleep(interval):

'''sleep that ignores EINTR in 2.x on Windows'''

while True:

try:

t = time.time()

time.sleep(interval)

except IOError as e:

if e.errno != errno.EINTR:

raise

interval -= time.time() - t

if interval <= 0:

break

def func(signum, frame):

# note: don't print in a signal handler.

global g_sigint

g_sigint = True

#raise KeyboardInterrupt

signal.signal(signal.SIGINT, func)

g_kill = False

while True:

g_sigint = False

g_kill = not g_kill

print('Running [%d]' % os.getpid())

sleep(2)

if g_kill:

kill(os.getpid(), signal.SIGINT)

if g_sigint:

print('SIGINT')

else:

print('No SIGINT')

讨论

Windows不在系统级[*]实现信号. Microsoft的C运行时实现了标准C所需的六个信号:SIGINT,SIGABRT,SIGTERM,SIGSEGV,SIGILL和SIGFPE.

SIGABRT和SIGTERM仅针对当前流程实施.您可以通过C raise调用处理程序.例如(在Python 3.5中):

>>> import signal, ctypes

>>> ucrtbase = ctypes.CDLL('ucrtbase')

>>> c_raise = ucrtbase['raise']

>>> foo = lambda *a: print('foo')

>>> signal.signal(signal.SIGTERM, foo)

>>> c_raise(signal.SIGTERM)

foo

0

SIGTERM没用.

使用信号模块也无法对SIGABRT做很多事情,因为abort函数会在处理程序返回时终止进程,这会在使用信号模块的内部处理程序时立即发生(它会触发已注册的Python可调用的标记)主线).对于Python 3,您可以改为使用faulthandler模块.或者通过ctypes调用CRT的signal函数来设置ctypes回调作为处理程序.

CRT通过为相应的Windows异常设置Windows structured exception handler来实现SIGSEGV,SIGILL和SIGFPE:

STATUS_ACCESS_VIOLATION SIGSEGV

STATUS_ILLEGAL_INSTRUCTION SIGILL

STATUS_PRIVILEGED_INSTRUCTION SIGILL

STATUS_FLOAT_DENORMAL_OPERAND SIGFPE

STATUS_FLOAT_DIVIDE_BY_ZERO SIGFPE

STATUS_FLOAT_INEXACT_RESULT SIGFPE

STATUS_FLOAT_INVALID_OPERATION SIGFPE

STATUS_FLOAT_OVERFLOW SIGFPE

STATUS_FLOAT_STACK_CHECK SIGFPE

STATUS_FLOAT_UNDERFLOW SIGFPE

STATUS_FLOAT_MULTIPLE_FAULTS SIGFPE

STATUS_FLOAT_MULTIPLE_TRAPS SIGFPE

CRT对这些信号的实现与Python的信号处理不兼容.异常过滤器调用已注册的处理程序,然后返回EXCEPTION_CONTINUE_EXECUTION.但是,Python的处理程序只会为解释器跳转一个标志,以便稍后在主线程中调用已注册的可调用对象.因此,触发异常的错误代码将继续在无限循环中触发.在Python 3中,您可以使用faulthandler模??块来处理这些基于异常的信号.

这就留下了SIGINT,Windows添加了非标准的SIGBREAK.控制台和非控制台进程都可以引发这些信号,但只有控制台进程可以从另一个进程接收它们. CRT通过SetConsoleCtrlHandler注册控制台控制事件处理程序来实现这一点.

控制台通过在附加进程中创建一个新线程来发送控制事件,该进程在kernel32.dll或kernelbase.dll(未记录)中的CtrlRoutine处开始执行.处理程序不在主线程上执行会导致同步问题(例如在REPL中或使用输入).此外,如果在等待同步对象或等待同步I / O完成时被阻塞,则控制事件不会中断主线程.如果SIGINT可以中断,则需要注意避免主线程中的阻塞. Python 3尝试通过使用Windows事件对象解决此问题,该事件对象也可用于应该可被SIGINT中断的等待.

当控制台向进程发送CTRL_C_EVENT或CTRL_BREAK_EVENT时,CRT的处理程序分别调用已注册的SIGINT或SIGBREAK处理程序.还会为控制台在窗口关闭时发送的CTRL_CLOSE_EVENT调用SIGBREAK处理程序. Python默认通过在主线程中运行KeyboardInterrupt来处理SIGINT.但是,SIGBREAK最初是默认的CTRL_BREAK_EVENT处理程序,它调用ExitProcess(STATUS_CONTROL_C_EXIT).

您可以通过GenerateConsoleCtrlEvent将控制事件发送到连接到当前控制台的所有进程.这可以定位属于进程组的进程子集,或目标组0,以将事件发送到连接到当前控制台的所有进程.

进程组不是Windows API的详细记录方面.查询进程组没有公共API,但Windows会话中的每个进程都属于进程组,即使它只是wininit.exe组(服务会话)或winlogon.exe组(交互式会话).通过在创建新进程时传递创建标志CREATE_NEW_PROCESS_GROUP来创建新组.组ID是已创建进程的进程ID.据我所知,控制台是唯一使用进程组的系统,而且只适用于GenerateConsoleCtrlEvent.

当目标ID不是进程组ID时,控制台所执行的操作是未定义的,不应依赖它.如果进程及其父进程都附加到控制台,则向其发送控制事件基本上就像目标是组0一样.如果父进程未附加到当前控制台,则GenerateConsoleCtrlEvent失败,并且os.kill调用TerminateProcess.奇怪的是,如果您定位“系统”进程(PID 4)及其子进程smss.exe(会话管理器),则调用成功但没有任何反应,除了目标被错误地添加到附加进程列表(即GetConsoleProcessList).这可能是因为父进程是“空闲”进程,由于它是PID 0,因此被隐式接受为广播PGID.父进程规则也适用于非控制台进程.定位非控制台子进程不会做任何事情 – 除了通过添加未附加的进程错误地破坏控制台进程列表.我希望您应该只将控制事件发送到组0或通过CREATE_NEW_PROCESS_GROUP创建的已知进程组.

不要依赖于能够将CTRL_C_EVENT发送到除组0之外的任何内容,因为它最初在新进程组中被禁用.将此事件发送到新组并非不可能,但目标进程首先必须通过调用SetConsoleCtrlHandler(NULL,FALSE)来启用CTRL_C_EVENT.

CTRL_BREAK_EVENT是您可以依赖的所有内容,因为它无法禁用.发送此事件是一种优雅地杀死使用CREATE_NEW_PROCESS_GROUP启动的子进程的简单方法,假设它具有Windows CTRL_BREAK_EVENT或C SIGBREAK处理程序.如果没有,默认处理程序将终止进程,将退出代码设置为STATUS_CONTROL_C_EXIT.例如:

>>> import os, signal, subprocess

>>> p = subprocess.Popen('python.exe',

... stdin=subprocess.PIPE,

... creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)

>>> os.kill(p.pid, signal.CTRL_BREAK_EVENT)

>>> STATUS_CONTROL_C_EXIT = 0xC000013A

>>> p.wait() == STATUS_CONTROL_C_EXIT

True

请注意,CTRL_BREAK_EVENT未发送到当前进程,因为该示例以子进程的进程组为目标(包括连接到控制台的所有子进程,依此类推).如果示例使用了组0,那么当前进程也会被杀死,因为我没有定义SIGBREAK处理程序.让我们尝试一下,但设置一个处理程序:

>>> ctrl_break = lambda *a: print('^BREAK')

>>> signal.signal(signal.SIGBREAK, ctrl_break)

>>> os.kill(0, signal.CTRL_BREAK_EVENT)

^BREAK

[*]

Windows有asynchronous procedure calls(APC)将目标函数排入线程.有关Windows APC的深入分析,请参阅文章Inside NT’s Asynchronous Procedure Call,尤其是阐明内核模式APC的作用.您可以通过QueueUserAPC将用户模式APC排队到一个线程.它们也会在ReadFileEx和WriteFileEx排队等待I / O完成例程.

当线程进入可警告等待时(例如,WaitForSingleObjectEx或SleepEx,其中bAlertable为TRUE),执行用户模式APC.另一方面,内核模式APC立即被调度(当IRQL低于APC_LEVEL时).它们通常由I / O管理器用于在发出请求的线程的上下文中完成异步I / O请求包(例如,将数据从IRP复制到用户模式缓冲区).有关显示APC如何影响可警告和不可警报等待的表,请参阅Waits and APCs.请注意,内核模式APC不会中断等待,而是由等待例程在内部执行.

Windows可以使用APC实现类似POSIX的信号,但实际上它使用其他方法来实现相同的目的.例如:

窗口消息可以发送并发布到共享调用thread’s desktop且处于相同或更低完整性级别的所有线程.当线程调用PeekMessage或GetMessage时,发送窗口消息会将其置于系统队列中以调用窗口过程.发布消息会将其添加到线程的消息队列中,该消息队列的默认配额为10,000条消息.具有消息队列的线程应具有消息循环以通过GetMessage和DispatchMessage处理队列.仅控制台进程中的线程通常没有消息队列.但是,控制台主机进程conhost.exe显然可以.单击关闭按钮时,或者通过任务管理器或taskkill.exe终止控制台的主进程时,WM_CLOSE消息将发布到控制台窗口的线程的消息队列中.控制台轮流向其所有连接的进程发送CTRL_CLOSE_EVENT.如果进程处理该事件,则在强制终止之前,它会在5秒内正常退出.

python兼职平台信号处理_如何在Windows机器上处理python中的信号相关推荐

  1. python安装numpy模块教程_如何在windows机器上安装python 3.6的matplotlib和numpy模块?...

    如果你在python(3.6)命令提示符Windows上执行以下命令时收到无效的Systemax错误:import matplotlib import scipy import numpy 在浏览器下 ...

  2. python兼职平台信号处理_基于Python的数字信号处理初步

    作者:许欢 来源:EETOP 行者无疆(论坛usrname:ICNO.1) 的博客 Python 是目前的热门语言,一直觉得掌握一门编程语言对作为搞技术的来说还是很有必要的,结合工作中能用到的一些数据 ...

  3. vscode重置应用程序_如何在Windows 10上重置应用程序的数据

    vscode重置应用程序 With Windows 10's Anniversary Update, you can now reset an app's data without actually ...

  4. skype自动回复_如何在Windows 10上阻止Skype自动启动

    skype自动回复 Microsoft 微软 The Skype app included with Windows 10 now has a notification area icon. That ...

  5. python 管理windows客户端_在远程windows客户端上执行python脚本

    我正在使用paramiko在远程windows服务器上执行命令.我能够执行dir之类的命令并提取输出,但是执行python脚本似乎失败了.不会引发错误消息.在 下面是我的代码片段:def ssh_co ...

  6. ffmpeg添加到环境变量_如何在Windows 10上下载和安装FFmpeg

    如何在Windows 10上下载和安装FFmpeg FFmpeg是一种流行的开源工具,用于对音频和视频文件执行多项操作.这些操作包括多路复用,解复用,编码,解码,过滤,流式传输等. 它还用于缩放和旋转 ...

  7. 如何在Windows 10上安装Python

    Installing and using Python on Windows 10 is very simple. The installation procedure involves just t ...

  8. shell bash脚本_如何在Windows 10上创建和运行Bash Shell脚本

    shell bash脚本 With the arrival of Windows 10's Bash shell, you can now create and run Bash shell scri ...

  9. python兼职平台信号处理_Python模块之信号(signal)

    在了解了Linux的信号基础之 后,Python标准库中的signal包就很容易学习和理解.signal包负责在Python程序内部处理信号,典型的操作包括预设信号处理函数,暂 停并等待信号,以及定时 ...

最新文章

  1. vmware 共享文件夹(win10下的vmware安装了centos7)
  2. nginx视频直播/点播服务干货分享
  3. MySQL数据库设计总结
  4. XGBoost相关知识-1
  5. 美团配送资金安全治理之对账体系建设
  6. 中科微研携手-农业大健康·李喜贵:从玉农业谋定功能性农业
  7. mysql8.0.22 win7_ArcGIS10.8地理信息软件中英文版安装教程
  8. Windows 系统管理
  9. php日期时间代码,PHP日期计算
  10. php父子遍历,php无限分类父子追溯方法
  11. 微信开发者工具 wxmi修改模版颜色_网站建设公司讲解:微信小程序的开发者工具界面...
  12. js 对象的_proto_属性 和函数的prototype属性分析
  13. 如何通过a/a中的href刷新当前界面
  14. 四年级计算机考试反思,四年级期中考试总结反思三篇
  15. 在Windows本地安装ElasticSearch和Kibana
  16. 帝国cms ajax,帝国CMS注册体验加强 ajax检测用户名和密码
  17. GoLang - Go中Mocking(3)
  18. Python 爬取微信公众号文章
  19. FileZilla文件传输失败问题
  20. 游戏多开计算机内存不足,技术宅 解决天刀多开单开内存不足问题

热门文章

  1. axure怎么做5秒倒计时_装修隔音怎么做,进屋秒变“静音”模式
  2. 【PDF下载】无意中发现的另三本统计学入门好书
  3. SAP UI5 应用开发教程之四十七 - 如何自定义 SAP UI5 字符串类型输入字段的校验逻辑
  4. SAP Spartacus 服务器端渲染处理内存泄漏的准则
  5. SAP Spartacus user role页面的checkbox设计明细介绍
  6. Angular async pipe在Component html模板中的一个实际应用
  7. 如何判断一个SAP CRM UI能否被扩展
  8. 使用SAP OData offline库实现Android应用的离线(offline)模式
  9. why my SAP CRM One Order custom callback is not called
  10. view detail data in gateway error log