임베디드 리눅스 개발자를 위한 파일시스템 분석 ②

시작하며지난 연재에서는 정보가전제품을 위한 파일시스템이 가져야 하는 특성을 살펴보았고, 이를 위하여 필요한 기술 요소가 무엇인지를 알아보았다. 이번 회에서는 Linux에서 다양한 파일시스템의 특성을 간단히 소개하고, 이런 다양한 파일시스템을 가능하게 하는 Linux Virtual Filesystem(VFS)의 구성에 대해 설명한다. 또한 간단한 파일시스템 코드를 작성하여 볼 것이다.이번 글은 독자들로 하여금 Linux VFS를 이해하고 간단한 파일시스템 코드를 통해 개별 파일시스템을 이해하는 데 도움을 주기 위한 목표로 내용을 작성했다. 물론 Linux는 다양한 파일시스템을 지원하고 있어서 대부분의 경우 파일시스템을 새로 개발하는 일이 많지 않지만(거의 없지만), 개발하려는 제품의 특성에 따라 기존 파일시스템 코드를 일부 수정해야 하는 경우가 많다. 특히 Linux 파일시스템들은 서버나 데스크톱 활용에 최적화된 경우가 많아서 대용량의 멀티미디어 데이터를 처리하는 정보가전제품에 적용하기 위해서는 최적화가 필요하며, VFS 및 개별 파일에 대해 이해하는 것은 제품에 파일시스템을 적용하여 최적화하는 경우 큰 도움이 될 것이다.연재 중 “Virtual Filesystem” 부분은 Understanding Linux Kernel 3rd Edition, Linux Kernel Development 2nd Edition, Linux Kernel 2.6의 Documentation/filesystems/ vfs.txt을 참고하였음을 미리 밝힌다. 또한 “실제로 만들어 보기”는 Ottawa Linux Symposium 2006의 Tutorial Session에서 Steve French가 진행한 “Linux Filesystems in 21days 45 minutes”를 기본으로 작성했다. 필자도 비슷한 자료를 만들어 사내 교육용으로 쓴 경험도 있지만, 올해 OLS에서 이 자료를 보고 또한 그의 프레젠테이션을 들으며 대가(大家)의 설명 방식에 감탄했다. 그래서 독자들과 공유하고 싶은 생각이 들어, 이를 기본으로 연재를 작성하게 되었다(솔직히 고백하면 필자는 이 자료보다 더 잘 만들 수 없었다).또한 이번 연재를 이해하기 위해서는 Linux 커널 모듈과 모듈 컴파일 그리고 커널 Configuration에 대해 어느 정도의 지식이 필요하며, 커널 및 디바이스 드라이버 디버깅에 대한 지식이 있다면 “실제로 만들어 보기”편을 독자가 따라서 진행하면 큰 도움이 될 것이다.Virtual Filesystem(VFS)Linux가 다양한 파일시스템을 지원하고, 누구나 새로운 파일시스템을 추가할 수 있다는 것은 널리 알려진 장점인데 오히려 이 점 때문에 사용자 입장에서는 어떤 파일시스템을 선택해야 하는지 더 혼란스러운 것 같다. 그래서 VFS에 대해 알아보기 전에 Linux에 기본적인 파일시스템들에 대해 아주 간략히 살펴보기로 한다.Linux FilesystemsLinux Kernel의 fs/ 아래에 보면 많은 Filesystem들이 존재하고 있으며, 2.6.11 기준으로 n개가 기본적으로 지원되는데, 가장 많이 쓰이는 파일시스템과 특징은 아래와 같다.- ext2ext2는 개발된 지 오랜 시간이 지났지만, 아직까지 저널링(journaling)이 필요하지 않은 개인용 PC에서는 성능과 안정성을 보장한다. 오랜 시간 동안 다양한 환경에서 많은 사람들이 쓰고 있고, 그에 따라 최적화되어 있다. 일반적 상황에서는 최선의 선택이나 50byte 정도의 아주 작은 파일을 다량으로 처리하는 경우 성능 감소가 심하다는 보고가 있으며, 저널링 기술을 사용하지 않으므로, 성능은 상대적으로 좋으나 파일시스템 충돌이 발생한 경우 복구를 위해 반드시 외부 유틸리티를 사용해야 하며, 저장 공간의 크기에 따라 시간이 상당히 오래 걸리는 단점이 있다.- ext3ext3는 ext2에 JBD(Journaling Block Device)라는 저널링 모듈을 추가한 것으로, ext2에 저널링 기능을 부여하였으며, ext2와도 100% 호환되어 ext2로 포맷한 디스크에 ext3로 마운트(mount) 및 저널링 기능을 추가하는 것이 가능하다. 저널링 부하(overhead)를 최소화하기 위해 마운트할 때 사용자의 상황에 맞도록 저널 옵션을 선택할 수 있다. 약간의 부하를 견딜 수 있는 경우 현재 Linux 시스템에서 가장 효과적인 선택이다.- XFSXFS는 SGI가 자체 OS인 Irix를 위해 개발한 파일시스템으로 대용량의 데이터 처리 및 래이턴시(latency)에 최적화된 실시간 성능을 갖추고 있으며, Linux 2.4에서 오픈소스화 되었고, Linux 2.6에서 실시간 볼륨(Realtime Volume) 기능까지 추가적으로 오픈되어, 전체 기능을 다 사용할 수 있게 되었다. Linux 2.4에서 한 때 데이터 오염 문제와 충돌 후에 복구가 되지 않는 등의 문제가 있었으나 2.6에서는 모두 해결된 것으로 보이며, 성능에 있어서는 최고 파일시스템 중에 하나이다. 신규로 제작하는 파일시스템들이 자신들의 성능을 BMT할 때 주로 XFS와 비교하고 있음을 누구나 잘 알고 있을 것이다.- ReiserFSReiserFS는 일반적으로 크기의 파일 뿐만이 아니라 작은 파일까지도 디스크 용량 낭비를 없애면서 성능까지 높이기 위해 설계되었는데, 이를 위해 Variable Block Size라는 기술을 이용했다. 즉 일반적인 파일시스템은 포맷 시에 기본적으로 공간을 처리하는 단위인 블록을 고정(1kb~4kb)하는 데 반해 ReiserFS는 블록의 크기를 유동적으로 변경할 수 있다. 예를 들어 900bytes의 데이터를 저장하기 위해 4kb의 블록을 사용할 필요 없이 1kb 블록을 사용하므로 공간의 낭비와 b* balanced tree를 이용하여 효과적인 파일관리도 가능하게 되었다. 하지만 안정화에 대한 이견이 아직도 있다.- (V)FAT(V)FAT는 MS-DOS의 산물로 이미 수명을 다했어야 한다. 그러나 이동식 미디어(USB stick, MMC, SD 등)를 위한 de facto standard가 되어 21세기가 되도록 없어지지 않고 있으며, 만들고자 하는 시스템이 Windows에서 이동식 저장장치를 지원하기 위해서는 반드시 사용해야 하는 파일시스템이다.Virtual Filesystem(VFS)이제부터 설명하는 VFS는 ext2, XFS와 같은 native 파일시스템은 아니며, native 파일시스템이 Linux 커널과 연결될 수 있도록 하는 매개체로 개별 native 파일시스템은 VFS라는 인터페이스를 통해 커널에 인식되며 서로 데이터를 교류할 수 있다. Linux 파일시스템이라면 가져야 할 공통의 오브젝트와 Operation을 정의하고 있어 커널이 파일시스템 작업이 필요한 경우 개별 native 파일시스템에 대한 정보가 없어도 공통 오브젝트와 operation만으로도 충분이 원하는 작업을 수행할 수 있다.또한 파일시스템마다 반복적으로 구현해야 하는 일반적인 operation은 미리 구현을 해 놓고 파일시스템마다 차이가 있을 수 있는 부분만을 함수 포인터로 받아 개별 파일시스템 구현에 도움을 주고 있다. 예를 들면 generic_file_aio_read 같은 함수는 이미 구현이 되어 있어 개별 파일시스템 getblock 함수만 구현하면 이를 이용해 쉽게 read를 구현할 수 있다.그밖에도 여러 개의 개별 파일시스템 사이에서 상대 파일시스템에 대한 정보가 없이도 데이터 교환이 VFS를 통해 가능하며, 사용자 입장에서는 개별 파일시스템의 종류와 상관없이 일관된 인터페이스를 통해 데이터 입출력이 가능하다. 개별 파일시스템이 반드시 구현해야 하는 VFS 인터페이스에 대해선 다음 장에서 살펴본다.중요 오브젝트(Object)VFS에서 모든 개별 파일시스템에 공통적으로 요구하는 중요 오브젝트와 오브젝트에 따른 Operation은 다음과 같다.- Superblock Object와 Operation- Inode Object와 Operation- File Object와 Operation- Dentry Object와 Operation이 외에도 fs_struct, file_strut, file_system_type등이 있으나 Superblock, inode, file, dentry가 VFS의 가장 중요한 오브젝트이며 개별 파일시스템을 구현할 때도 가장 중요한 요소가 된다.Linux는 C 언어로 구현되어 객체 지향적 프로그래밍(OOP)을 적용하기가 쉽지 않은데, 위에서 설명한 VFS의 중요 인자들은 그냥 보기에도 OOP 개념을 적용하여 프로그램 하기에 적당해 보인다. 그래서 실제 VFS와 관련된 많은 코드들은 파일 포인터를 포함한 구조체를 사용하여 C++ 객체와 비슷한 느낌으로 구현되어 있으며, 생성자와 소멸자는 kmalloc과 free를 이용한 wrapper 함수를 통해 대신하고 있다.각 오브젝트들이 많은 필드를 가진 구조체이고 참고 도서를 통해 각 필드에 대해 잘 설명이 되어 있으므로 아래에서는 중요한 개념과 개별 파일시스템 구현 시 필요한 필드에 대해서만 설명하겠다.- SuperblockSuperblock은 Filesystem에 하나가 존재하여 파일시스템에 의존하는 정보를 가지며 파일시스템의 크기 등과 같은 파일시스템의 전체적인 정보를 가지고 있다. 디스크에 저장되어 있다가 마운트할 때 메모리로 올라온다. 성능 문제로 메모리에 존재하는 superblock이 변경될 때 마다 디스크에 변경된 사항을 저장하지 않고 s_dirty라는 필드에 변경되었다는 표시만 하게 된다. 그런 후에 주기적으로 메모리 내용을 디스크에 기록하게 되므로 갑작스러운 시스템 오류 시에 메모리 내용이 디스크에 저장되지 못하는 경우가 발생할 수 있다.개별 파일시스템 구현 시 필요한 부분이 void 포인터 구조체인 s_fs_info로, 개별 파일시스템에서 필요한 정보 중 메모리 존재해야 하는 데이터를 저장한다. 이 정보는 마운트할 때 VFS get_sb에 의해 메모리에 저장되는데, 그림 1은 ext3에서 파일시스템 함수를 통해 VFS get_sb가 디스크에서 읽어온 디스크 superblock 데이터를 이용하여 VFS superblock 내용을 생성하는 모습을 보여준다.VFS는 마운트되는 파일시스템의 superblock 정보를 얻기 위해 VFS call인 get_sb를 호출하게 되는데, 이는 개별 파일시스템에서 구현한 get_sb의 wrapper 함수로 개별 파일시스템의 get_sb는 일반적으로 (filesystem name)_get_sb가 함수명이 된다. 호출된 ext3_get_sb는 VFS 함수인 get_sb_bdev를 호출하여 VFS superblock object를 생성하게 되는데 이 때 디스크에 있는 superblock 오브젝트를 읽어오는 역할은 개별 파일시스템에서 fill_super 함수를 통해 수행된다.즉, superblock은 VFS superblock과 디스크에 존재하는 파일시스템 별 superblock이 있으며 fill_super를 통해 파일시스템 별 superblock을 읽어와 VFS superblock을 완성하게 된다.- InodeInode는 파일의 이름을 제외한 해당 파일의 모든 정보를 가지고 있어 하나의 파일을 나타내며, 빠진 파일 이름에 대한 inode 번호와 함께 디렉터리 엔트리 안에 저장된다. Inode Operation은 create, link, unlink, symlink, mk(rm)dir, mknod, rename, truncate 등 주로 파일의 생성과 삭제에 대한 것으로 Inode가 대표하는 것이 파일에 대한 정보임을 고려하면 당연하다고 할 것이다.또한 inode도 superblock과 같이 디스크에 존재하는 각 파일시스템별 inode와 그것을 통해 생성하는 VFS inode가 있으며 메모리에 올라온 후에는 변경사항이 발생하면 dirty flag를 이용하여 주기적으로 저장한다.- FileFile 오브젝트는 특정 프로세스(process)와 열린 파일 사이의 관계를 나타내는데, 위에서 다룬 superblock이나 inode와는 달리 디스크에 해당하는 정보를 저장하지 않는다. 그 이유는 file object가 다루는 프로세스와 열린 파일 자체가 메모리에서만 생성되는 정보이며 영원히 디스크에 기록되는 정보가 아니기 때문이다. File operation은 프로세스가 특정 파일을 통해 IO를 할 때 필요한 동작들로, open , read, aio_read, write, aio_write, llseek, readv, writev 등이다. 비 동기 IO 함수인 aio_* 와 vector IO 함수인 readv, writev는 kernel 2.6에서 file operation에 포함되었고 2.4 시절에는 user library에서 처리하였다.비동기 IO 함수는 블로킹 IO 함수와 반대로 개념으로 IO를 요청한 후 그 결과를 기다리지 않고(결과가 올 때까지 해당 프로세스가 블록되지 않고) 바로 다음 작업을 진행하다가 요청한 IO 종료 시 signal 등을 통해 결과를 통보 받는다. 이런 함수는 IO 시간을 낭비하지 않고 최대한 활용할 수 있는 장점이 있는데, 이에 대해서는 UNIX 프로그래밍 관련 서적에서 잘 다루고 있으므로 IO 시간의 효율적인 사용이 필요하다면 꼭 참고하기 바란다.DentryDentry 오브젝트는 개별 파일시스템이 디스크에 관리하는 directory entry를 읽어서 VFS가 변환하여 관리하는 형태로, 특정 경로의 디렉터리는 각 요소마다 하나의 dentry 오브젝트를 갖는다. 그림 2는 /etc/test/page라는 경로가 어떻게 denty로 변환되는 지를 보여주는데, 하나의 경로는 / (root), etc, test, page라는 4개의 denty 오브젝트로 변환되고, 서로 간에 parent 관계는 dentry object의 d_parent를 통해 표시된다.Dentry는 디스크에 존재하는 개별 파일시스템의 directory entry를 읽어서 생성하는 것이므로 생성 시 마다 디스크 read에 따른 부담이 있으므로, dentry cache를 두어 성능 향상을 실현하고 있다.실제로 만들어 보기 ①본 장에서는 앞에서 언급한 대로 “Linux Filesystems in 21days 45 minutes”를 기본으로 진행하게 되는데 계획한 제목과는 달리 양이 꽤 많으므로 연재를 두 번에 나눠 진행하며, 커널 모듈 컴파일에 대한 지식이 있어야만 실제 실행하면서 따라 해볼 수 있다.연재를 진행하는 자료는 아래 링크에서 참고할 수 있다(Open office 파일).http://svn.samba.org/samba/ftp/cifs-cvs/ols2006-fs-tutorial-smf.odp또한 작성하는 samplefs 코드는 아래 링크에서 다운로드 가능하다.http://svn.samba.org/samba/ftp/cifs-cvs/samplefs. tar.gz환경 설정위에서 코드를 다운로드하면 우선 아래와 같이 압축을 풀어야 한다.압축 푼 내용을 확인해 보면 samplefs라는 디렉터리가 생성되고 그 내용은 다음과 같을 것이다.위에서 day1~day11까지는 단계별로 파일시스템 기능을 완성해 가는 것이고 Kconfig.diff는 커널 옵션에 samplefs를 넣기 위한 patch 파일이다. 우선 파일시스템을 작업할 커널을 Kconfig.diff와 같은 디렉터리에 준비한다. 연재에서는 linux 2.6.11을 사용한다.Linux 디렉터리에 Kconfig를 samplefs의 Kconfig.diff를 이용하여 patch 한다.이 때 파일명이 맞지 않다고 문제가 발생할 수 있는데 이 경우 아래와 같이 붉은 색처럼 입력하여 대처할 수 있다.위 작업의 결과가 어떻게 되었는지 확인해 보기 위해 Kernel config를 수행해 보면Menuconfig 화면에서 “filesystems ->” 를 선택하면 아래 그림과 같이 “Sample filesystem (EXPERIMENTAL) (NEW)”가 추가되었음을 알 수 있다.아직 코드를 한 줄도 작성하지 않았는데, Menuconfig에 메뉴가 나타나니 벌써 뿌듯하다!다음은 samplefs를 위한 디렉터리 생성 및 compile 환경 설정으로 커널에 fs/에 samplefs라는 디렉터리를 만들고 Makefile을 구성하면 된다.이렇게까지 하면 make할 준비가 완료되었는데, day1은 superblock만 정의하므로 Makefile도 별 내용이 없다.또한 fs/Makefile도 수정이 필요한데, 아래와 같이 samplefs에 대한 정보를 추가해 준다.위에서 띄어 쓸 때 반드시 tap key를 사용해야 한다는 점을 주의해야 한다.그럼 빌드하기 위해 day1의 super.c도 fs/samplefs로 복사한다.빌드를 하기 위해, 우선 menuconfig를 다시 실행시켜서 아래와 같이 설정되었는지 확인한다.확인했으면 바로 빌드를 한다.위에서 볼 수 있듯이 우리의 samplefs가 빌드되었고, 우리는 독자적인 파일시스템을 가지게 되었다.만약 위에서 커널에 static하게 링크되는 것보다 모듈을 선호하는 경우에는 menuconfig에서 다음과 같이 <*> 대신 이 되도록 하면 된다.그런 후 아래와 같이 빌드를 진행한다.모듈을 선택한 경우에는 위와 같이 빌드 시에 [M]이라는 표시해 해주며, 빌드 결과로 커널 모듈 파일인 samplefs.ko가 생성되었다. samplefs.ko은 커널에 static하게 링크되지 않고 사용자가 필요 시에 insmod를 이용하여 커널에 연결해 주어야 하고, 사용이 끝나면 rmmod를 이용하여 커널과의 연결 및 메모리 리소스 사용을 제거해야 한다. 이로써 환경 설정을 모두 끝났고, 다음 회에서는 실제 코드를 바탕으로 개별 파일시스템이 어떻게 구현되는지를 살펴보도록 하겠다.마치며이번 달은 여러 가지 일들로 인해 연재 원고 준비가 정말 힘들었다. 월초에는 긴 추석 연휴가 있어서 너무 바빴고, 추석이 끝나자 영원히 옆에서 계실 것 같은 할아버님께서 끝내 돌아가고 말았다. 그밖에도 필자가 진행중인 프로젝트가 현재 개발 완료되어 Software Quality Assurance(SQA)팀에 넘겨졌는데, 개발자들은 다들 알겠지만 SQA가 얼마나 피 말리는 작업인가! 그렇게 정신없는 와중에 연재 원고의 마감날짜는 개인 사정과 관계없이 어느새 다가왔다. 연재물의 첫 원고를 전달한 날이 엊그제 같은데 벌써 두 번째 원고를 마감하니 정말 세월이 빠름을 실감할 수 있는 요즈음이다.이번 회에서는 Linux VFS에 대한 전반적인 내용과 스스로 파일시스템을 만들어 보기 위한 기본 준비에 대해 다루었다. 다음 회부터는 실제 코드를 보면서 작업을 계속 할 예정이니 커널 모듈과 커널 디버깅에 대해 잘 모르시는 독자는 틈틈이 공부를 해 놓으시면 다음 회 연재 글의 이해에 많은 도움이 될 것이다(아마도 현재 연재 진도를 봐서는 다음 달 연재 내용을 약간 조정해야 될 것 같다).독자들 중 연재 내용에 대해 더 궁금한 점이 있거나, 잘못되어 수정이 필요한 부분이 있다면 언제든지 mdpe36@empal. com으로 연락 바란다.쪹 연재순서임베디드 리눅스 개발자를 위한 파일시스템 분석1회 임베디드 시스템용 파일시스템 개괄2회 리눅스 VFS 분석과 파일시스템의 작성3회 IO 서브시스템의 기초 : Memory, Process4회 IO 서브시스템 분석 : 블록 디바이스장민성(mdpe36@empal.com)1999년 대학원 졸업 직전부터 회사 생활을 시작하여, 8bit 펌웨어부터 RTOS, 윈도우용 드라이버까지 다양한 소프트웨어를 개발했다. 삼성전자 소프트웨어연구소에서 2003년부터 임베디드 리눅스 관련 업무를 수행하고 있으며, 주로 저전력기술 및 파일시스템 관련 부분에 경험을 쌓았다. 지난해 CELF Technical Conference, Linux World Conference 2006 Korea 등에서 관련 발표를 진행했으며, 올해는 Ottawa Linux Symposium 2006(OLS06)에 관련 Technical Paper 제출을 통해 참가했다.
이 기사를 공유합니다
저작권자 © 테크월드뉴스 무단전재 및 재배포 금지