一、问题描述

在用Python爬取网络视频时,利用了ffmpeg下载并合并m3u8文件,在CMD上运行结果如下:

C:\Users\fz.000>ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"
ffmpeg version N-91646-g78d4b6bd43 Copyright (c) 2000-2018 the FFmpeg developersbuilt with gcc 8.2.1 (GCC) 20180813configuration: --enable-gpl --enable-version3 --enable-sdl2 --enable-fontconfig --enable-gnutls --enable-iconv --enable-libass --enable-libbluray --enable-libfreetype --enable-libmp3lame --enable-libopencore-amrnb --enable-libopencore-amrwb --enable-libopenjpeg --enable-libopus --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libtheora --enable-libtwolame --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxml2 --enable-libzimg --enable-lzma --enable-zlib --enable-gmp --enable-libvidstab --enable-libvorbis --enable-libvo-amrwbenc --enable-libmysofa --enable-libspeex --enable-libxvid --enable-libaom --enable-libmfx --enable-amf --enable-ffnvcodec --enable-cuvid --enable-d3d11va --enable-nvenc --enable-nvdec --enable-dxva2 --enable-avisynthlibavutil      56. 19.100 / 56. 19.100libavcodec     58. 23.100 / 58. 23.100libavformat    58. 17.103 / 58. 17.103libavdevice    58.  4.101 / 58.  4.101libavfilter     7. 26.100 /  7. 26.100libswscale      5.  2.100 /  5.  2.100libswresample   3.  2.100 /  3.  2.100libpostproc    55.  2.100 / 55.  2.100
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00001.ts?auth_key=1535101557-0-0-e6986ea2f49dd0a3ad5954823a79b3d0' for reading
[hls,applehttp @ 00000263fedba840] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00002.ts?auth_key=1535101557-0-0-1f6b4fb3db9df8067d972990c71a74cb' for reading
Input #0, hls,applehttp, from 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0':Duration: 00:00:59.12, start: 1.433556, bitrate: N/AProgram 0Metadata:variant_bitrate : 0Stream #0:0: Video: h264 (High) ([27][0][0][0] / 0x001B), yuv420p, 1280x2276, 25 fps, 25 tbr, 90k tbn, 50 tbcMetadata:variant_bitrate : 0Stream #0:1: Audio: aac (LC) ([15][0][0][0] / 0x000F), 44100 Hz, stereo, fltpMetadata:variant_bitrate : 0
Output #0, mp4, to 'E:/PycharmProjects/Video_Crack/video/video1.mp4':Metadata:encoder         : Lavf58.17.103Stream #0:0: Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x2276, q=2-31, 25 fps, 25 tbr, 90k tbn, 90k tbcMetadata:variant_bitrate : 0Stream #0:1: Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltpMetadata:variant_bitrate : 0
Stream mapping:Stream #0:0 -> #0:0 (copy)Stream #0:1 -> #0:1 (copy)
Press [q] to stop, [?] for help
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00003.ts?auth_key=1535101557-0-0-91103599f3d036c53025d4469811f47f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00004.ts?auth_key=1535101557-0-0-5425a38189bbdc37cce8e75c99ab8bc0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00005.ts?auth_key=1535101557-0-0-9b2cfb83e2058f2904be5b9fc3cd2665' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00006.ts?auth_key=1535101557-0-0-7c76c5b983dec9efe285a19beafe7fa4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00007.ts?auth_key=1535101557-0-0-973c55d77a4dc646c11d79aeba2bac4f' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00008.ts?auth_key=1535101557-0-0-76f76084ff3e5bd058aa7e00336b07d4' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00009.ts?auth_key=1535101557-0-0-82b119c4d5e5e756c4449e5870e18f00' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00010.ts?auth_key=1535101557-0-0-3530886b5ebacb68c63dca9b1b0d31af' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00011.ts?auth_key=1535101557-0-0-22cba48926d6bfedd02711f7da42a577' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00012.ts?auth_key=1535101557-0-0-af00d2111cf04e0d1d81f6523cb3d2ab' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00013.ts?auth_key=1535101557-0-0-64bd48246a8ba362cedfff4a78d1ebfa' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00014.ts?auth_key=1535101557-0-0-b82190c2c1a4ddebeea337688c1a351e' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00015.ts?auth_key=1535101557-0-0-72da7559f7fbe2ec24f30ef0be2cbc50' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00016.ts?auth_key=1535101557-0-0-7c93a14b6c6c10b09d24b30ea4bf7db3' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00017.ts?auth_key=1535101557-0-0-a7ae4f217482020d64e85c2e78704cd2' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00018.ts?auth_key=1535101557-0-0-f74f9fb587e0b96e1a1a053bcb2e511d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00019.ts?auth_key=1535101557-0-0-21fcb34bb2c06fb165ed29083d278014' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00020.ts?auth_key=1535101557-0-0-36b552cde620d564fe469a5d7138111a' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00021.ts?auth_key=1535101557-0-0-fd86d0f8d8e7815f3da90626f4855233' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00022.ts?auth_key=1535101557-0-0-6cfd887896c6292abb643c8fc6d05535' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00023.ts?auth_key=1535101557-0-0-513a1071888bd474984b9b37345b997c' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00024.ts?auth_key=1535101557-0-0-126f976c659bf9bc55eacb6dfdb0f1e0' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00025.ts?auth_key=1535101557-0-0-1a990e2b3bbb8167c9dc1c3ae4fe644b' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00026.ts?auth_key=1535101557-0-0-9fa465a24d3329a415484e286fbd252d' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00027.ts?auth_key=1535101557-0-0-6cb593e7c453f0d981e3ddc7baff6ace' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00028.ts?auth_key=1535101557-0-0-fbdf9f5f84c52e39c5d973e53eb01f75' for reading
[https @ 00000263fedc0080] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00029.ts?auth_key=1535101557-0-0-95cf329432d5c39f98c8a7554194477e' for reading
[https @ 00000263fee04180] Opening 'https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone-00030.ts?auth_key=1535101557-0-0-a601529344546d2b3733e2e6454b475c' for reading
frame= 1479 fps= 58 q=-1.0 Lsize=   15220kB time=00:00:59.11 bitrate=2109.0kbits/s speed=2.33x
video:14263kB audio:924kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: 0.210394%C:\Users\fz.000>

