포크된 프로세스에서 CPU 사용량을 제한하는 것은 무엇입니까?

포크된 프로세스에서 CPU 사용량을 제한하는 것은 무엇입니까?

저는 멀티 코어 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%.)

우선순위가 다시 같으면 첫 번째 상태로 돌아갑니다.

문제가 특정 하드웨어 구성이나 다른 것과 관련이 있을 수 있다고 생각하기 시작했지만 추가 디버깅을 위한 도구/지식이 부족합니다.

관련 정보