TerminateProcess via os.kill on Windows?

On Windows there isn’t really a SIGKILL signal, hence it not existing in signal on Windows.

Could we define SIGKILL on Windows, then internally take:

os.kill(<pid>, signal.SIGKILL)

and have that call TerminateProcess?

I know it isn’t a 100% mapping of SIGKILL → TerminateProcess but its pretty darn close. From the winapi docs:

The TerminateProcess function is used to unconditionally cause a process to exit.

vs the GNU docs:

The SIGKILL signal is used to cause immediate program termination

TLDR: I propose defining signal.SIGKILL on Windows and if it is used by os.kill, internally call TerminateProcess; after all they seem to be functionally similar.

I think there is strong precedent that functionality in os is supposed to reflect the actual functionality offered by the operating system, including mirroring its API as closely as makes sense in Python; and then platform-agnostic wrappers are put at a higher level.

Actually upon looking at the source, it seems it actually already sort of does what I was asking about: https://github.com/python/cpython/blob/main/Modules/posixmodule.c#L8915

So I guess sending a kill with any regular signal is more/less the same on Windows… Weird.

Unfortunately, Python’s implementation of os.kill() on Windows is kind of a mess.

First and foremost, the use of GenerateConsoleCtrlEvent() is completely wrong. This API function requires a console process group ID (pgid). The current implementation of GenerateConsoleCtrlEvent() in the console host (i.e. conhost.exe or openconsole.exe) is ridiculously buggy in many cases if it gets passed a pid that’s not a pgid. The only way to get a pid value that’s known to also be a pgid value is to spawn a process with the creation flag CREATE_NEW_PROCESS_GROUP. Thus os.kill() should only call GenerateConsoleCtrlEvent() when passed a negative process ID, which, as specified by POSIX, indicates that it’s a process group ID. The pid value -1 should be special cased as Windows console process group 0, which includes every process in the current console session. The pid value 0 (i.e. every process in the current process group, according to POSIX) should fail with a ValueError. It can’t be implemented on Windows since there’s no documented way to query the process group ID of the current process.

Also, when the pid value is negative, the implementation of os.kill() should map signal.SIGINT and signal.SIGBREAK to the console control events CTRL_C_EVENT and CTRL_BREAK_EVENT. Those are the only two values that should be supported in this code path. Any value other than SIGINT or SIGBREAK should fail as a ValueError. In particular, the special signal value 0 that POSIX specifies can’t be reasonably supported to test for the existence of a process group.

If the pid value is positive, then the implementation of os.kill() should take the code path that calls OpenProcess() and TerminateProcess(). This code path can allow any value for the signal number, which will be used as the exit status value of the process. To better support cross-platform code, signal.SIGKILL could be defined as the integer enum value 1. It’s common on Windows to use 1 as the exit status when forcibly terminating a process. If the signal value is 0, only OpenProcess() should be called, to test for existence and PROCESS_TERMINATE access, as specified by POSIX.

3 Likes