如何kill整一个进程组或会话
从 http://morningcoffee.io/killing-a-process-and-all-of-its-descendants.html 看到的,记录一下
今天才知道,kill 命令可以后接一个负数来表示发送信号给一个进程组中的所有进程。
Negative PID values may be used to choose whole process groups; see the PGID column in ps command output. A PID of -1 is special; it indicates all processes except the kill process itself and init.
所以我们可以用 kill -SIGTERM -- -$PGID
来强制关闭指定进程组中的所有进程。
kill 命令的这个特性是由 kill(2)
系统调用而来的。
DESCRIPTION The kill() system call can be used to send any signal to any process group or process. If pid is positive, then signal sig is sent to the process with the ID specified by pid. If pid equals 0, then sig is sent to every process in the process group of the calling process. If pid equals -1, then sig is sent to every process for which the calling process has permission to send signals, except for process 1 (init), but see below. If pid is less than -1, then sig is sent to every process in the process group whose ID is -pid.
而 kill(2)
系统调用并不支持直接根据Session ID来发送信号(甚至对于某些UNIX实现来说都没有Session ID这个概念),因此要发送信号给某个 Session 中的进程需要遍历 /proc/
中的进程树( /proc/pid/stat ),搜集符合 SID 的进程,然后发送信号给这些进程。
pkill 命令已经帮我们实现了这一功能,只需要执行 pkill -s $SID
就可以了。
最后,要察看进程组ID 和 SessionID,可以通过 ps 的 j
参数看到
ps j -A
PPID PID PGID SID TTY TPGID STAT UID TIME COMMAND 0 1 1 1 ? 0 Ssl 0 0:00 /init ro 1 6 6 6 tty1 0 Ss 0 0:00 /init ro 6 7 7 6 tty1 0 S 1000 0:03 -bash 7 269 269 6 tty1 0 T 1000 0:00 -bash 1 1367 1367 1367 tty2 0 Ss 0 0:00 /init ro 1367 1368 1368 1367 tty2 0 S 1000 0:05 -bash 1 7714 7714 7714 ? 0 Ssl 1000 7:37 emacs --daemon 7714 7760 7760 7760 ? 0 Ss 1000 0:00 /usr/bin/aspell -a -m -d en --encoding=utf-8 7714 10222 10222 10222 pts/0 0 Ssl 1000 0:03 /usr/bin/python -c from __future__ import print_function # CLI arguments. import sys assert len(sys.argv) > 3, 'CLI arguments: %s' % sys.argv server_directory = sys.argv[-3] server_address = sys.argv[-2] virtual_environment = sys.argv[-1] # Ensure directory. import os server_directory = os.path.expanduser(server_directory) if not os.path.exists(server_directory): os.makedirs(server_directory) # Installation check. jedi_dep = ('jedi', '0.13.0') service_factory_dep = ('service_factory', '0.1.5') missing_dependencies = [] def instrument_installation(): for package in (jedi_dep, service_factory_dep): package_is_installed = False for path in os.listdir(server_directory): path = os.path.join(server_directory, path) if path.endswith('.egg') and os.path.isdir(path): if path not in sys.path: sys.path.insert(0, path) if package[0] in path: package_is_installed = True if not package_is_installed: missing_dependencies.append('>='.join(package)) instrument_installation() # Installation. def install_deps(): import site import setuptools.command.easy_install site.addsitedir(server_directory) cmd = ['--install-dir', server_directory, '--site-dirs', server_directory, '--always-copy','--always-unzip'] cmd.extend(missing_dependencies) setuptools.command.easy_install.main(cmd) instrument_installation() if missing_dependencies: install_deps() del missing_dependencies[:] try: import jedi except ImportError: missing_dependencies.append('>='.join(jedi_dep)) try: import service_factory except ImportError: missing_dependencies.append('>='.join(service_factory_dep)) # Try one more time in case if anaconda installation gets broken somehow if missing_dependencies: install_deps() import jedi import service_factory # Setup server. assert jedi.__version__ >= jedi_dep[1], 'Jedi version should be >= %s, current version: %s' % (jedi_dep[1], jedi.__version__,) if virtual_environment: virtual_environment = jedi.create_environment(virtual_environment, safe=False) else: virtual_environment = None # Define JSON-RPC application. import functools import threading def script_method(f): @functools.wraps(f) def wrapper(source, line, column, path): timer = threading.Timer(30.0, sys.exit) timer.start() result = f(jedi.Script(source, line, column, path, environment=virtual_environment)) timer.cancel() return result return wrapper def process_definitions(f): @functools.wraps(f) def wrapper(script): definitions = f(script) if len(definitions) == 1 and not definitions[0].module_path: return '%s is defined in %s compiled module' % ( definitions[0].name, definitions[0].module_name) return [[definition.module_path, definition.line, definition.column, definition.get_line_code().strip()] for definition in definitions if definition.module_path] or None return wrapper @script_method def complete(script): return [[definition.name, definition.type] for definition in script.completions()] @script_method def company_complete(script): return [[definition.name, definition.type, definition.docstring(), definition.module_path, definition.line] for definition in script.completions()] @script_method def show_doc(script): return [[definition.module_name, definition.docstring()] for definition in script.goto_definitions()] @script_method @process_definitions def goto_definitions(script): return script.goto_definitions() @script_method @process_definitions def goto_assignments(script): return script.goto_assignments() @script_method @process_definitions def usages(script): return script.usages() @script_method def eldoc(script): signatures = script.call_signatures() if len(signatures) == 1: signature = signatures[0] return [signature.name, signature.index, [param.description[6:] for param in signature.params]] # Run. app = [complete, company_complete, show_doc, goto_definitions, goto_assignments, usages, eldoc] service_factory.service_factory(app, server_address, 0, 'anaconda_mode port {port}') ~/.emacs.d/.cache/anaconda-mode/0.1.13 127.0.0.1 10222 10227 10222 10222 pts/0 0 S 1000 0:00 /usr/bin/python /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/jedi-0.14.1-py3.6.egg/jedi/evaluate/compiled/subprocess/__main__.py /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/parso-0.5.1-py3.6.egg 2.7.15 10222 10344 10222 10222 pts/0 0 S 1000 0:00 /usr/bin/python /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/jedi-0.14.1-py3.6.egg/jedi/evaluate/compiled/subprocess/__main__.py /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/parso-0.5.1-py3.6.egg 2.7.15 10222 10428 10222 10222 pts/0 0 S 1000 0:00 /usr/bin/python /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/jedi-0.14.1-py3.6.egg/jedi/evaluate/compiled/subprocess/__main__.py /home/lujun9972/.emacs.d/.cache/anaconda-mode/0.1.13/parso-0.5.1-py3.6.egg 2.7.15 7714 10610 10610 10610 ? 0 Ss 1000 0:00 /bin/bash 10610 10611 10610 10610 ? 0 R 1000 0:00 ps j -A
其中 PGID 即为进程组ID, SID 为 SessionID