리눅스에서 logical block address 얻기

kbj0607의 이미지

리눅스에서 현재 제 프로그램이 접근하고 있는 logical block address를 출력해보고자 합니다. 리눅스를 접한지 얼마 안되서 힘드네요ㅠ
구체적으로는 SQLite3 에서 현재 제 임시 파일의 logical address를 확인해보려고 합니다. 방법이 있을까요? 구글링 해도 잘 안나오고 전부 physical address 얘기만 나오네요.
저는 physical address로 매핑되기 이전의 logical address를 얻고 싶습니다.

그리고 제가 이해하고 있는 개념이 맞는지 잘 모르겠습니다.
SQLite3에서 임시파일이 실제로 write 되는 곳은 disk일텐데. 어찌되었든 MMU에서 logical address(제가 알기론, 리눅스에서는 logical address=linear address = virtual address인데요,, 이것도 맞나요?) 를 physical address로 매핑해주는 매핑 table이 있고 저는 이 부분에서 메모리수준에서의 주소인 logical address를 얻고싶습니다.

질문을 요약하자면
1. 리눅스에서는 segmentation을 안써서 logical address=linear address = virtual address가 맞나요?
2. 제 프로그램에서 접근하고 있는 logical block address를 출력하는 방법은?

며칠째 고생중입니다. ㅠㅠ

chanik의 이미지

질문 안에 디스크 이야기도 있고 메모리 이야기도 있어서 헷갈리는군요.

> 1. 리눅스에서는 segmentation을 안써서 logical address=linear address = virtual address가 맞나요?

리눅스 뿐 아니라 윈도95 이후버전 등 보호모드에서 동작하는 OS에서 다 성립하는 말 같습니다.

각 프로세스는 자신만의 virtual address 공간에서 동작하고 physical address로의 매핑은 커널에서 MMU를 이용해서 알아서 하죠.
32-bit OS라면, 각 프로세스가 쓰는 virtual address 공간은 segmentation 없는 linear한 4GB 공간이고요.
64-bit OS라면 더 큰 virtual address 공간을 쓸 수 있겠죠.

> 2. 제 프로그램에서 접근하고 있는 logical block address를 출력하는 방법은?

이것이 메모리 관련 질문이라면, 아래 샘플에서처럼 아무 변수에 대해서나 그 포인터를 출력하면
그것이 바로 그 변수의 virtual address 입니다. logical address라고 불러도 무방한 것 같고요.

$ cat virtual_addr.c
#include <stdio.h>
#include <stdlib.h>
 
char p_global[] = "Hello";
 
int main()
{
        int i = 1;
        char p_stack[]  = "Hello";
        char *p_literal = "Hello";
        char *p_heap    = (char *)malloc(16);
 
        printf("virtual address of i in stack = 0x%X\n", &i);
        printf("virtual address p_stack       = 0x%X\n", p_stack);
        printf("virtual address p_heap        = 0x%X\n", p_heap);
        printf("virtual address p_literal     = 0x%X\n", p_literal);
        printf("virtual address p_global      = 0x%X\n", p_global);
 
        free(p_heap);
 
        return 0;
}
$ gcc -o virtual_addr virtual_addr.c
$ ./virtual_addr
virtual address of i in stack = 0x8F7AEE1C
virtual address p_stack       = 0x8F7AEE10
virtual address p_heap        = 0x72F5010
virtual address p_literal     = 0x4006D8
virtual address p_global      = 0x600A64

만약 메모리가 아닌 디스크 관련 질문이었다면,
특정 파일을 위한 디스크 접근은 virtual address나 MMU 등과 직접적인 관련이 없습니다.
이 경우라면 질문을 좀 더 정리해서 올려주시면 좋겠습니다.

kbj0607의 이미지

답변 감사드립니다. 일단 저도 답변을 보고 공부를 더 하면서 제가 메모리와 디스크를 헷갈려 했다는 것을 깨달았습니다;;
결론적으로는 제가 원하고자 하는 address는 디스크 (제가 가정하는 환경은 SSD 입니다.)의 logical block address 입니다.
디스크 상에서도 physical address(진짜로 디스크에 쓰여지는 주소)의 fragmentation 현상 때문에, OS(리눅스)와 디스크 사이에서 mapping table(logical block address to physical block address)을 이용하여 disk 바깥쪽에서 보는 address를 logical block address로 보이게 해주는 것으로 알고있습니다. 즉 제가 logical block address를 얻는다는 것은 OS 입장에서 보는 disk의 physical address(용어를혼동해서 쓰게 되는데.. 이 주소는 OS가 디스크를 바깥에서 보는 주소입니다.)를 구하면 되는 것인가요?
즉, OS에서 디스크쪽으로 전달되는 주소를 뽑으면 되는 것 같은데... 맞나요? 맞다면... 구체적으로 방법을 알려주시면 감사하겠습니다.

