[완료] vmalloc의 메모리 할당 및 해제에 대해서..

Arcyze의 이미지

안녕하세요.. 항상 QnA에서 얻어가기만 했는데 또 이렇게 공부하다가 궁금한점이 생겨 질문까지 올리게 됐습니다-_-;

검색해봐도 나오지 않아서 너무 답답해서 글을 올립니다...

책을 세개 정도 봤는데 알 수가 없네요.

vmalloc에 관한 내용인데, 책에는 vmalloc을 수행하게 되면 커널의 pgd(init프로세스의 pgd) 및 연결되어 있는 pud, pmd, pte

만을 갱신한뒤 vmallc을 호출한 프로세스 p의 페이지 테이블은 수정하지 않고, 프로세스가 접근하면 그때 페이지 폴트 핸들러에서 커널의

페이지 테이블을 보고, 커널의 페이지 테이블 엔트리가 존재하므로 올바른 접근이라 보고 프로세스의 페이지 테이블을 이 커널 페이지

테이블 엔트리로 복사하여 사용한다고 배웠습니다. 그리고 vmalloc이 할당된 '선형주소'를 반환하구요. 여기까지는 이해가 잘 되는데요.

이때 만약에 다른 프로세스 p1이 이전에 vmalloc을 했던 p의 선형주소에는 접근을 할 수 없나요? 커널 영역은 3~4G 부분을 프로세스끼리

같이 공유하니까(init프로세스의 pgd를 통해) p1이 방금 갱신한 p의 커널 선형주소에도 접근할 수 있을것 같은데..

접근이 가능하다면(즉 같은 선형주소에 접근하면) p1도 페이지 폴트가 발생해서 p 가 했던것 처럼 똑같이 페이지 테이블을

복사해오고 잘 쓰다가 해지 하는 시점에 문제가 생길것 같아서요..

이부분이 책에 써있는 부분을 봐서 헷갈리는 부분입니다. vfree로 vmalloc을 통해 할당 받은 메모리를 해지할때, 해당

'프로세스의 페이지 테이블 엔트리'을 지우지 않고, '커널 페이지 테이블 엔트리'만 지운다는것 같은데(사실 제가 소스를 보면

pte_offset_kernel함수로 받은 pte를 ptep_get_and_clear하니까 이게 맞는것 같은데요) 이렇게 되면 커널 페이지 테이블은

지웠지만 프로세스 페이지 테이블은 지우지 않았으므로 프로세스에서는 계속 접근이 가능하지 않을까요?(프로세스 페이지 테이블은

존재하니까 페이지 폴트는 발생하지 않죠?) 위의 다른 p1이 접근 가능했다면 p1 또한 잘 그 주소를 잘 접근하구요..

너무 두서없이 질문 한것 같아서 죄송합니다 ㅠㅠ; 정리하자면

1. vmalloc으로 할당받은 선형주소를 다른 프로세스에서 접근 가능한가

2. vfree로 선형주소를 해지 하면(vm_struct도 함께) '커널 페이지 테이블 엔트리'만 지우는가.
(만약 프로세스 페이지 테이블 엔트리를 지운다면 vfree 가 호출하는 함수중 어디에서 지우는지..)

이 두가지입니다. 혹시 아시는분이 계시다면 알려 주시면 정말 감사하겠습니다.. 몇일동안 이거땜에 고민하고 있는지 모르겠네요

참, 제가 위에서 언급한 pte_offset_kernel 함수 및 ptep_get_and_clear 함수를 쓰는 곳은

vfree -> __vunmap -> remove_vm_area -> __remove_vm_area -> unmap_vm_area -> unmap_kernel_range ->.....생략-_-; ->

에서 결국 호출하는 vunmap_pte_range() 함수입니다.(소스는 2.6.23 입니다)

headbang의 이미지

헐...

32-bit i386 아키텍쳐 리눅스 커널 2.6을 기준으로 기억나는 대로 말씀드리겠습니다.