运行结束后,在相应的目录下就会有下载好的视频。

运行命令后的结果中有这么一句:

也就是说下载过程中按下“q”键会终止下载。用同样的视频m3u8链接尝试了一下,果然还没下载完就立刻停止了,从文件的大小就可以看出来,是比原来要小的,不过这时的视频仍然能够正常播放(这就是后面我们要的效果)。

下面我们不用CMD,而是用python程序实现这个过程:

该程序使用subprocess模块创建并返回一个子进程,并在这个子进程中执行指定的程序(也就是ffmpeg命令)。同时可以进行进程间通信,将子程序的输出信息(也就是采用命令行时的那一大片输出信息)打印出来。

subprocess模块用法的相关用法参见:https://blog.csdn.net/polyhedronx/article/details/82015271

import shlex
import subprocessif __name__ == '__main__':try:shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535101557-0-0-fedf56c648bfe0d60ef8e0aa89b6a297&expiration=1535101557&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'cmd = shlex.split(shell_cmd)p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)while p.poll() is None:line = p.stdout.readline().decode('gbk').strip()print(line)if p.returncode == 0:print('subprocess success')else:print('subprocess failed with code', p.returncode)except (OSError, ValueError):print('stop here')

程序写好之后,我们仍然不满足,想要把它做成一个图形界面,其中需要有三个按钮,“下载”、“停止”和“退出”,如下图。“下载”和“退出”都比较容易,问题就是这个停止按钮,怎么样才能实现按下“停止”按钮视频停止下载,并且已经下载好的部分还能正常播放呢?也就是说我们要写一个函数,按下“停止”按钮会启动该函数,而该函数的功能就是停止视频下载(并且保证已下载的部分能正常播放)。

二、初步尝试

首先,我们很自然的会想到,可以向子进程发送一个“q”,实现CMD中按下“q”的效果,而且查一下资料,的确有向subprocess子进程发送信息的命令:

Popen.communicate(input='something', timeout=None)# orPopen.stdin.write('something')

但是...这其实是行不通的,因为这样发送给子进程的是数据而不是命令,效果就像是在CMD上打印了一个“q”,并不能使视频下载停止。

好吧,我们不要放弃,那有没有向子进程发送信号的命令呢?答案是有的:

Popen.send_signal(signal)  # 向子进程发送signal信号Popen.terminate()  # 终止(stop)子进程Popen.kill()  # 杀死子进程。在Windows上,kill()和terminate()作用相同

其中,signal信号的相关知识参见:https://blog.csdn.net/polyhedronx/article/details/81989918

通过上面这篇博客,我们知道在Windows中还有两种方法可以分别向进程和进程组发送信号:

