日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

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

發布時間:2023/12/19 python 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python兼职平台信号处理_如何在Windows机器上处理python中的信号 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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中的信号的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。