c++ - 해결 - 스레드가있는 포크 및 코어 덤프




잘못된 명령어 core dumped (3)

목표가 특정 지점에서 모든 스레드의 정확한 상태를 이해하기 위해 전체 프로세스를 스냅 샷하는 것이라면 어떤 종류의 인터럽트 서비스 루틴이 필요하지 않은이 작업을 수행 할 수있는 방법이 없습니다. 모든 프로세서를 중지하고 각 스레드의 현재 상태를 기록해야합니다.

이런 종류의 전체 프로세스 코어 덤프를 제공하는 시스템에 대해서는 알지 못합니다. 프로세스의 대략적인 개요는 다음과 같습니다.

  1. 모든 CPU (논리적 및 물리적 코어 모두)에서 인터럽트를 발행합니다.
  2. 모든 코어가 동기화 될 때까지 대기 중입니다 (오래 걸리지 않아야 함).
  3. 원하는 프로세스의 메모리 공간을 복제하십시오. 페이지 테이블을 복제하고 모든 페이지를 copy on write로 표시하십시오.
  4. 각 프로세서가 현재 스레드가 대상 프로세스에 있는지 여부를 확인하게하십시오. 그렇다면 해당 스레드에 대한 현재 스택 포인터를 기록하십시오.
  5. 다른 모든 스레드에 대해 현재 스택 포인터에 대한 스레드 데이터 블록을 검사하고이를 기록하십시오.
  6. 복사 된 메모리 공간과 스레드 스택 포인터를 저장하는 커널 스레드를 작성하십시오.
  7. 모든 코어를 재개하십시오.

이것은 프로세서 간 인터럽트가 발행 된 순간 실행 중이던 모든 프로세스의 스냅 샷을 포함하여 전체 프로세스 상태를 캡처해야합니다. 모든 스레드가 (표준 스케줄러 일시 중지 프로세스 또는 사용자 정의 인터럽트 프로세스를 통해) 인터럽트되므로 모든 레지스터 상태가 프로세스 메모리의 어딘가에 스택에 있습니다. 그런 다음 각 스레드 스택의 맨 위가 어디인지 알아야합니다. 쓰기 메커니즘을 사용하여 페이지 테이블을 복제하면 원래 프로세스를 다시 시작할 수있는 동안 투명하게 절약 할 수 있습니다.

주요 기능은 상당 기간 동안 모든 프로세서를 일시 중단해야합니다 (동기화, 복제, 모든 스레드 걷기). 그러나이를 통해 모든 스레드의 상태를 정확하게 캡처 할 수있을뿐 아니라 검사 점에 도달했을 때 실행중인 스레드 (및 CPU)를 확인할 수 있습니다. 이 프로세스를 수행하기위한 프레임 워크가 있다고 가정합니다 (예 : CRIU). 물론 프로세스를 다시 시작하면 쓰기 메커니즘의 복사본이 체크 포인트가 지정된 시스템 상태를 보호하므로 페이지 할당이 급격하게 증가합니다.

이 질문에있는 것과 비슷한 점이 here 와 here 에서 제기되었는데, 나는 Google coredump 라이브러리에 대해 알고있다. (나는 평가하고 부족한 것으로 나타났다. 문제를 더 잘 이해하면 해결할 수도 있지만 ).

프로세스를 중단하지 않고 실행중인 Linux 프로세스의 코어 덤프를 얻고 싶습니다. 자연적 접근 방식은 다음과 같이 말합니다.

if (!fork()) { abort(); }

fork 된 프로세스는 원래 프로세스의 메모리에 대한 고정 스냅 샷 복사본을 얻으므로 전체 코어 덤프를 가져와야하며 복사본은 copy-on-write를 사용하므로 일반적으로 저렴해야합니다. 그러나이 접근 방식의 중요한 단점은 fork() 가 현재 스레드 만 분기하고 원래 프로세스의 다른 모든 스레드는 분기 된 복사본에 존재하지 않는다는 것입니다.

