2024-05-17 23:04:34 +08:00
|
|
|
import os
|
|
|
|
import time
|
|
|
|
import signal
|
|
|
|
from logging import getLogger
|
|
|
|
log = getLogger(__name__)
|
|
|
|
|
|
|
|
|
|
|
|
class CGroup:
|
|
|
|
fs: str = "/sys/fs/cgroup"
|
|
|
|
name: str
|
|
|
|
|
|
|
|
@property
|
|
|
|
def path(self) -> str:
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Get full path of this cgroup
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
return os.path.join(self.fs, self.name)
|
|
|
|
|
|
|
|
@property
|
|
|
|
def valid(self) -> bool:
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Can read or write to this cgroup
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
return os.path.exists(self.path)
|
|
|
|
|
|
|
|
def create(self):
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Create this cgroup now
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
if self.valid: return
|
|
|
|
os.mkdir(self.path)
|
|
|
|
|
|
|
|
def destroy(self):
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Destroy the cgroup
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
if not self.valid: return
|
|
|
|
os.rmdir(self.path)
|
|
|
|
|
|
|
|
def add_pid(self, pid: int):
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Add a pid to track
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
if not self.valid: return
|
|
|
|
procs = os.path.join(self.path, "cgroup.procs")
|
|
|
|
with open(procs, "w") as f:
|
|
|
|
f.write(f"{pid}\n")
|
|
|
|
|
|
|
|
def list_pid(self) -> list[int]:
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
List all tracked children progress id
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
ret: list[int] = []
|
|
|
|
if not self.valid: return ret
|
|
|
|
procs = os.path.join(self.path, "cgroup.procs")
|
|
|
|
with open(procs, "r") as f:
|
|
|
|
for line in f:
|
|
|
|
ret.append(int(line))
|
|
|
|
return ret
|
|
|
|
|
|
|
|
def kill_all(self, sig: int = signal.SIGTERM, timeout: int = 10, kill: int = 8):
|
2024-05-20 09:56:42 +08:00
|
|
|
"""
|
|
|
|
Kill all children process and wait them exit
|
|
|
|
"""
|
2024-05-17 23:04:34 +08:00
|
|
|
if not self.valid: return
|
|
|
|
pids = self.list_pid()
|
|
|
|
remain = 0
|
|
|
|
while True:
|
2024-05-20 09:56:42 +08:00
|
|
|
# send a signal
|
2024-05-17 23:04:34 +08:00
|
|
|
for pid in pids:
|
|
|
|
log.debug(f"killing {pid}")
|
|
|
|
try: os.kill(pid, sig)
|
|
|
|
except: pass
|
2024-05-20 09:56:42 +08:00
|
|
|
|
|
|
|
# waitpid to clean zombie
|
2024-05-17 23:04:34 +08:00
|
|
|
try: os.waitpid(-1, os.WNOHANG)
|
|
|
|
except: pass
|
2024-05-20 09:56:42 +08:00
|
|
|
|
|
|
|
# check all children was exited
|
2024-05-17 23:04:34 +08:00
|
|
|
pids = self.list_pid()
|
|
|
|
if len(pids) <= 0: break
|
2024-05-20 09:56:42 +08:00
|
|
|
|
|
|
|
# set to SIGKILL when reached kill time
|
2024-05-17 23:04:34 +08:00
|
|
|
if 0 < kill <= remain:
|
|
|
|
sig = signal.SIGKILL
|
2024-05-20 09:56:42 +08:00
|
|
|
|
|
|
|
# timeoutd, throw out
|
2024-05-17 23:04:34 +08:00
|
|
|
if remain >= timeout:
|
|
|
|
raise TimeoutError("killing pids timedout")
|
2024-05-20 09:56:42 +08:00
|
|
|
|
|
|
|
# wait...
|
2024-05-17 23:04:34 +08:00
|
|
|
time.sleep(1)
|
|
|
|
|
|
|
|
def __init__(self, name: str, fs: str = None):
|
|
|
|
if fs: self.fs = fs
|
|
|
|
self.name = name
|