chanik의 이미지

우선 강조하고 싶은 부분이 있는데요.

  • SSD를 가정하신다면 단편화(fragmentation)는 신경쓰지 않으셔도 될 것입니다. 단편화는 기계적인(mechanical) 동작요소가 들어간 하드디스크에서는 영향이 있지만 전자적인 동작만으로 이뤄진 SSD같은 장치에는 상관이 없죠.
  • 그리고, 파일시스템과 디스크 사이에서 이뤄지는 매핑의 복잡도는 메모리상의 가상주소와 물리주소 사이에 이뤄지는 매핑에 비해 현저히 낮습니다. 파일시스템 상의 블록번호가 증가하면 디스크상의 블록번호(혹은 섹터번호)도 함께 linear하게 증가하는 식입니다. 다만 파일시스템이 디스크나 파티션에 바로 올라가지 않고 레이드 볼륨 위에 올라간다면 파일시스템 블록주소와 디스크상의 물리블록주소 사이에는 복잡한 매핑관계가 생길 수 있죠.
ext2/3/4 상에서 특정 파일의 블록주소를 알아내려면 아래와 같이 e2fsprogs 패키지에 포함된 filefrag를 이용하면 됩니다. 다른 파일시스템의 경우에도 뭔가 방법이 있겠지만 저도 모르겠습니다.

$ ls -al largefile.dat
-rwx------ 1 tester tester 262909363 12월  1  2009 largefile.dat
$ sudo filefrag -v largefile.dat
Checking largefile.dat
Filesystem type is: ef53
Filesystem cylinder groups is approximately 7185
Blocksize of file largefile.dat is 4096
File size of largefile.dat is 262909363 (64187 blocks)
First block: 28532957
Last block: 30394554
Discontinuity: Block 3 is at 28541442 (was 28532959)
Discontinuity: Block 9 is at 28574210 (was 28541447)
Discontinuity: Block 14 is at 30310914 (was 28574215)
Discontinuity: Block 20 is at 30329285 (was 30310919)
Discontinuity: Block 13889 is at 30343688 (was 30343167)
Discontinuity: Block 46105 is at 30376456 (was 30375935)
largefile.dat: 7 extents found, perfection would be 3 extents
chanik의 이미지

위 파일을 예제로 삼아 첫번째 블록의 디스크상의 물리적인 위치를 추적해 보겠습니다.

[1] 파일시스템 내에서의 offset

filefrag 실행결과를 보면 블록크기는 4096B 즉 4KB이고 첫번째 블록의 주소는 28532957 입니다. 즉, 파일시스템이 올라가있는 공간(파티션일 수도 있고, 디스크일 수도 있고, 레이드볼륨일 수도 있고, ...) 안에서의 이 블록의 offset은 4KB * 28532957 이 됩니다.

[2] 디스크 상에서의 파일시스템 자체의 offset

위 파일을 포함한 파일시스템이 올라가 있는 디스크의 파티션 레이아웃은 아래와 같습니다.

$ sudo fdisk -lu /dev/sda
 
Disk /dev/sda: 1000.2 GB, 1000204886016 bytes
255 heads, 63 sectors/track, 121601 cylinders, total 1953525168 sectors
Units = sectors of 1 * 512 = 512 bytes
 
   Device Boot      Start         End      Blocks   Id  System
/dev/sda1   *           1  1913357564   956678782   fd  Linux raid autodetect
/dev/sda2      1913357565  1953520064    20081250    f  W95 Ext'd (LBA)
/dev/sda5      1913357628  1953520064    20081218+  82  Linux swap / Solaris

파일시스템은 첫번째 파티션인 /dev/sda1 에 올라가 있습니다. 이 파티션은 Start 주소가 1이므로 디스크의 두 번째 섹터에서 시작됩니다. 이 디스크가 512 바이트 섹터를 쓰고 있다고 나오므로 이 파티션의 시작 offset은 512B * 1 이 됩니다.

[3] 디스크상에서의 물리적 offset 계산

위의 두 결과를 종합해보면, 물리적 offset은 아래와 같이 계산됩니다.

파티션 offset + 파티션 내에서의 offset = (512B * 1) + (4KB * 28532957) = 116870992384

이것은 바이트 단위의 offset이고, 섹터단위로는 116870992384 / 512 = 228263657 섹터가 됩니다. 사용자 프로그램이 파일시스템을 통해 이 파일의 첫 번째 4K 블록을 읽어내려고 하면, 커널 안에 있는 디스크 디바이스 드라이버는 결국 228263657번째 섹터부터 8개의 섹터를 디스크로부터 읽어내게 될 것입니다.

