Programing

프로세스가 실행되는 코어를 제어하는 ​​방법은 무엇입니까?

lottogame 2020. 12. 31. 07:51
반응형

프로세스가 실행되는 코어를 제어하는 ​​방법은 무엇입니까?


여러 프로세스 또는 스레드를 사용하는 프로그램을 작성하는 방법을 이해할 수 있습니다. fork () 새 프로세스를 사용하고 IPC를 사용하거나 여러 스레드를 만들고 이러한 종류의 통신 메커니즘을 사용합니다.

컨텍스트 전환도 이해합니다. 즉, CPU가 한 번만 있으면 운영 체제가 각 프로세스에 대한 시간을 예약하고 (그리고 수많은 예약 알고리즘이 있음) 여러 프로세스를 동시에 실행할 수 있습니다.

이제 다중 코어 프로세서 (또는 다중 프로세서 컴퓨터)가 있으므로 두 개의 개별 코어에서 동시에 실행되는 두 개의 프로세스를 가질 수 있습니다.

내 질문은 마지막 시나리오에 관한 것입니다. 커널은 프로세스가 실행되는 코어를 어떻게 제어합니까? 어떤 시스템 호출 (Linux 또는 Windows에서)이 특정 코어에서 프로세스를 예약합니까?

제가 묻는 이유는 다음과 같습니다. 저는 최근 컴퓨팅 주제를 탐구하는 학교 프로젝트를 진행하고 있으며 멀티 코어 아키텍처를 선택했습니다. 그런 종류의 환경에서 프로그래밍하는 방법 (교착 상태 또는 경쟁 조건을 관찰하는 방법)에 대한 자료는 많지만 개별 코어 자체를 제어하는 ​​방법은별로 없습니다. 몇 가지 데모 프로그램을 작성하고 몇 가지 어셈블리 지침 또는 C 코드를 제시하여 "2 번째 코어에서 무한 루프를 실행하고 있습니다. 특정 코어에 대한 CPU 사용률의 급증을 확인하십시오. " .

코드 예제가 있습니까? 아니면 튜토리얼?

편집 : 설명을 위해-많은 사람들이 이것이 OS의 목적이며 OS가이를 처리하도록해야한다고 말했습니다. 완전히 동의 해! 그러나 내가 요구하는 (또는 느낌을 얻으려는) 것은 운영 체제가 실제로 이것을 수행하는 것입니다. 스케줄링 알고리즘이 아니라 "코어가 선택되면 해당 코어가 명령을 가져 오기 시작하려면 어떤 명령을 실행해야합니까?"


다른 사람들이 언급했듯이 프로세서 선호도는 운영 체제에 따라 다릅니다 . 운영 체제의 범위 밖에서이 작업을 수행하고 싶다면 많은 재미를 느끼며 고통을 겪게됩니다.

즉, 다른 사람들이 SetProcessAffinityMaskWin32에 대해 언급 했습니다. 아무도 프로세서 선호도를 설정하는 Linux 커널 방법을 언급하지 않았으므로 나는 그렇게 할 것입니다. sched_setaffinity기능 을 사용해야합니다 . 여기 멋진 튜토리얼 방법은.


일반적으로 앱이 실행될 코어는 시스템에서 결정합니다. 그러나 응용 프로그램의 "친 화성"을 특정 코어로 설정하여 해당 코어에서만 앱을 실행하도록 OS에 지시 할 수 있습니다. 일반적으로 이것은 좋은 생각이 아니지만, 드물게 이해가되는 경우가 있습니다.

Windows에서이 작업을 수행하려면 작업 관리자를 사용하고 프로세스를 마우스 오른쪽 단추로 클릭 한 다음 "친화도 설정"을 선택합니다. SetThreadAffinityMask, SetProcessAffinityMask 또는 SetThreadIdealProcessor와 같은 함수를 사용하여 Windows에서 프로그래밍 방식으로 수행 할 수 있습니다.

ETA :

OS가 실제로 스케줄링을 수행하는 방법에 관심이 있다면 다음 링크를 확인하십시오.

컨텍스트 전환에 대한 Wikipedia 기사

스케줄링에 관한 위키 백과 기사

Linux 커널에서 예약

대부분의 최신 OS에서 OS는 짧은 시간 동안 코어에서 실행되도록 스레드를 예약합니다. 타임 슬라이스가 만료되거나 스레드가 자발적으로 코어를 양보하도록하는 IO 작업을 수행하면 OS는 다른 스레드가 코어에서 실행되도록 예약합니다 (실행할 준비가 된 스레드가있는 경우). 정확히 어떤 스레드가 예약되는지는 OS의 예약 알고리즘에 따라 다릅니다.

