2014-01-16 8 views
3

주소를 가져 오는 함수를 구현하는 방법을 찾고이 주소에 사용 된 페이지 크기를 알려줍니다. 하나의 솔루션은/proc // smaps에있는 세그먼트의 주소를 찾고 "KernelPageSize :"의 값을 반환합니다. 이 솔루션은 파일을 선형으로 읽는 것을 포함하기 때문에 속도가 매우 느립니다. 더 빠르고 효율적인 솔루션이 필요합니다.특정 주소의 페이지 크기를 프로그래밍 방식으로 가져 오는 방법은 무엇입니까?

시스템 호출이 있습니까? (int getpagesizefromaddr (void * addr);) 그렇지 않으면 페이지 크기를 추산 할 수있는 방법이 있습니까?

+0

'/ proc/PID/smaps'는 파일이 아닙니다. 커널 *이 읽을 때 * 제공하는 의사 파일입니다.('open()'/'read()'/'close()'시스템 호출은 커널로 하여금 데이터를 생성하게하고, 필요에 따라 파일 시스템을 거치지 않고 놀랍도록 가벼운 시스템입니다.) 특히 "unistd.h"I/O와 강력한 구문 분석기 기능을 사용하는 경우 "매우 느리지"않습니다. 'stdio.h'는 모든 기능을 갖추고 있지만 느리지 만 피하십시오. –

+0

정확 합니다만, 느린 해결책은 파일 I/O로 인해 느리지 만 문자열 처리로 인해 느려지지는 않았습니다. 내가 한 테스트에서, 스맥 파일은 500K 줄 이상을 가질 수 있기 때문에 최악의 경우 실행 시간이 길다. – Eldad

+1

다른 주소의 페이지 크기가 다른가요? 그렇지 않다면 어쩌면 이것이 필요한 것이다.'sysconf (_SC_PAGESIZE)' –

답변

3

많은 Linux 아키텍처가 "거대한 페이지"를 지원합니다 (자세한 내용은 Documentation/vm/hugetlbpage.txt 참조). 예를 들어 x86-64에서 sysconf(_SC_PAGESIZE)은 4096을 페이지 크기로보고하지만 2097152 바이트의 거대한 페이지도 사용할 수 있습니다. 응용 프로그램의 관점에서 볼 때 이것은 거의 중요하지 않습니다. 커널은 사용자 공간 애플리케이션이 걱정할 필요없이 필요에 따라 한 페이지 유형에서 다른 페이지 유형으로 완벽하게 변환 할 수 있습니다.

그러나 특정 작업 부하의 경우 성능이 인 경우 중요합니다. 투명 투명한 페이지 지원 (Documentation/vm/transhuge.txt 참조)이 개발 된 이유입니다. 이는 가상 환경, 즉 작업 부하가 게스트 환경에서 실행되는 가상 환경에서 특히 두드러집니다. 새 조언 플래그 MADV_HUGEPAGEmadvise()에 대해 응용 프로그램이 커널에 환경 설정을 알릴 수 있으므로 mmap(...MAP_HUGETLB...)은 이러한 성능상의 이점을 얻을 수있는 유일한 방법은 아닙니다.

저는 개인적으로 Eldad의 게스트가 게스트 환경에서 실행되는 작업 부하와 관련되어 있다고 가정하고 벤치마킹 중에 페이지 매핑 유형 (일반 페이지 또는 거대한 페이지)을 관찰하여 특정 작업 부하에 대해 가장 효과적인 구성을 찾습니다. 가능하면

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

#define PAGES 1024 