내 질문은 어떻게 든 다른, 원래 스레드의 관련 데이터를 얻을 수 있는지 여부입니다. 나는이 문제에 어떻게 접근해야하는지 완전히 모르겠다. 그러나 여기에 몇 가지 하위 질문이있다.

  1. 모든 스레드의 스택을 포함하는 메모리가 여전히 사용 가능하며 분기 된 프로세스에서 액세스 할 수 있습니까?

  2. 원래 프로세스에서 실행중인 모든 스레드를 (빠른) 열거하고 해당 스택의 기본 주소를 저장할 수 있습니까? 내가 알기에, 리눅스상의 쓰레드 스택의 기반은 커널의 쓰레드 부기 데이터에 대한 포인터를 담고있다. 그래서 ...

  3. 저장된 스레드 기본 주소를 사용하면 분기 된 프로세스의 원래 스레드 각각에 대한 관련 데이터를 읽을 수 있습니까?

이것이 가능하다면 다른 스레드의 데이터를 코어 덤프에 추가하는 문제 일 수 있습니다. 그러나 데이터가 포크 지점에서 이미 손실 된 경우이 방법에 대한 희망은없는 것 같습니다.


특정 위치에 코어 파일을 가져오고 죽이지 않고 실행중인 프로세스의 핵심 이미지 만 얻으려면 gcore 를 사용할 수 있습니다.

특정 위치 (조건)에서 코어 파일을 가져오고 프로세스를 계속 실행하려는 경우 - 원시 접근 방식은 해당 위치에서 프로그래밍 방식으로 gcore 를 실행하는 것입니다.

보다 고전적이고 깨끗한 접근법은 gcore가 애플리케이션에서 사용하고 임베드 한 API를 확인하는 것이지만 대부분의 경우에 비해 너무 많은 노력이 필요합니다.

HTH!


fork 를하면 실행중인 프로세스 메모리의 전체 복사본을 얻을 수 있습니다. 여기에는 모든 스레드의 스택이 포함됩니다 (모든 포인터가 유효한 포인터를 가질 수는 있습니다). 그러나 호출 스레드 만 자식에서 계속 실행됩니다.

이것을 쉽게 테스트 할 수 있습니다. 다중 스레드 프로그램을 만들고 실행하십시오.

pid_t parent_pid = getpid();

if (!fork()) {
    kill(parent_pid, SIGSTOP);

    char buffer[0x1000];

    pid_t child_pid = getpid();
    sprintf(buffer, "diff /proc/%d/maps /proc/%d/maps", parent_pid, child_pid);

    system(buffer);

    kill(parent_pid, SIGTERM);

    return 0;
} else for (;;);

그래서 당신의 모든 메모리가 거기에 있고 당신이 코어 덤프를 생성 할 때 그것은 모든 다른 쓰레드 스택을 포함 할 것입니다 (여러분의 최대 코어 파일 크기가 그것을 허용한다면). 누락 될 유일한 부분은 레지스터 세트입니다. 당신이 그 (것)들을 필요로하는 경우에 당신은 그 (것)들을 얻기 위하여 당신의 부모를 ptrace 해야 할 것이다.

코어 덤프의 원인이되는 스레드가 하나 이상인 런타임 정보를 포함하도록 코어 덤프가 설계되지는 않았지만 명심해야합니다.

다른 질문에 답하려면 다음과 같이하십시오.

/proc/[pid]/tasks 통해 스레드를 열거 할 수 있지만 ptrace 할 때까지 스택 스택을 식별 할 수는 없습니다.

네, fork 된 프로세스에서 다른 스레드 스택 스냅 샷 (위 참조)에 대한 모든 액세스 권한을가집니다. 그것들을 결정하는 것은 사소한 일이 아니지만 코어 파일 크기가 허용한다면 코어 덤프에 들어갑니다. 가장 좋은 방법은 창조 할 때 할 수 있다면 그들을 세계적으로 접근 가능한 구조로 저장하는 것입니다.







coredump