프로세스를 종료하는 데 시간이 오래 걸리는 이유는 무엇입니까?

프로세스를 종료하는 데 시간이 오래 걸리는 이유는 무엇입니까?

기본적으로 저는 PowerPC(Freescale e500mc)에서 Linux 2.6.34를 사용하고 있습니다. 약 2.25G의 mlocked VM을 사용하는 프로세스(내부적으로 개발된 가상 머신)가 있습니다. 종료하면 종료하는 데 2분 이상 걸리는 것을 알 수 있습니다.

나는 그것을 조사했다. 먼저 열려 있는 파일 설명자를 모두 닫았지만 별 차이가 없는 것 같습니다. 그런 다음 커널에 일부 printk를 추가했고 이를 통해 VMA를 잠금 해제하는 커널에서 모든 지연이 발생한다는 것을 알았습니다. 지연 시간은 페이지 전체에서 일관되었으며 /proc/meminfo에서 잠긴 페이지 수를 다시 확인하여 이를 확인했습니다. 그렇게 많은 메모리를 할당하는 프로그램을 확인해 본 적이 있는데 신호를 보내자마자 즉시 죽었습니다.

지금 무엇을 확인해야 할 것 같나요? 당신의 답변에 감사드립니다.

편집하다:나는 이 문제에 대해 더 많은 정보를 공유할 수 있는 방법을 찾아야 했기 때문에 다음 프로그램을 작성했습니다.

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>

#define MAP_PERM_1              (PROT_WRITE | PROT_READ | PROT_EXEC)
#define MAP_PERM_2              (PROT_WRITE | PROT_READ)

#define MAP_FLAGS               (MAP_ANONYMOUS | MAP_FIXED | MAP_PRIVATE)

#define PG_LEN                  4096
#define align_pg_32(addr)       (addr & 0xFFFFF000)
#define num_pg_in_range(start, end)     ((end - start + 1) >> 12)

inline void __force_pgtbl_alloc(unsigned int start)
{
        volatile int *s = (int *) start;
        *s = *s;
}

int __map_a_page_at(unsigned int start, int whichperm)
{
        int perm = whichperm ? MAP_PERM_1 : MAP_PERM_2;

        if(MAP_FAILED == mmap((void *)start, PG_LEN, perm, MAP_FLAGS, 0, 0)){
                fprintf(stderr,
                        "mmap failed at 0x%x: %s.\n",
                        start, strerror(errno));
                return 0;
        }

        return 1;
}

int __mlock_page(unsigned int addr)
{
        if (mlock((void *)addr, (size_t)PG_LEN) < 0){
                fprintf(stderr,
                        "mlock failed on page: 0x%x: %s.\n",
                        addr, strerror(errno));
                return 0;
        }

        return 1;
}

void sigint_handler(int p)
{
        struct timeval start = {0 ,0}, end = {0, 0}, diff = {0, 0};
        gettimeofday(&start, NULL);
        munlockall();
        gettimeofday(&end, NULL);
        timersub(&end, &start, &diff);

        printf("Munlock'd entire VM in %u secs %u usecs.\n",
                diff.tv_sec, diff.tv_usec);

        exit(0);
}

int make_vma_map(unsigned int start, unsigned int end)
{
        int num_pg = num_pg_in_range(start, end);

        if (end < start){
                fprintf(stderr,
                        "Bad range: start: 0x%x end: 0x%x.\n",
                        start, end);
                return 0;
        }

        for (; num_pg; num_pg --, start += PG_LEN){
                if (__map_a_page_at(start, num_pg % 2) && __mlock_page(start))
                        __force_pgtbl_alloc(start);
                else
                        return 0;
        }

        return 1;
}

void display_banner()
{
        printf("-----------------------------------------\n");
        printf("Virtual memory allocator. Ctrl+C to exit.\n");
        printf("-----------------------------------------\n");
}

int main()
{
        unsigned int vma_start, vma_end, input = 0;
        int start_end = 0; // 0: start; 1: end;

        display_banner();

        // Bind SIGINT handler.
        signal(SIGINT, sigint_handler);

        while (1){
                if (!start_end)
                        printf("start:\t");
                else
                        printf("end:\t");

                scanf("%i", &input);

                if (start_end){
                        vma_end   = align_pg_32(input);
                        make_vma_map(vma_start, vma_end);
                }
                else{
                        vma_start = align_pg_32(input);
                }
                start_end = !start_end;
        }

        return 0;
}

보시다시피 프로그램은 가상 주소 범위를 허용하며 각 범위는 시작과 끝으로 정의됩니다. 그런 다음 인접한 페이지에 서로 다른 권한을 부여하여 각 범위를 페이지 크기의 VMA로 세분화합니다. 프로그램을 중단(SIGINT 사용)하면 munlockall()에 대한 호출이 트리거되고 프로세스가 완료되는 시간이 적절하게 기록됩니다.

이제 0x30000000-0x35000000 범위의 Linux 버전 2.6.34를 사용하는 freescale e500mc에서 실행하면 총 munlockall() 시간이 거의 45초가 됩니다. 그러나 전체 페이지 수(및 잠긴 VMA)가 거의 동일하도록 무작위 순서로 더 작은 시작-끝 범위로 동일한 작업을 수행하면(즉, 반드시 주소를 늘릴 필요는 없음) munlockall( )은 4초를 넘지 않습니다.

Linux 2.6.34를 사용하여 x86_64에서 동일한 작업을 시도했으며 내 프로그램은 -m32 매개변수에 따라 컴파일되었습니다. ppc만큼 명확하지는 않지만 변경 사항이 두 번째가 아닌 첫 번째 경우에는 여전히 8초인 것 같습니다. 1초 단위로.

한편으로는 Linux 2.6.10에서 다른 한편으로는 3.19에서 프로그램을 시도했는데 이러한 큰 차이점은 존재하지 않는 것 같습니다. 게다가 munlockall()은 항상 1초 이내에 완료됩니다.

따라서 문제가 무엇이든 Linux 커널 버전 2.6.34에만 존재하는 것 같습니다.

답변1

tmpfs메모리를 오버커밋하는 경우 디스크에 많은 메모리가 있을 수 있습니다. 닫기를 처리하기 위해 콘텐츠의 페이지를 매겨야 할 수도 있습니다. mlock()많은 양의 추가 메모리가 디스크에 강제로 할당될 수 있습니다. 디스크가 없다고 표시하면 아마도 웹을 통해 읽고 있는 것입니다.

sar모든 통계를 수집하려면 서버가 다운된 동안 실행하세요. ( sar기본적으로 설치되지 않을 수 있습니다.) 5~10초 간격으로 데이터를 파일로 캡처하려면 이 옵션을 사용합니다. 그런 다음 여가 시간에 병목 현상이 발생하는 리소스를 검사할 수 있습니다.

편집: 귀하의 의견에 따르면 페이지 테이블에 잠금 문제가 발생할 수 있습니다. mlock전화는 몇 통이나 하셨나요? 모두 필수인가요? 프로세스를 종료하기 전에 잠긴 메모리 세그먼트 목록을 덤프해 보십시오.

관련 정보