int main(void) 
{ 
    FILE *in; 
    void *ptr; 
    size_t page; 

    page = (size_t)sysconf(_SC_PAGESIZE); 

    ptr = mmap(NULL, PAGES * page, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_HUGETLB, -1, (off_t)0); 
    if (ptr == MAP_FAILED) { 
     fprintf(stderr, "Cannot map %ld pages (%ld bytes): %s.\n", (long)PAGES, (long)PAGES * page, strerror(errno)); 
     return 1; 
    } 

    /* Dump /proc/self/smaps to standard out. */ 
    in = fopen("/proc/self/smaps", "rb"); 
    if (!in) { 
     fprintf(stderr, "Cannot open /proc/self/smaps: %s.\n", strerror(errno)); 
     return 1; 
    } 
    while (1) { 
     char *line, buffer[1024]; 

     line = fgets(buffer, sizeof buffer, in); 
     if (!line) 
      break; 

     if ((line[0] >= '0' && line[0] <= '9') || 
      (line[0] >= 'a' && line[0] <= 'f') || 
      (strstr(line, "Page")) || 
      (strstr(line, "Size")) || 
      (strstr(line, "Huge"))) { 
      fputs(line, stdout); 
      continue; 
     } 
    } 

    fclose(in); 
    return 0; 
} 

위의 할당에게 큰 페이지를 사용하여 1024 페이지 :

는의가, huge.c을 실제 예를 나타내는 모든 오해를 보자. (x86-64에서는 거대한 페이지가 2 MiB 또는 512 일반 페이지이므로 개인용 익명 메모리 2 큰 페이지 또는 4 MiB를 할당해야합니다. 다른 아키텍처에서 실행하는 경우 PAGES 상수를 조정하십시오.)

/proc/sys/vm/nr_hugepages이 0보다 큰지 확인하여 막대한 페이지가 활성화되어 있는지 확인하십시오. 대부분의 시스템은 기본적으로 제로, 등등 사용 가능한 10 거대한 페이지 (- 64에서 20은 MIB)의 풀을 유지하기 위해 커널을 알려줍니다

sudo sh -c 'echo 10 > /proc/sys/vm/nr_hugepages' 

를 사용하여 예를 들어, 인상 할 필요가있다. 당신이 단축 /proc/PID/smaps 출력을 얻을 것이다

gcc -W -Wall -O3 huge.c -o huge && ./huge 

컴파일하고 위의 프로그램을 실행합니다. 내 컴퓨터에서 흥미로운 부분은 분명히 전형적인 부분과 다르다. 예를 들어, 흥미로운 부분은

이다.

01830000-01851000 rw-p 00000000 00:00 0 [heap] 
Size:    132 kB 
AnonHugePages:   0 kB 
KernelPageSize:  4 kB 
MMUPageSize:   4 kB 

전체 /proc/self/smaps 파일의 정확한 형식은 man 5 proc 설명하고 분석하는 것은 매우 간단합니다.이것은 커널에 의해 생성 된 의사 파일이므로 결코 지역화되지 않습니다. 공백 문자는 HT (코드 9) 및 SP (코드 32)이고 개행 문자는 LF (코드 10)입니다.


내 추천 방법은 가능한 경우 하나 개의 스레드가 다른 스레드 동안 영역 어레이를 검사하는 lock 부재만을 필요 예

struct region { 
    size_t start; /* first in region at (void *)start */ 
    size_t length; /* last in region at (void *)(start + length - 1) */ 
    size_t pagesize; /* KernelPageSize field */ 
}; 

struct maps { 
    size_t   length; /* of /proc/self/smaps */ 
    unsigned long hash;  /* fast hash, say DJB XOR */ 
    size_t   count; /* number of regions */ 
    pthread_rwlock_t lock;  /* region array lock */ 
    struct region *region; 
}; 

위한 매핑을 설명하는 구조를 유지하는 것 업데이트 또는 교체 중입니다.

원하는 간격으로 /proc/self/smaps 의사 파일을 읽고 빠르고 간단한 해시 (또는 CRC)를 계산한다는 아이디어가 있습니다. 길이와 해시가 일치하면 매핑이 변경되지 않았다고 가정하고 기존 정보를 다시 사용합니다. 그렇지 않으면 쓰기 잠금이 수행되고 (정보가 이미 부실), 매핑 정보가 구문 분석되고 새 region 배열이 생성됩니다.