[4] 검증

이 위치가 정말 해당 파일의 첫 섹터가 맞는지 확인해보겠습니다.

#
# 파일의 첫번째 블록 4KB를 읽어서 blk_from_file.dat 파일로 저장합니다.
# (파일시스템을 통해서 읽어들임)
#
$ dd if=largefile.dat of=blk_from_file.dat bs=4096 count=1
1+0 records in
1+0 records out
4096 bytes (4.1 kB) copied, 2.7e-05 seconds, 152 MB/s
#
# 이번에는 디스크의 228263657 섹터로부터 4KB를 읽어서 blk_from_disk.dat 파일로 저장합니다.
# (파일시스템을 거치지 않고 디스크로부터 바로 읽어들임)
#
$ sudo dd if=/dev/sda of=blk_from_disk.dat skip=228263657 bs=512 count=8
8+0 records in
8+0 records out
4096 bytes (4.1 kB) copied, 5.6e-05 seconds, 73.1 MB/s
#
# 두 파일을 비교합니다.
#
$ diff blk_from_file.dat blk_from_disk.dat && echo no difference
no difference

두 내용물에 차이가 없으므로 이런 단순계산법이 옳음을 알 수 있습니다.
레이드나 볼륨매니저 등의 중간계층이 개입되어 있는 경우엔 물론 좀 복잡해집니다.

kbj0607의 이미지

일단 프로그램을 활용해서 주소를 알아내는 방법은 잘 배웠습니다.
그런데 제가 SQLite3라는 db 프로그램에서 현재 이 프로그램이 접근하고 있는 주소의 log를 얻고 싶습니다. 그러니까 SQLite3 소스를 수정하여 화면상으로 현재 접근중인 physical address를 실시간으로 확인하거나 log파일을 만들려고 합니다. OS 내부 method중에서 physical address를 얻을 수 있는 것이 있나요?
리눅스, 파일시스템을 잘 모르는 상태에서 코드를 수정하고 있어서 어려움을 많이 겪네요..

jick의 이미지

일단 "SQLite3라는 db 프로그램에서 현재 이 프로그램이 접근하고 있는 주소의 log를" 얻으려고 하는 목적이 뭔지 얘기해 주시는 게 나을 것 같은데요?

지금까지 설명으로 봐서는 "접근중인 주소를 실시간으로 확인"할 수 있다고 해도 (그게 되는지도 잘 모르겠지만) 그 결과가 원하시던 원래 목표와는 아무 상관이 없을 가능성이 상당히 높아보이는데요...

kbj0607의 이미지

궁극적으로 하고 싶은 것은 "내 시스템이 접근하고 있는 logical block address 값 뽑아내기" 입니다. 굳이 SQLite3 프로그램에 한정시킬 필요는 없을 것 같습니다. 사실 목적은 여러가지라고 볼 수 있습니다만...
이를 활용하여 특정 주소에 대한 쓰기 동작 특성을 분석하고, address pointer를 조작하여 flash memory 수준에서 여러가지 일들을 해보려고 합니다. 어찌되었든... 이러한 목적을 위해서는 기본적으로 저장장치(flash memory)에서의 접근 log(내역)이 필요한 것만은 확실합니다..

chanik의 이미지

sqlite를 예로 들자면, 지금 원하시는 일은 사용자 프로그램인 sqlite 자체를 수정하더라도 이룰 수 있는 것이 아닙니다. strace 해보니 sqlite는 데이터파일 접근을 위해 lseek()/read()/write() 시스템콜을 이용하더군요. 이들 시스템콜은, 파라미터만 봐도 알 수 있지만, 특정파일 안에서의 offset을 지정할 수 있을 뿐 파일시스템 내에서의 블럭위치나 더 나아가 디스크 상의 블럭위치에 대한 제어 및 정보취득은 불가능하죠.

결국 파일시스템 드라이버나 더 아래쪽의 SCSI 드라이버 등에서 목적을 이루는 수밖에 없겠습니다. 저도 이쪽으로는 잘 모릅니다. 이전 KLDP 글(https://kldp.org/node/117094)이 있어 좀 더 찾아보니 커널소스를 브라우즈하며 볼 수 있는 http://lxr.free-electrons.com/ident?i=ext2_get_block 페이지가 있더군요. 그리고, "linux scsi trace"로 구글을 뒤지면 http://petroskoutoupis.com/index.php5?title=SCSI_Trace 등의 페이지가 나오기는 하는데, 커널패치를 해서 다시 빌드해야 하거나, 어떤 것은 커널 빌드옵션을 바꿔서 다시 빌드해야 하는 것 같습니다. 어떻게든 목적을 이루시면 좋겠군요.

댓글 달기

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
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.