私はマルチコアLinuxシステムで科学的な目的でいくつかの長期実行プログラムを実行しています。プロセスは小さなデーモンによって制御され、1つのプロセスが完了すると次のタスクを再開します(一度に3〜6回実行)。しかし、常にCPUを100%使用しているわけではありません。プログラム(およびデーモン)はPythonで書かれています。
Mac OS Xでこのコードを実行すると、数週間プログラムを実行でき、コンピュータが通常の温度で実行されている間、プログラムは常に利用可能なシステムリソースを最大限に消費します。
私は、必要なものよりも多くのRAMと6つのコアを持つDebian Linux(他のコンピュータ)でこのようなタスクを実行し始めました。私はこのタスクの5つを同時に実行しています。
数日前に初めて作業を開始したとき、top
それぞれ100%CPUを使用する5つのPythonプロセスがありました。 1日ほど後に実行状態を確認すると、3つのプロセスはCPU使用率が100%で、他の2つのプロセスは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%.)
優先順位が同じであれば、最初の状態に戻ります。
問題は特定のハードウェア構成や他のものと関連があると考え始めましたが、追加のデバッグのためのツール/知識が不足しています。