컨텍스트 전환이 정확히 어떻게 발생하는지에 대한 구현 세부 사항은 CPU 및 OS에 따라 다릅니다. 일반적으로 커널 모드로의 전환, OS가 이전 스레드의 상태를 저장하고 새 스레드의 상태를로드 한 다음 사용자 모드로 다시 전환하고 새로로드 된 스레드를 다시 시작하는 작업이 포함됩니다. 위에서 링크 한 컨텍스트 전환 기사에는 이에 대해 좀 더 자세히 설명되어 있습니다.


코어에 "이제이 프로세스 실행 시작"을 알려주는 것은 없습니다.

코어 프로세스를 보지 않고 실행 가능한 코드와 다양한 실행 수준 및 실행할 수있는 명령에 대한 관련 제한 사항 만 알고 있습니다.

컴퓨터가 부팅되면 간단하게 하나의 코어 / 프로세서 만 활성화되고 실제로 모든 코드를 실행합니다. 그런 다음 OS가 MultiProcessor를 사용할 수있는 경우 시스템 특정 명령을 사용하여 다른 코어를 활성화하고 다른 코어는 다른 코어와 정확히 동일한 지점에서 선택하여 실행합니다.

따라서 스케줄러는 OS 내부 구조 (작업 / 프로세스 / 스레드 대기열)를 살펴보고 하나를 선택하고 코어에서 실행중인 것으로 표시합니다. 그런 다음 다른 코어에서 실행중인 다른 스케줄러 인스턴스는 작업이 다시 대기 상태가 될 때까지 (특정 코어에 고정 된 것으로 표시되지 않은)이를 건드리지 않습니다. 작업이 실행 중으로 표시된 후 스케줄러는 이전에 일시 중단 된 지점에서 작업을 다시 시작하여 사용자 영역으로 전환을 실행합니다.

기술적으로는 코어가 똑같은 코드를 똑같은 시간에 실행하는 것을 막을 수는 없지만 (잠금 해제 된 많은 함수가 수행합니다) 코드가이를 예상하도록 작성되지 않는 한, 아마도 그 자체로 오줌을 누를 것입니다.

시나리오는 더 특이한 메모리 모델 (위에서 "일반적인"선형 단일 작업 메모리 공간을 가정 함)을 사용하면 더 이상해집니다. 코어가 모두 동일한 메모리를 볼 필요는없고 다른 코어의 클러치에서 코드를 가져 오는 데 필요한 요구 사항이있을 수 있지만 간단하게 처리하는 것이 훨씬 쉽습니다. 코어에 고정 된 작업을 유지합니다 (SPU가있는 AFAIK Sony PS3 아키텍처는 이와 같습니다).


는 OpenMPI의 프로젝트는이 프로세서 선호도 설정 라이브러리리눅스를 이식 가능한 방법을.

얼마 전에 나는 이것을 프로젝트에서 사용했으며 잘 작동했습니다.

주의 사항 : 운영 체제가 코어에 번호를 매기는 방법을 알아내는 데 몇 가지 문제가 있었다는 것을 희미하게 기억합니다. 저는 이것을 각각 4 개의 코어가있는 2 Xeon CPU 시스템에서 사용했습니다.

살펴보면 cat /proc/cpuinfo도움이 될 것입니다. 내가 사용한 상자에는 꽤 이상합니다. 삶은 결과물이 끝에 있습니다.

분명히 균등 한 번호의 코어는 첫 번째 CPU에 있고 홀수 번호의 코어는 두 번째 CPU에 있습니다. 그러나 올바르게 기억하면 캐시에 문제가 있습니다. 이러한 Intel Xeon 프로세서에서 각 CPU의 두 코어는 L2 캐시를 공유합니다 (프로세서에 L3 캐시가 있는지 여부는 기억 나지 않습니다). 가상 프로세서 0과 2는 하나의 L2 캐시를 공유하고 1과 3은 하나를 공유하고 4와 6은 하나를 공유하고 5와 7은 하나를 공유한다고 생각합니다.

이 기이함 때문에 (1.5 년 전 Linux에서 프로세스 번호 매기기에 대한 문서를 찾을 수 없었습니다.) 이런 종류의 저수준 조정을 수행하는 데주의해야합니다. 그러나 분명히 몇 가지 용도가 있습니다. 코드가 몇 가지 종류의 컴퓨터에서 실행되는 경우 이러한 종류의 조정을 수행하는 것이 좋습니다. 또 다른 애플리케이션은 StreamIt 과 같은 특정 도메인 언어로 되어있어 컴파일러가이 더러운 작업을 수행하고 스마트 일정을 계산할 수 있습니다.

processor       : 0
physical id     : 0
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 1
physical id     : 1
siblings        : 4
core id         : 0
cpu cores       : 4

processor       : 2
physical id     : 0
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 3
physical id     : 1
siblings        : 4
core id         : 1
cpu cores       : 4

processor       : 4
physical id     : 0
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 5
physical id     : 1
siblings        : 4
core id         : 2
cpu cores       : 4