os.kill(pid, sid)os.killpg(pgid, sid)

其中,pid为进程号,比如“Popen.pid”,sid为要发送的信号。Windows中可以使用的信号是有限制的,详见:https://blog.csdn.net/polyhedronx/article/details/81988335

下面两种方法的效果是一样的:

Popen.send_signal(signal.SIGTERM)os.kill(Popen.pid, signal.SIGTERM)

有了这么多可以用的命令,我们先来尝试一下 terminate() 和 kill() :

import shlex
import subprocessif __name__ == '__main__':try:shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'cmd = shlex.split(shell_cmd)p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)count = 0while p.poll() is None:line = p.stdout.readline().decode('gbk').strip()print(line)count += 1if count == 50:p.terminate()# p.kill()if p.returncode == 0:print('subprocess success')else:print('subprocess failed with code', p.returncode)except (OSError, ValueError, KeyboardInterrupt):print('stop here')

上面程序的意思是打印出50条语句时终止子进程,因为下载完成的话CMD中打印的语句肯定大于50条,也就是我们在它还没有下载完成时就把进程终止了:

从图中可以看出,视频确实没有下载完成,因为下载完成之后视频会有15220KB,这样好像是已经满足我们的要求了,因为我们只要在按下“停止”按钮触发的函数里把子进程kill掉就好了。

但是!!!这个视频是无法正常播放的。。。

也即是说terminate() 和 kill()虽然可以终止视频下载,但也破坏了下载过程,使得下载好的部分无法播放。

三、出现转机

terminate() 和 kill()尝试失败后,现在我们只有下面两条命令可以用了:

Popen.send_signal(signal)os.kill(Popen.pid, signal)

希望全部寄托在了Windows可用的signal上,通过这篇文章python-subprocess模块用法了解到我们可用下面几种signal:

# SIGTERM信号
Popen.send_signal(signal.SIGTERM)
os.kill(Popen.pid, signal.SIGTERM)# CTRL_C信号
Popen.send_signal(signal.CTRL_C_EVENT)
os.kill(Popen.pid, signal.CTRL_C_EVENT)# CTRL_BREAK信号
Popen.send_signal(signal.CTRL_BREAK_EVENT)
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)

此外,需要注意的是,对于 os.kill(pidsig) ,在python文档中有如下描述:

Windows:signal.CTRL_C_EVENT和signal.CTRL_BREAK_EVENT信号是特殊信号,只能发送到共享公共控制台窗口的控制台进程,例如某些子进程(subprocesses)。 sig的任何其他值都将导致进程被TerminateProcess API无条件地终止,并且退出代码将被设置为sig。 Windows版本的 kill() 还会占用进程句柄。

原文:https://docs.python.org/3.6/library/os.html

对于signal.CTRL_C_EVENT(signal.CTRL_BREAK_EVENT与此类似)python文档有如下描述:

The signal corresponding to the Ctrl+C keystroke event. This signal can only be used with os.kill().

Availability: Windows.

New in version 3.2.

原文:https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT

也就是说 signal.CTRL_C_EVENT 和 signal.CTRL_BREAK_EVENT 只能用于os.kill()。此外,由于在 Windows 上,SIGTERM和terminate() 的作用相同,所以我们弃用该方法。综上,只剩下了两种方法:

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)# CTRL_BREAK信号
os.kill(Popen.pid, signal.CTRL_BREAK_EVENT)

看到CTRL+C,我们好像明白了什么,这不就是中断程序的命令吗!

先在命令行试一下,下载过程中按下CTRL+C,程序终止,视频也下了一半,最重要的是,视频是可以正常播放的!

这说明程序运行的过程中,我们只要给子进程发送一个Ctrl+C信号,就可以完美达到我们的目的,而Ctrl+C信号也正是我们有的:

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)

二话不说,立马在python程序中试一试:

import os
import shlex
import signal
import subprocessif __name__ == '__main__':try:shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'cmd = shlex.split(shell_cmd)p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)count = 0while p.poll() is None:line = p.stdout.readline().decode('gbk').strip()print(line)count += 1if count == 50:os.kill(p.pid, signal.CTRL_C_EVENT)if p.returncode == 0:print('subprocess success')else:print('subprocess failed with code', p.returncode)except (OSError, ValueError, KeyboardInterrupt):print('stop here')

但是,可能是为了让我深刻认识到理论和实践的差距,果然程序又出了其它幺蛾子:视频并没有停止下载,而是下载完了之后才触发CTRL+C信号。。。

