저는 멀티 코어 Linux 시스템에서 과학적인 목적으로 일부 장기 실행 프로그램을 실행하고 있습니다. 프로세스는 작은 데몬에 의해 제어되며 하나의 프로세스가 완료되면 다음 작업을 다시 시작합니다(한 번에 3-6개 실행). 하지만 항상 CPU를 100% 사용하지는 않는다는 것을 알았습니다. 프로그램(및 데몬)은 Python으로 작성됩니다.
Mac OS X에서 이 코드를 실행하면 몇 주 동안 프로그램을 실행할 수 있으며 컴퓨터가 정상 온도에서 실행되는 동안 프로그램은 항상 사용 가능한 시스템 리소스를 최대한 많이 사용합니다.
저는 작업에 필요한 것보다 더 많은 RAM과 6개의 코어를 갖춘 Debian Linux(다른 컴퓨터)에서 이와 같은 작업을 실행하기 시작했습니다. 저는 이 작업 중 5개를 동시에 실행하고 있습니다.
며칠 전 처음 작업을 시작했을 때 top
각각 100% CPU를 사용하는 5개의 Python 프로세스가 있었습니다. 하루 정도 후에 실행 상태를 확인해 보니 3개의 프로세스는 CPU 사용률이 100%이고 다른 두 프로세스는 CPU 사용률이 50%인 것으로 나타났습니다. 최근(약 4일) 5개의 프로세스가 20% CPU에서 실행되고 있습니다.
원인은 무엇일까요? CPU 사용량 관리 도구가 Debian Wheezy에 사전 설치되어 있다는 징후는 없는 것 같습니다. 그리고 나 자신도 (내가 아는 한) 이와 유사한 것을 설치하거나 구성한 적이 없습니다. 그리고, 마족이 얼마나 오래 활동하느냐에 따라 한계가 달라지는 것 같아서, 그런 시스템이 있을 수 있다고는 생각하지 않습니다. 나는 기계가 과열되었는지 확인했고 그것이 있던 (추운) 방보다 훨씬 더 뜨겁지 않은 것 같았습니다. 팬/환풍구에서 나오는 공기는 맑고 시원했습니다.
프로세스가 계속 실행 중이므로 이 문제를 디버깅하기 위해 디버깅에 유용할 수 있는 모든 항목(실행 시간, 프로세스 우선 순위 등)을 측정할 수 있습니다. 어디서부터 시작해야 하는지, 가능한 해결책은 무엇인지 말해 줄 수 있는 사람이 있나요?
고쳐 쓰다:
5개가 아닌 3개의 스레드로 동일한 작업을 시도하면 스레드당 사용률이 33%로 떨어집니다(처음에는 50%로 떨어졌습니다).
단일 프로세스의 모든 하위 프로세스의 총 개수를 100%로 제한하는 프로그램이나 일정 정책이 있습니까? 왜냐하면 그런 일이 일어나고 있는 것 같거든요.
다음 테스트는 별도의 셸에서 스크립트를 직접 실행하여 screen
(첫 번째 스크립트는 내부에서 실행됨 screen
) 속도 저하가 있는지 확인하는 것입니다. 이렇게 작업을 수동으로 나누는 것은 좋은 해결 방법이지만 다소 귀찮습니다(필요하지 않아야 합니다.) 물론 일반적으로 이러한 문제는 이 방법으로 해결되지 않을 수 있지만 각 작업에는 모든 결과가 반환되지 않고 디스크에 저장됩니다. 내가 도망칠 스레드 관리자에게.
업데이트 2:
다른 화면 인스턴스에서 시작된 별도의 프로세스는 14시간 후에도 계속 100% CPU로 실행됩니다. 속도 저하가 확인되면 보고되지만 예상대로 이는 제한의 영향을 받지 않습니다.
Linux의 프로세스 우선순위를 설명하는 내용을 작성해 주실 분(또는 저에게 알려 주실 분)이 있습니까? 내 생성 프로세스가 낮은 우선순위로 표시되고(CPU 자체를 너무 적게 사용하기 때문에) 하위 프로세스가 이를 상속하는지 궁금합니다.
편집하다:
내가 실행 중인 스크립트와 분기된 데몬이 수행하는 작업에 대해 질문을 받았습니다.
장기 실행 스크립트는 완료될 때까지 항상 100% CPU로 실행되며 병렬화 또는 다중 처리에 아무런 관련이 없는 대규모 계산입니다. (이것은 광범위하게 테스트된 주장입니다.) 추가 설명 - Mac에서 이러한 프로세스가 100% 미만의 CPU에서 실행되는 것을 본 유일한 경우는 과열되거나 페이징/스와핑되는 경우입니다. 이 중 어느 것도 Linux 상황과 관련이 없습니다.
이는 분기되어 장기 실행 프로세스를 관리하는 기능입니다.
from multiprocessing import Process
import time, sys, os
# An alternative entry point which runs N jobs in parallel over a list of files.
# Note, since this is designed to be used as a library function, we "return" from the initial
# function call rather than exiting.
def run_over_file_list(script_path, list_of_data_files, num_processes, timeout=float('inf')):
try:
pid = os.fork()
if pid > 0:
# exit first parent
return
except OSError, e:
print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# decouple from parent environment
os.chdir("/")
os.setsid()
os.umask(0)
# do second fork
try:
pid = os.fork()
if pid > 0:
# exit from second parent, print eventual PID before
print "Daemon PID %d" % pid
sys.exit(0)
except OSError, e:
print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
sys.exit(1)
# OK, we're inside a manager daemon.
if os.path.isfile(status_filename):
raise Exception("a daemon is already running. failed.")
f = open(status_filename, "w")
f.write(str(os.getpid()))
f.close()
jobs = [script_path] * num_processes
data_files_remaining = [f for f in list_of_data_files]
update_files_remaining_file(len(data_files_remaining))
assert num_processes <= len(data_files_remaining)
restart = False
with nostdout():
while True:
processes = []
for job in jobs:
p = Process(target=file_list_worker, args=(job, data_files_remaining.pop(0)))
p.started = time.time()
p.start()
processes.append(p)
stop = False
while True:
time.sleep(10)
ended = []
for i, p in enumerate(processes):
if not p.is_alive():
j = i
ended.append((j,p))
elif time.time() - p.started > timeout:
p.terminate()
j = i
ended.append((j,p))
if not stop:
for tup in ended:
if not data_files_remaining:
stop = True
break
i, e = tup
new_p = Process(target=file_list_worker, args=(jobs[i], data_files_remaining.pop(0)))
new_p.started = time.time()
new_p.start()
processes[i] = new_p
# old e will be garbage collected
else:
if len(ended) == len(processes) and not data_files_remaining:
stop = False
break
try:
command = check_for_command()
if command == "stop":
stop = True
elif command == "restart":
stop = True
restart = True
elif command == "kill":
for p in processes:
p.terminate()
clear_command()
os.remove(status_filename)
exit(0)
except NoCommandError:
pass
update_files_remaining_file(len(data_files_remaining))
clear_command()
update_files_remaining_file(len(data_files_remaining))
if not restart:
os.remove(status_filename)
break
else:
jobs = None
restart = False
# While in a fork, we should never return (will continue running the original/calling script in parallel, hilarity ensues.)
exit(0)
편집 2:
우선순위
20
따라서 사전 제한된 프로세스, 사후 제한된 프로세스, 데몬 관리자, 프로세스가 화면 아래의 셸에서 직접 실행되는 등 모든 것이 먼저 실행되는 것 같습니다.
ulimit -a
배쉬에서:
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 127788
max locked memory (kbytes, -l) 64
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 127788
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
물고기에서:
Maximum size of core files created (kB, -c) 0
Maximum size of a process’s data segment (kB, -d) unlimited
Maximum size of files created by the shell (kB, -f) unlimited
Maximum size that may be locked into memory (kB, -l) 64
Maximum resident set size (kB, -m) unlimited
Maximum number of open file descriptors (-n) 1024
Maximum stack size (kB, -s) 8192
Maximum amount of cpu time in seconds (seconds, -t) unlimited
Maximum number of processes available to a single user (-u) 127788
Maximum amount of virtual memory available to the shell (kB, -v) unlimited
화면 아래의 물고기에서:
(일반 생선과 똑같습니다.)
오랜만에 업데이트
또한 별도의 셸에서 장기 실행 프로세스를 실행할 때 이 버그를 발견했습니다. 예를 들어:
Instance 1: 17% (one core of 6 at 100%.)
Instance 2: 8% (one core of 6 at 50%.)
Instance 3: 8% (one core of 6 at 50%.)
인스턴스 2의 우선순위를 "매우 높음"으로 변경하면 상태는 다음과 같습니다.
Instance 1: 17% (one core of 6 at 100%.)
Instance 2: 17% (one core of 6 at 100%.)
Instance 3: 0% (one core of 6 at 0%.)
우선순위가 다시 같으면 첫 번째 상태로 돌아갑니다.
문제가 특정 하드웨어 구성이나 다른 것과 관련이 있을 수 있다고 생각하기 시작했지만 추가 디버깅을 위한 도구/지식이 부족합니다.