processor       : 6
physical id     : 0
siblings        : 4
core id         : 3
cpu cores       : 4

processor       : 7
physical id     : 1
siblings        : 4
core id         : 3
cpu cores       : 4

/ proc / cpuinfo를 사용하는 대신 프로세서 수를 확인하려면 다음을 실행하십시오.

nproc

특정 프로세서 그룹에서 프로세스를 실행하려면 :

taskset --cpu-list 1,2 my_command 

내 명령은 cpu 1 또는 2에서만 실행할 수 있다고 말합니다.

4 개의 다른 작업을 수행하는 4 개의 프로세서에서 프로그램을 실행하려면 매개 변수화를 사용하십시오. 프로그램에 대한 인수는 다른 작업을 수행하도록 지시합니다.

for i in `seq 0 1 3`;
do 
  taskset --cpu-list $i my_command $i;
done

A good example of this is dealing with 8 million operation in an array so that 0 to (2mil-1) goes to processor 1, 2mil to (4mil-1) to processor 2 and so on.

You can look at the load on each process by installing htop using apt-get/yum and running at the command line:

 htop

As others have mentioned, it's controlled by the operating system. Depending on the OS, it may or may not provide you with system calls that allow you to affect what core a given process executes on. However, you should usually just let the OS do the default behavior. If you have a 4-core system with 37 processes running, and 34 of those processes are sleeping, it's going to schedule the remaining 3 active processes onto separate cores.

You'll likely only see a speed boost on playing with core affinities in very specialized multithreaded applications. For example, suppose you have a system with 2 dual-core processors. Suppose you have an application with 3 threads, and two of threads operate heavily on the same set of data, whereas the third thread uses a different set of data. In this case, you would benefit the most by having the two threads which interact on the same processor and the third thread on the other processor, since then they can share a cache. The OS has no idea what memory each thread needs to access, so it may not allocate threads to cores appropriately.

If you're interested in how the operating system, read up on scheduling. The nitty gritty details of multiprocessing on x86 can be found in the Intel 64 and IA-32 Architectures Software Developer's Manuals. Volume 3A, Chapters 7 and 8 contain relevant information, but bear in mind these manuals are extremely technical.


The OS knows how to do this, you do not have to. You could run into all sorts of issues if you specified which core to run on, some of which could actually slow the process down. Let the OS figure it out, you just need to start the new thread.

For example, if you told a process to start on core x, but core x was already under a heavy load, you would be worse off than if you had just let the OS handle it.


I don't know the assembly instructions. But the windows API function is SetProcessAffinityMask. You can see an example of something I cobbled together a while ago to run Picasa on only one core


Linux sched_setaffinity C minimal runnable example

In this example, we get the affinity, modify it, and check if it has taken effect with sched_getcpu().

#define _GNU_SOURCE
#include <assert.h>
#include <sched.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void print_affinity() {
    cpu_set_t mask;
    long nproc, i;

    if (sched_getaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_getaffinity");
        assert(false);
    } else {
        nproc = sysconf(_SC_NPROCESSORS_ONLN);
        printf("sched_getaffinity = ");
        for (i = 0; i < nproc; i++) {
            printf("%d ", CPU_ISSET(i, &mask));
        }
        printf("\n");
    }
}

int main(void) {
    cpu_set_t mask;

    print_affinity();
    printf("sched_getcpu = %d\n", sched_getcpu());
    CPU_ZERO(&mask);
    CPU_SET(0, &mask);
    if (sched_setaffinity(0, sizeof(cpu_set_t), &mask) == -1) {
        perror("sched_setaffinity");
        assert(false);
    }
    print_affinity();
    /* TODO is it guaranteed to have taken effect already? Always worked on my tests. */
    printf("sched_getcpu = %d\n", sched_getcpu());
    return EXIT_SUCCESS;
}

Compile and run with:

gcc -std=c99 main.c
./a.out

Sample output:

sched_getaffinity = 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 
sched_getcpu = 9
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

Which means that:

  • initially, all of my 16 cores were enabled, and the process was randomly running on core 9 (the 10th one)
  • after we set the affinity to only the first core, the process was moved necessarily to core 0 (the first one)

It is also fun to run this program through taskset:

taskset -c 1,3 ./a.out

Which gives output of form:

sched_getaffinity = 0 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 2
sched_getaffinity = 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
sched_getcpu = 0

and so we see that it limited the affinity from the start.

This works because the affinity is inherited by child processes, which taskset is forking: How to prevent inheriting CPU affinity by child forked process?

Tested in Ubuntu 16.04, GitHub upstream.

x86 bare metal

If you are that hardcore: What does multicore assembly language look like?

How Linux implements it

How does sched_setaffinity() work?

ReferenceURL : https://stackoverflow.com/questions/663958/how-to-control-which-core-a-process-runs-on

반응형