请注意视频的大小是15220KB,也就是完整的下载了整个视频。

当然,我也试了 CTRL_BREAK_EVENT 信号,结果也是下载完视频才触发信号:

四、最终圣战

到现在,所有想到的方法都试了一遍,但是没有一个既能中止视频下载,已经下载好的那部分又能正常播放的。当然,我们的努力还是有收获的,最接近我们最终目的的还是 CTRL_C_EVENT 信号,下面就继续拿他寻找解决办法。

# CTRL_C信号
os.kill(Popen.pid, signal.CTRL_C_EVENT)

好吧,我就不卖关子了,最终我还是找到解决办法,很简单,只要这样就行了:

os.kill(0, signal.CTRL_C_EVENT)

详见:https://stackoverflow.com/questions/7085604/sending-c-to-python-subprocess-objects-on-windows

上程序试一试:

import os
import shlex
import signal
import subprocessif __name__ == '__main__':try:shell_cmd = 'ffmpeg -i "https://vdn.vzuu.com/Act-ss-m3u8-hd/c5777f43b2ca4e588c3747d9c4ca2838/39068440-68b3-11e8-bb26-0242ac112a1eNone.m3u8?auth_key=1535110216-0-0-3d035cc32139ce884c8a1cbb14d0d04d&expiration=1535110216&disable_local_cache=0" -c copy "E:/PycharmProjects/Video_Crack/video/video1.mp4"'cmd = shlex.split(shell_cmd)p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)count = 0while p.poll() is None:line = p.stdout.readline().decode('gbk').strip()print(line)count += 1if count == 50:os.kill(0, signal.CTRL_C_EVENT)if p.returncode == 0:print('subprocess success')else:print('subprocess failed with code', p.returncode)except (OSError, ValueError, KeyboardInterrupt):print('stop here')

能看到它能正常播放真是太舒服了,那这个终极方法的原理到底是什么呢?

我们知道,os.kill() 有两个参数,即os.kill(pidsig),如果这两个参数中其中一个为0会是什么情况呢?

(1)os.kill(pid, 0)

若sig为0,则不发送任何信号,但仍执行错误检查;这可用于检查进程ID或进程组ID的存在。

原文链接:https://unix.stackexchange.com/questions/169898/what-does-kill-0-do

(2)os.kill(0, sig)

例如:os.kill(0, signal.CTRL_C_EVENT) ,表示程序通过向cmd窗口中的所有进程发送CTRL_C_EVENT来获取CTRL-C信号。