언급하신 대로 vmalloc을 수행하면 커널의 pgd(init프로세스의 pgd) 및 연결되어있는 pud, pmd, pte 만을 갱신한 뒤, 프로세스가 접근하면 그때 페이지 폴트 핸들러에서 커널의 페이지 테이블을 보고, 커널의 페이지 테이블 엔트리가 존재하므로 올바른 접근이라 보고 프로세스의 페이지 테이블을 이 커널 페이지 테이블 엔트리로 복사합니다.
다만 커널 공간에 해당하는 최하위 페이지 테이블--그러니까 Page Global Directory, Page Upper Directory, Page Middle Directory 말고 Page Table, 이하 Page Table 이라 하겠습니다--는 init 프로세스와 모든 다른 프로세스와 공유합니다. 이 사실만 알면 나머지는 쉽게 이해하시리라 믿습니다.

vfree 할 때 단지 init 프로세스의 해당 Page Table의 엔트리만 지우면 다른 모든 프로세스는 해당 주소를
접근하지 못합니다--페이지 폴트가 나겠죠.

다시 정리해서 말하자면 init 프로세스와 모든 프로세스는 Page Table을 공유하며, vfree가 수행되면 init 프로세스의 해당 Page Table 엔트리를 지우며, 따라서 다른 프로세스는 해당 주소를 접근하지 못합니다. 그 Page Table 엔트리가 지워졌으까요.

Arcyze의 이미지

아..
말씀하신 부분이 init 프로세스의 pgd, pud, pmd는 따로 가질 수 있지만, 마지막 pte 부분은 프로세스마다 커널 pte의 페이지 프레임 부분을(pte의 상위--pmd나 pgd등--엔트리에 인덱스로) 가리키고 있기 때문에 '공유' 한다는 말씀이신가요 ^^;?
(제가 생각하는 그림을 첨부 했습니다.)

근데 책을 보면 커널 페이지 테이블에서 해당 페이지 테이블 엔트리를 프로세스의 페이지 테이블 엔트리로 "복사"한다고 되어 있는데, 이 말에 따르면 제 그림이랑은 틀려지는것 같습니다. 사실 제가 이 "복사" 때문에 헷갈리는것인데 커널 페이지 테이블의 엔트리를 프로세스것으로 복사한다는것은 그다음부터는 커널 페이지 테이블 엔트리를 안본다는것 아닌가요?

"공유" 한다면 제가 생각하는 그림처럼 되야 하는것은 아닌지..(페이지 테이블의 상위를 복사)

아무튼 답변 정말 감사합니다!

p.s headbang님 예전에 답글을 달아주신 것중에(제 질문이 아니었지만) 커널주소 공간의 맵핑에 답글을 보고 영구 맵핑영역과
임시 맵핑 영역을 이해하는데 정말 큰 도움이 되었는데 우연찮게 또 이런 도움을 받게 되네요. 다시 한번 감사드립니다 ^^

댓글 첨부 파일: 
첨부파일 크기
Image icon temp.JPG14.5 KB
headbang의 이미지

그림 맞게 그리셨습니다.

그러니까 Page Global Directory, Page Upper Direcoty, Page Middle Directory를 저장하기 위한 페이지 프레임은 init 프로세스와 다른 프로세스가 각각 할당해서 씁니다.
페이징 시스템에서 최하위 테이블인 Page Table은 따로 할당하지 않고 init 프로세스가 할당한 Page Table을 가르킵니다.

"근데 책을 보면 커널 페이지 테이블에서 해당 페이지 테이블 엔트리를 프로세스의 페이지 테이블 엔트리로 "복사"한다고 되어 있는데,"
==> 여기서 말하는 페이지 테이블은 Page Global Directory, Page Upper Direcoty, Page Middle Directory 이겠죠.

그건 그렇고 처음부터 질문 내용이 예사롭지 않았습니다. 덜덜덜.
Arcyze님의 능력이라면 제가 말씀드린 내용이 말이 되는지 안되는지는 금방 알 수 있으리라 생각됩니다.

Arcyze의 이미지

제 맘을 완전히 헤아리셧군요. 제가 방금 headbang님의 댓글을 보고 책에 대한 그 부분의 언급을 말씀하신대로

"여기서 말하는 페이지 테이블은 Page Global Directory, Page Upper Direcoty, Page Middle Directory 이겠죠.?"

이런식으로 물어보려고 했는데 혹시나 해서 백스페이스로 나가보니 댓글을 수정하셧군요 ^^;;

정성어린 답변에 눈물이 ㅠㅠ