다중 스레드 인 경우 lock 구성원은 여러 개의 동시 판독기를 허용하지만 버려진 region 배열을 사용하는 것을 방지합니다.

참고 : 속성 행이 모두 대문자 ASCII 문자 (A-Z, 코드 65-90)로 시작하므로 해시를 계산할 때지도 항목 수를 계산할 수도 있습니다. 즉, 소문자 16 진수 (0 - 9, 코드 48 - 57 또는 a - f, 코드 97 - 102)로 시작하는 줄 수는 설명 된 메모리 영역의 수입니다.


C 라이브러리에 의해 제공되는 기능

, mmap(), munmap(), mremap(), madvise() (및 posix_madvise()) mprotect(), malloc(), calloc(), realloc(), free(), brk()sbrk()는 (메모리 매핑을 변경할 수 I 있지만 이 목록에 모두 포함되어 있는지 확실하지 않습니다.) 이러한 라이브러리 호출을 삽입 할 수 있으며 각 (성공한) 호출 후에 메모리 영역 목록이 업데이트됩니다. 이것은 응용 프로그램이 정확한 정보를 얻기 위해 메모리 영역 구조에 의존 할 수 있어야합니다.

개인적으로이 기능을 미리로드 라이브러리 (LD_PRELOAD을 사용하여로드 됨)로 생성합니다. 따라서 위의 함수를 몇 줄의 코드로 쉽게 삽입 할 수 있습니다. 삽입 된 함수는 원래 함수를 호출하고 성공하면 /proc/self/smaps에서 메모리 영역 정보를 다시로드하는 내부 함수를 호출합니다. 원래 메모리 관리 기능을 호출하고 errno을 변경하지 않도록주의해야합니다. 그렇지 않으면 매우 간단해야합니다. 필자는 개인적으로 필드를 구문 분석하기 위해 라이브러리 함수 (예 : string.h)를 사용하지 않을 것이지만 어쨌든 지나치게주의를 기울입니다.

삽입 된 라이브러리는 분명히 특정 주소 (예 : pagesizeat())의 페이지 크기를 쿼리하는 기능을 제공합니다. (응용 프로그램에서 항상 -1을 반환하는 약한 버전을 errno==ENOTSUP으로 내보내는 경우 미리로드 라이브러리가이를 무시할 수 있으며 프리로드 라이브러리가로드되는지 여부에 대해 걱정할 필요가 없습니다. 그렇지 않은 경우 함수는 오류.)

질문이 있으십니까?

+0

감사합니다. 구문 분석을 유지하는 솔루션은 내가 필요로하는 것이 아닙니다. 그것은 간단하지 않고 DB를 할당하고 유지 관리해야합니다. 마지막으로 해결 방법은이 문제를 해결하는 것입니다. 어쨌든 고마워, . – Eldad

+1

@ Eldad : 현명한 소리가납니다. 대답에서 말했듯이 실제로 페이지 크기를 알아야 할 필요는 거의 없습니다. 성능상의 이점조차도 ​​커널 힌트를 제공하는 것만으로도 정상적인 페이지로 넘어갈 수 있습니다. 그런 다음 다시 한 번 페이지 크기를 얻는 방법에 대해 질문했습니다. 왜 당신은 누군가가 당신의 실제 문제와 맥락이 무엇인지 알기를 기대할 수 있습니까? 귀하가 결정한 솔루션에 대한 도움을 요청하는 대신 실제의 원래 문제에 대해 설명하고 질문해야합니다. –

+0

죄송합니다. 완전히 명확하지 않았기 때문에 효율적이지 않으므로 '스왑'읽기 및 구문 분석과 관련된 솔루션을 원하지 않습니다. – Eldad