이 문서에서는 주로 가상 파일 시스템에 대해 설명합니다. Linux 파일 시스템의 아키텍처에는 특정 파일 시스템(예: Ext2, Ext3, XFS 등)과 애플리케이션, 즉 VFS(가상 파일 시스템) 간의 추상화 계층이 포함되어 있습니다. VFS를 사용하면 응용 프로그램이 기본 파일 시스템의 세부 사항을 알지 못해도 다양한 유형의 파일 시스템과 통신할 수 있습니다. VFS를 사용하면 파일 시스템 구현을 응용 프로그램에서 분리하고 분리할 수 있으므로 시스템 유연성과 유지 관리성이 향상됩니다. VFS는 또한 Linux 커널이 여러 파일 시스템 유형을 지원할 수 있도록 하며 응용 프로그램이 파일 시스템에 액세스할 수 있는 통합 인터페이스를 제공합니다. VFS 프레임워크에서는 표준 파일 시스템 작업 인터페이스를 구현하여 다양한 파일 시스템이 커널과 통신할 수 있습니다
사진
위 그림은 이 아키텍처의 중심이 가상 파일 시스템 VFS임을 보여줍니다. VFS는 파일 시스템 프레임워크를 제공하며, VFS를 기반으로 로컬 파일 시스템을 구현할 수 있습니다. 주로 다음 작업을 완료합니다:
1) 추상화 계층인 VFS는 애플리케이션 계층을 위한 통합 인터페이스(읽기, 쓰기, chmod 등)를 제공합니다.
2) inode 캐시, 페이지 캐시와 같은 일부 공용 기능은 VFS에서 구현됩니다.
3) 특정 파일 시스템이 구현해야 하는 인터페이스를 표준화합니다.
위 설정을 기반으로 다른 특정 파일 시스템은 VFS의 규칙을 따르고 해당 인터페이스와 내부 논리를 구현한 후 시스템에 등록하기만 하면 됩니다. 사용자는 파일 시스템을 포맷하고 마운트한 후 하드 디스크 리소스를 사용하여 파일 시스템 기반 작업을 수행할 수 있습니다.
Linux 운영 체제에서는 디스크를 포맷한 후 mount 명령을 사용하여 시스템 디렉터리 트리의 디렉터리에 탑재해야 합니다. 이 디렉터리를 탑재 지점이라고 합니다. 마운트가 완료되면 이 파일 시스템을 기반으로 포맷된 하드 디스크 공간을 사용할 수 있습니다. Linux 운영 체제에서 마운트 지점은 거의 모든 디렉터리일 수 있지만 표준화를 위해 마운트 지점은 일반적으로 mnt 디렉터리 아래의 하위 디렉터리입니다.
다음은 비교적 복잡한 디렉터리 구조를 보여줍니다. 이 디렉토리 구조에서 루트 디렉토리는 하드 디스크 sda에 위치하고 mnt 디렉토리 아래에는 ext4, xfs 및 nfs라는 세 개의 하위 디렉토리가 있으며 각각 Ext4 파일 시스템(sdb 하드 디스크에 구축됨)을 마운트하고 XFS 파일 시스템(sdc 하드 드라이브에 구축) 및 NFS 파일 시스템(네트워크를 통해 마운트).
사진
디렉토리 트리에 있는 여러 파일 시스템 간의 관계는 커널의 일부 데이터 구조로 표현됩니다. 파일 시스템을 마운트하면 파일 시스템 간의 관계가 설정되고 특정 파일 시스템의 API가 등록됩니다. 사용자 모드가 API를 호출하여 파일을 열면 해당 파일 시스템 API를 찾아 이를 파일 관련 구조(예: 파일 및 inode 등)에 연결합니다.
위 설명은 비교적 도식적이므로 여전히 약간 혼란스러울 수 있습니다. 하지만 걱정하지 마세요. 다음에는 코드를 기반으로 VFS와 여러 파일 시스템을 지원하는 방법을 자세히 소개하겠습니다.
Linux의 VFS는 처음부터 존재하지 않았습니다. 출시된 Linux의 초기 버전에는 VFS가 없었습니다. 더욱이 VFS는 Linux에서 발명되지 않았습니다. Sun은 1985년 SunOS2.0에서 처음 개발했습니다. VFS 개발의 주요 목적은 로컬 파일 시스템과 NFS 파일 시스템을 조정하는 것입니다.
VFS는 일련의 공개 API 및 데이터 구조를 통해 특정 파일 시스템의 추상화를 구현합니다. 사용자가 운영체제에서 제공하는 파일 시스템 API를 호출하면 소프트 인터럽트를 통해 커널 VFS에 구현된 기능이 호출된다. 다음 표는 일부 파일 API와 커널 VFS 함수 간의 대응 관계를 보여줍니다.
사용자 모드 API |
커널 기능 |
지침 |
오픈 |
do_sys_open |
파일 열기 |
닫기 |
ksys_close |
파일 닫기 |
읽어 |
ksys_read/vfs_read |
데이터 읽기 |
쓰기 |
ksys_write/vfs_write |
데이터 쓰기 |
마운트 |
do_mount |
파일 시스템 마운트 |
위 표에서 볼 수 있듯이 각 사용자 모드 API에는 해당 커널 모드 기능이 있습니다. 애플리케이션이 파일 시스템 API를 호출하면 커널 상태의 해당 기능이 트리거됩니다. 여기에 나열된 것은 파일 시스템 API의 비교적 작은 하위 집합일 뿐이며 목적은 API와 VFS 간의 관계를 설명하는 것입니다. 다른 API에 대해 알고 싶다면 커널 소스 코드를 직접 읽어보시기 바랍니다. 이 글에서는 자세히 다루지 않겠습니다.
모든 사람에게 VFS와 특정 파일 시스템 간의 관계에 대한 지각적인 이해를 제공하기 위해 이 섹션에서는 Ext2 쓰기 API를 예로 들어 API에서 VFS 함수, 그리고 Ext2 파일 시스템 함수로의 호출 관계를 보여줍니다. 아래 그림과 같이 API 함수 write는 소프트 인터럽트를 통해 커널의 ksys_write 함수를 트리거합니다. 이 함수는 여러 프로세스를 거쳐 결국 함수 포인터(file->f_op)를 통해 Ext2 파일 시스템의 ext2_file_write_iter 함수를 호출하게 됩니다. ->wirte_iter).
사진
위 그림에서 커널 프로세스의 입구는 ksys_write 함수입니다. 구현 코드에서 볼 수 있듯이 여기서 주요 목적은 fd를 얻은 다음 fd의 멤버 파일을 매개 변수로 vfs_write를 호출하는 것입니다. . 그 중 fd는 구조체로, 그 형식은 아래 그림과 같다. 파일 멤버는 비교적 핵심적인 데이터 구조이다. 위 그림에서 볼 수 있듯이 이 멤버의 내용을 통해 Ext2 파일 시스템의 기능이 전달됩니다.
사진
매우 간단한 것 같습니다. VFS는 특정 파일 시스템에 등록된 함수 포인터만 호출하면 됩니다. 그런데 여기서 해결되지 않은 문제가 있습니다. VFS의 함수 포인터는 언제 등록되었습니까?
Ext2의 함수 포인터는 파일이 열릴 때 초기화됩니다(자세한 내용은 "파일 시스템 기술의 내부자" 섹션 3.1.2.2 참조). 우리 모두 알고 있듯이 사용자 모드 프로그램은 파일을 열 때 파일 설명자를 반환하지만 커널에서 파일을 나타내는 구조 파일은 이에 해당합니다. 이 구조의 더 중요한 멤버에는 아래 그림과 같이 f_inode, f_ops 및 f_mapping이 포함됩니다.
사진
위 그림에서 f_inode는 해당 파일에 해당하는 inode 노드입니다. f_ops는 특정 파일 시스템(예: Ext2)에서 파일 작업을 위한 함수 포인터 모음입니다. 파일이 열릴 때 초기화됩니다. VFS가 특정 파일 시스템에 대한 액세스를 구현하는 것은 이 함수 포인터 모음을 통해서입니다.
위 내용은 VFS의 또 다른 개념인 inode와 관련이 있습니다. Linux에서 inode는 index node의 약어로, 파일 시스템의 특정 개체(예: 파일 또는 디렉터리)를 나타냅니다. VFS에는 특정 파일 시스템 inode를 추상화한 inode라는 데이터 구조가 있습니다. 예를 들어 Ext2 파일 시스템에서는 ext2_inode_info로 구체적으로 정의되지만 XFS에서는 xfs_inode 데이터 구조로 표시됩니다. 또한 특정 파일 시스템의 inode 데이터 구조는 본질적으로 VFS의 inode와 관련되어 있습니다.
아키텍처 다이어그램에서 페이지 캐시, inode 캐시, dentry 캐시 등을 포함하여 VFS에 여러 캐시 구현이 있음을 알 수 있습니다. inode 캐시와 dentry 캐시는 동일한 방식으로 구현되며 비교적 간단합니다. 따라서 이 글에서는 먼저 이 두 가지 캐시를 소개합니다.
사실 이 두 캐시는 해시 테이블을 통해 구현됩니다. 해시 테이블의 개념은 누구나 알고 있으므로 이 글에서는 자세히 설명하지 않겠습니다. inode 캐시를 예로 들어 보겠습니다. 다음 그림은 ihash_entries 매개변수를 통해 해당 크기가 동적임을 알 수 있습니다. (크기는 시스템 메모리와 관련이 있습니다. 시스템 메모리가 클수록 더 커집니다.) 읽다).
사진
파일에 접근할 때 inode와 dentry에 자주 접근하기 때문에 이를 캐싱하면 하드디스크에서 데이터를 읽을 때 발생하는 성능 손실을 피할 수 있습니다.
VFS 페이지 캐시(Cache)는 주로 파일 시스템의 성능을 향상시키는 데 사용됩니다. 캐싱 기술은 파일 시스템의 성능을 향상시키기 위해 파일 시스템의 데이터와 메타데이터 중 일부를 메모리에 저장하는 기술을 말한다. 메모리의 액세스 지연 시간은 기계식 하드 디스크 액세스 지연 시간의 10만분의 1이므로(아래 그림 참조, 레지스터를 기본 단위로 1s) 캐싱 기술을 사용하면 파일 성능을 크게 향상시킬 수 있습니다. 체계.
사진
캐시는 IO 최적화의 세 가지 측면, 즉 핫 데이터, 사전 읽기 및 IO 병합을 통해 파일 시스템의 성능을 향상시킵니다. 예를 들어 작성자가 문서를 편집할 때 현재 데이터 블록과 주변 데이터 블록은 핫 데이터입니다. 혹은 인기기사가 나오면 이 기사의 내용이 핫데이터가 됩니다. 기본 스토리지 장치는 대규모 블록 읽기 및 쓰기에 대해 더 나은 성능을 갖는 경향이 있습니다. 미리 읽기는 기본 장치에서 대규모 데이터 블록을 미리 읽고 캐시하여 애플리케이션 요청이 캐시를 통해 응답할 수 있음을 의미합니다. IO 병합은 쓰기 요청을 위한 것입니다. 쓰기 요청은 백엔드 장치에 즉시 유지되지 않지만 쓰기 전에 캐시되어 큰 IO 청크로 나뉩니다.
메모리 용량은 하드 디스크 용량에 비해 훨씬 작기 때문에 페이지 캐시가 모든 하드 디스크 데이터를 캐시할 수는 없습니다. 이러한 방식으로 파일 시스템 데이터의 하위 집합만 캐시에 저장할 수 있습니다. 사용자가 계속해서 데이터를 쓰면 캐시가 가득 찬 상황에 직면하게 됩니다. 이때 캐시 데이터를 디스크에 플러시한 후 새 데이터를 저장하는 방법에 대한 문제가 발생합니다.
캐시를 디스크에 플러시하고 새 데이터를 저장하는 과정을 캐시 교체라고 합니다. 캐시 교체에는 다양한 알고리즘이 있으며 각각은 서로 다른 문제를 해결하는 데 사용됩니다. 다음으로 몇 가지 일반적인 캐시 교체 알고리즘을 소개합니다.
LRU 알고리즘, LRU의 전체 이름은 Least Recent Used이며, 이는 가장 최근에 사용되지 않았다는 의미입니다. 이 알고리즘은 시간적 지역성(temporal locality)의 원리를 기반으로 합니다. 즉, 최근에 사용된 데이터라면 앞으로도 다시 사용될 확률이 높습니다. 따라서 알고리즘은 최근에 사용되지 않은 캐시를 해제합니다.
LRU 알고리즘은 일반적으로 연결 목록을 사용하여 구현됩니다. 방금 사용된 캐시는 테이블의 헤드에 삽입되고, 자주 사용되지 않은 데이터는 연결 목록의 끝으로 천천히 압축됩니다. LRU의 원리를 보다 명확하게 이해하기 위해 다음 그림을 통해 설명하겠습니다.
사진
이 예에서는 전체 히트를 예로 들어 보겠습니다. 그림의 첫 번째 줄에 표시된 것처럼 캐시에 6개의 데이터 블록이 있다고 가정합니다. 상자의 숫자는 데이터 블록의 번호를 나타냅니다. 첫 번째 액세스(읽기 또는 쓰기 가능)가 3번 데이터 블록이라고 가정합니다. 액세스되었으므로 연결 목록의 선두로 이동합니다.
두 번째 접근은 4번 데이터 블록입니다. 같은 원리로 이 데이터 블록도 연결 리스트의 선두로 이동합니다. 자세한 내용은 위 그림의 2번째 줄에 나와 있습니다.
유추하자면, 4라운드 액세스 후에 액세스된 데이터는 앞으로 이동하는 반면, 액세스되지 않은 데이터 블록(예: 1 및 2)은 연결된 목록의 뒤쪽으로 천천히 압착됩니다. 이는 이 두 데이터 블록이 나중에 액세스될 가능성이 상대적으로 적다는 것을 어느 정도 나타냅니다.
풀 히트가 발생한 경우 캐시 교체는 진행되지 않습니다. 실제 상황은 캐시가 부족한 경우가 많으며 새 데이터를 저장하려면 캐시에 있는 데이터를 해제해야 합니다(상황에 따라 디스크에 플러시해야 하는지 여부). 이때 LRU 알고리즘이 유용하게 쓰인다. 이 알고리즘은 아래 그림과 같이 tail 데이터 블록을 이용해 새로운 데이터를 저장한 후 연결 리스트의 선두에 넣는다. 이 데이터 블록에 더티 데이터가 있으면 디스크로 플러시해야 하며, 그렇지 않으면 직접 해제할 수 있습니다.
사진
LRU 알고리즘의 원리와 구현은 비교적 간단하지만 용도가 다양합니다. 그러나 LRU 알고리즘은 연속적인 대량의 데이터가 갑자기 기록되면 모든 캐시 블록이 교체되어 이전의 모든 캐시 사용 통계가 무효화된다는 단점이 있습니다. 이러한 현상을 캐시 오염이라고 합니다. 캐시 오염 문제를 해결하기 위해 개선된 LRU 알고리즘이 많이 있으며 그 중 가장 일반적인 것은 LRU-K, 2Q 및 LIRS입니다.
LFU 알고리즘, LFU의 전체 이름은 Least 빈번하게 사용됨입니다. 이는 최근에 사용 빈도가 가장 낮다는 것을 의미합니다. 이 알고리즘은 데이터 액세스 빈도에 따라 해제할 캐시 블록을 결정합니다. 접근 빈도가 가장 낮은 캐시 블록이 먼저 해제됩니다.
아래 그림은 LFU 알고리즘의 개략도입니다. 라인 1은 원래 상태이고, 상자 안의 숫자는 캐시 블록에 액세스한 횟수를 나타냅니다. 새로운 데이터 추가와 캐시 블록 제거는 테일부터 수행됩니다. 특정 데이터(점선 상자)에 4번 액세스했고 액세스 횟수가 12에서 16으로 변경되어 새로운 위치로 이동해야 한다고 가정합니다. 그림의 2번째 줄은 다음과 같습니다. .
사진
이 책에서는 LFU의 원리를 이해의 편의를 위해 연결리스트를 예로 들어 설명하고 있지만, 프로젝트 수행 중에는 절대로 연결리스트를 사용하여 구현하지는 않습니다. 데이터 블록에 대한 접근 횟수가 변경되면 새로운 위치를 찾아야 하기 때문에 연결리스트 검색 작업에는 많은 시간이 소요된다. 빠른 검색을 위해 일반적으로 검색 트리(Search Tree)가 사용됩니다.
LFU에도 단점이 있습니다. 오래 전에 특정 기간 동안 데이터 블록에 자주 액세스하고 이후에는 더 이상 액세스하지 않는 경우 데이터가 캐시에 남아 있습니다. 그러나 데이터에 더 이상 액세스할 수 없으므로 캐시의 유효 용량이 줄어듭니다. 즉, LFU 알고리즘은 최근 상황을 고려하지 않습니다.
이 글에서는 매우 기본적인 두 가지 대체 알고리즘인 LRU와 LFU를 주로 소개합니다. 위의 알고리즘 외에도 많은 대체 알고리즘이 있으며 대부분은 2Q, MQ, LRFU, TinyLFU 및 ARC 등과 같이 LRU 및 LFU 이론을 기반으로 합니다. 지면의 제약으로 인해 이 책에서는 관련 논문을 직접 읽어보실 수 있습니다.
데이터 미리 읽기에도 특정 알고리즘이 있습니다. 미리 읽기 알고리즘은 IO 패턴을 식별하여 디스크에서 캐시로 미리 데이터를 읽습니다. 이런 방식으로 애플리케이션이 데이터를 읽을 때 캐시에서 직접 데이터를 읽을 수 있으므로 데이터 읽기 성능이 크게 향상됩니다.
사전 읽기 알고리즘에서 가장 중요한 것은 트리거 조건, 즉 어떤 상황에서 사전 읽기 작업이 시작되는지입니다. 일반적으로 미리 읽기를 트리거하는 상황은 두 가지입니다. 하나는 여러 주소에서 연속적인 읽기 요청이 있어 미리 읽기 작업을 트리거하는 경우이고, 다른 하나는 애플리케이션이 미리 읽기 표시가 있는 캐시에 액세스하는 경우입니다. 여기서, 미리 읽기 마크의 캐시는 미리 읽기 작업이 완료되었을 때 캐시 페이지에 만들어지는 마크이며, 애플리케이션이 이 마크가 있는 캐시를 읽으면 다음 미리 읽기가 트리거되어 식별이 생략됩니다. IO 모드의.
사진
미리읽기의 논리를 좀 더 명확하게 설명하기 위해 전체 과정을 위의 그림을 통해 소개합니다. 파일 시스템이 IO 모드에 사전 읽기가 필요하다는 것을 인식하면 시간 1(첫 번째 줄)에 표시된 대로 콘텐츠의 추가 부분을 읽습니다(동기식 사전 읽기라고 함). 동시에 동기식 미리 읽기 데이터의 경우 파일 시스템은 블록 중 하나를 표시합니다. 이 표시의 목적은 캐시가 끝나기 전에 가능한 빨리 다음 미리 읽기를 트리거하는 것입니다.
두 번째 시점에서는 애플리케이션이 계속해서 데이터를 읽으면 표시된 캐시 블록을 읽었기 때문에 동시에 다음 사전 읽기가 트리거됩니다. 이때 한 단계씩 디스크에서 데이터를 읽어오게 되는데, 캐시가 늘어나는 것을 그림을 보면 알 수 있습니다.
다음 지점 3과 4에서는 애플리케이션이 캐시에서 직접 데이터를 읽을 수 있습니다. 표시된 캐시 블록을 읽지 않았으므로 다음 미리 읽기가 트리거되지 않습니다. 시점 5에서는 미리 읽기 표시가 있으므로 다시 미리 읽기 프로세스가 시작됩니다.
위 분석을 보면 미리 읽기 기능으로 인해 데이터를 미리 캐시에 읽어 들인다는 것을 알 수 있습니다. 애플리케이션은 디스크에 액세스하지 않고도 캐시에서 직접 데이터를 읽을 수 있으므로 전반적인 액세스 성능이 크게 향상됩니다.
위 내용은 Linux 파일 시스템(파일 시스템) 아키텍처에 대한 간략한 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!