그나저나 그럼 소스는 대체 어떻게 되었나를 보니까 확인이 되네요(그동안 제가 소스를 대충봤군요 -_-;)

do_page_fault 함수가 호출하는 vmalloc_sync_one 함수 중에서..
 
         pud = pud_offset(pgd, address);      // 프로세스의 pud
         pud_k = pud_offset(pgd_k, address);   // 커널의 pud
         if (!pud_present(*pud_k))
                 return NULL;
 
         pmd = pmd_offset(pud, address);
         pmd_k = pmd_offset(pud_k, address);
         if (!pmd_present(*pmd_k))
                 return NULL;
         if (!pmd_present(*pmd)) {
                 set_pmd(pmd, *pmd_k);
                 arch_flush_lazy_mmu_mode();
         } else
                 BUG_ON(pmd_page(*pmd) != pmd_page(*pmd_k));
         return pmd_k;

코드를 보면 pmd까지만 커널의 엔트리를 얻고 그 pmd_k를 프로세스의 pmd로 set_pmd해주는게 해당 코드인것 같네요.
아 정말 덕분에 속이 후련합니다 ^^

headbang의 이미지

여기까지 오셨으니 비밀을 하나 말씀드리겠습니다. 위에 제 답변엔 의도적으로 틀린 부분이 있습니다.

"최하위 단계의 페이지 테이블" --> "일정 단계 이하의 페이지 테이블"이 더 정확합니다.

i386의 PAE가 disable 되어 있는 경우와 그렇지 않은 경우를 생각해보죠.

여기서 환기하고 넘어갈 점은 리눅스에서 사용자 주소 공간 (통상 0 ~ 3GB)는 프로세스 마다 다르고 커널 주소 공간 (통상 3 ~ 4GB)는 모든 프로세스가 공유한다는 사실입니다.

1) i386의 PAE가 disable 되어 있을 때,
Page Global Directory, Page Table의 2 단계 페이징을 합니다.
Page Global Directory의 처음 768개의 엔트리는 사용자 주소 공간을 가르키고 나머지 128개의 엔트리는 커널 주소 공간을 가르킵니다. 따라서 Page Global Directory는 프로세스끼리 공유가 절대 불가능 합니다. 처음 768개의 엔트리가 서로 값을 가지거든요.
다만 커널 주소 공간을 매핑하는 Page Table은 공유할 수 있죠.
이 경우는 "최하위 단계의 페이지 테이블"을 공유합니다.

2) i386의 PAE가 enable 되어 있을 때,
Page Global Directory, Page Upper Directory, Page Table의 3 단계 페이징을 합니다.
Page Global Directory는 4개의 엔트리를 가지며 처음 3개의 엔트리는 사용자 주소 공간을 가르키고 나머지 1개의 엔트리는 커널 주소 공간을 가르킵니다. 따라서 Page Global Directory는 프로세스끼리 공유가 절대 불가능 합니다. 처음 3개의 엔트리는 프로세스마다 서로 다른 값을 가집니다. 하지만 마지막 1개의 엔트리는 커널 공간을 가르키므로 모든 프로세스가 동일값을 가져도 되겠죠.
즉, 커널 주소 공간을 매핑하는 Page Upper Directory와 Page Table 모두를 공유할 수 있습니다.
이 경우는 "일정 단계 이하의 페이지 테이블"을 공유하는 게 되겠죠.

다른 아키텍쳐에서도 동일한 논리가 적용됩니다. 정리하자면 이렇습니다.
1. 모든 프로세스는 커널 주소 공간을 공유한다.
2. 따라서 모든 엔트리가 커널 주소 공간만을 가르키는 페이지 테이블이라면 공유가 가능하다.
3. 최상위 단계의 페이지 테이블인 Page Global Directory 처럼 사용자 공간을 가르키는 엔트리가 있으면 공유가 불가능하다.

Arcyze의 이미지

PAE가 처음 최상위 테이블이 4개의 엔트리를 가지는것은 알았는데 마지막 1개를 커널 부분을 가리키게 할 수 있었군요.

생각해보니 0~4G 부분을 네부분으로 나누고 마지막 1G 부분을 커널 부분으로 할당하는게 이치에 맞는것 같네요.

추가적인 답변까지 해주시다니 덜덜덜... 복받으세요 ^^

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.