看一个例子(来源:https://www.programcreek.com/python/example/13966/signal.CTRL_C_EVENT):

def pipe_server():''' Part of attach/set_attach for Windows '''while 1:pipe = Pipe('vdb_%d' % os.getpid(), server=True)knock = pipe.read(3)if knock == 'vsi':os.kill(0, signal.CTRL_C_EVENT)      #ctypes.windll.kernel32.GenerateConsoleCtrlEvent(0, os.getpid())pipe.disconnect()pipe.close() 

更新(2018/08/28):

有一个小bug,使用os.kill(0, signal.CTRL_C_EVENT)之后,CTRL_C_EVENT信号发送给了当前子进程及其所有相关的父进程,所以我的图形界面也被关闭了!解决方法如下。原理就是接收到CTRL_C_EVENT信号之后会引发KeyboardInterrupt异常,我们捕捉这个异常,并在异常发生之后(此时GUI被关闭了)再次触发GUI,这个过程是很快的,所以我们不会发现GUI关闭过!

class GUIOperate(object):# 运行GUI@staticmethoddef gui_loop():try:top.mainloop()except KeyboardInterrupt:print('下载中止...')GUIOperate.gui_loop()

参考:

python-subprocess模块用法

Python错误:AttributeError: module 'signal' has no attribute 'SIGALRM'

https://docs.python.org/3.6/library/os.html

https://docs.python.org/3.6/library/signal.html#signal.CTRL_C_EVENT

Sending ^C to Python subprocess objects on Windows

GenerateConsoleCtrlEvent function

在Windows上将Ctrl+C信号发送到Python subprocess子进程相关推荐

  1. python serial 发送ctl+c_[已解决]shell 脚本 给命令发送 Ctrl+C信号

    Signal     Value     Action   Comment ─────────────────────────────────── ────────────────────────── ...

  2. linux系统发送信号的系统调用是,linux系统编程之信号:信号发送函数sigqueue和信号安装函数sigaction...

    信号发送函数sigqueue和信号安装函数sigaction sigaction函数用于改变进程接收到特定信号后的行为. sigqueue()是比较新的发送信号系统调用,主要是针对实时信号提出的(当然 ...

  3. 第九章 Shell信号发送与捕捉

    9.1 Linux信号类型 信号(Signal):信号是在软件层次上对中断机制的一种模拟,通过给一个进程发送信号,执行相应的处理函数. 进程可以通过三种方式来响应一个信号: 1)忽略信号,即对信号不做 ...

  4. linux 信号 core,Shell 信号发送与捕捉

    原标题:Shell 信号发送与捕捉 作者:李振良OK 1.Linux信号类型 信号(Signal):信号是在软件层次上对中断机制的一种模拟,通过给一个进程发送信号,执行相应的处理函数. 进程可以通过三 ...

  5. Windows下C 用 Socket 发送图片--基础

    Windows下C 用 Socket 发送图片--基础 转载:http://blog.csdn.net/yulinxx/article/details/51338214 服务器端: #include  ...

  6. linux普通用户发送信号,Linux信号发送与作业控制

    前言 Linux中进程间通信又称为IPC(Inter process communication),大致上可以分为这么几类:消息队列(message queue),旗语(semerpore),共享内存 ...

  7. Shell 信号发送与捕捉

    1.Linux信号类型 信号(Signal):信号是在软件层次上对中断机制的一种模拟,通过给一个进程发送信号,执行相应的处理函数. 进程可以通过三种方式来响应一个信号: 1)忽略信号,即对信号不做任何 ...

  8. 做python的socket训练,在Windows中使用telnet只能发送一个字符的问题。

    这个问题,就出在 Windows 上,在Linux或者苹果系统中使用telnet就没有问题,贴出大神的回答, 博_采_众_长 于 2020-11-16 13:18:30 发布 578 已收藏 1 分类 ...

  9. 32.全网最详细trap讲解,trap语法格式,信号和用途,最常用的信号,trap常用的命令,ctrl + c,信号屏蔽和恢复,trap -,debug,exit,return示例,三个综合案例

    文章目录 语法详解 trap格式 信号和用途 最常用的信号 trap常用的命令 示例 ctrl + c 示例 信号屏蔽和恢复 trap - 示例 debug示例 exit示例 return示例 综合案 ...

  10. qt信号发送间隔短而槽耗时多_Qt信号槽问题汇总 - osc_9q1dp3jk的个人空间 - OSCHINA - 中文开源技术交流社区...

    1. 发送一次信号,调用多次槽函数问题 在同一个类中,多次链接QObject::connect(sender, SIGNAL(signalSender(QString, int)), receiver ...

最新文章

  1. 在特定情况下的简单SSO实现方案
  2. 3月13日 抽奖活动
  3. 使用jQuery.Ajax向ASP.NET MVC控制器Post数据
  4. Python入门100题 | 第028题
  5. 1039: 二哥的困惑 Ⅱ
  6. 前沿实践:垃圾回收器是如何演进的?
  7. MIT算法导论(一)——算法分析和引论
  8. 百度MP3音乐API接口及应用
  9. neo4j 如何删除所以的节点和关系
  10. 无语!JDK 8 中的 HashMap 依然会死循环…
  11. 9.Linux/Unix 系统编程手册(上) -- 进程凭证
  12. windows环境下neo4j下载安装配置
  13. 分兵策略应对高速发展
  14. word文档怎么批量解除锁定_解除锁定的word文档的方法
  15. Sql server 2008 R2设置定期清理数据库日志文件
  16. 数据中心机房搬迁施工技术方案
  17. java7 xp版下载64位,xp32位系统可用最高版本jdk64位系统win10 64位系统
  18. 爬取年报数据、解析PDF提取数据、分析代码(巨潮 Python)
  19. 1202 -- 童年生活二三事
  20. 【单片机原理及其应用】第五章定时器的介绍和习题分析

热门文章

  1. 顺丰标准 150标准丰密面单 Clodop 实现打印
  2. IDEA使用教程(一)
  3. App Inventer制作蓝牙通讯软件
  4. SSD固态硬盘检测工具:SSDReporter mac版
  5. Altium Designer--多层线路的PDF文件输出
  6. CAD二次开发——cad查看对象C#:MdgDbg.dll
  7. CAD导入MAXWELL
  8. 五分钟就能上手的Android APP开发入门教程!!!
  9. Qt应用开发视频教程
  10. 使用JMeter进行简单的app接口测试