나는 시스템 호출, VFS, 장치 드라이버 처리, 궁극적으로 최종 장치가 무언가를 수행하도록 하는 것 사이의 상호 작용에 대해 가능한 한 많이 배우려고 노력하고 있습니다. 나는 매우 간단한 예(파일 생성)를 살펴보고 기본 프로세스를 최대한 자세히 이해하려고 노력할 것이라고 생각했습니다.
일부 C 코드를 생성하고 쓰기 위해 (존재하지 않는) 파일을 열고 (최적화 없이) 컴파일한 다음 실행하는 동안 strace를 사용하여 확인했습니다. 특히 openat
시스템 호출에 초점을 맞추고 이 호출이 궁극적으로 파일 객체/파일 설명을 생성할 뿐만 아니라 실제로 디스크(FYI, EXT4 파일 시스템, SATA HDD)에 쓸 수 있는 이유와 방법에 대해 설명하고 싶습니다.
일반적으로 말해서, 일부 검사 및 지원을 제외하고 이 프로세스에 대한 나의 이해는 다음과 같습니다(내 이해가 편향된 경우 정정해 주십시오!).
- 메모리에 매핑된 ELF
- libc는 메모리에 매핑됩니다.
fopen
~라고 불리는- libc 오픈
openat
O_CREAT
플래그를 포함하는 시스템 호출이 호출됩니다.- 시스템 호출 번호는 RAX 레지스터에 저장됩니다.
- 시스템 호출 매개변수(예: 파일 경로 등)는 RDI 레지스터(및 적절한 경우 RSI, RDX 등)에 저장됩니다.
- Syscall 명령이 발행되고 CPU가 링 0으로 전환됩니다.
- MSR_LSTAR 레지스터가 가리키는 System_call 코드를 호출합니다.
- 커널 스택에 푸시된 레지스터
- 오프셋에서 호출된 RAX의 함수 포인터
sys_call_table
asmlinkage
openat
실제 시스템 호출 코드를 호출하는 래퍼
당시에는 충분히 이해하지 못했지만 결국에는 다음과 같은 사실을 알게 되었습니다.
- open 호출은 프로세스에 고유하고 커널의 파일 설명자 테이블에서 전역적으로 유지되는 파일 설명자를 반환합니다.
- FD는 파일 설명 파일 객체에 매핑됩니다.
- 파일 객체는 inode 구조, inode_Operations, file_Operations 등의 구조로 채워집니다.
- 파일 작업 테이블은 해당 호출을 처리하기 위해 일반적인 시스템 호출을 적절한 장치 드라이버에 매핑해야 합니다
write
. SCSI 드라이버) - 이 매핑은 파일/장치의 주/부 번호를 기반으로 합니다.
- 이 프로세스 동안 명령이 하드 드라이브의 장치 드라이버로 전송되도록 하는 코드가 호출됩니다. 이 명령은 디스크 컨트롤러로 전송되어 파일이 하드 드라이브에 기록되도록 합니다. 또는 DMA 또는 기타 I 정의되지 않은 I/O 방법
- 결국 디스크 컨트롤러는 완료를 알리는 메시지를 커널에 다시 보내고 커널은 공간을 사용하기 위한 제어권을 반환합니다.
저는 커널 소스 추적을 잘 못해서 몇 가지 시도해 보았지만 부족한 부분이 많은 것 같습니다. 내 질문은 다음과 같습니다.
FD를 반환하고 파괴하는 일부 함수를 커널 소스에서 찾았지만 코드가 실제로 파일의 파일 객체/파일 설명을 채우는 위치를 찾을 수 없었습니다.
A) open
또는 openat
시스템 호출에서 새 파일이 생성될 때 파일 구조는 어떻게 채워지나요? 데이터는 어디에서 오는가? 구체적으로 이 파일의 file_Operations, inode_Operations 등은 어떻게 채워지나요? 예를 들어, 이 구조를 채울 때 커널은 이 특정 파일에 대한 파일 작업이 SCSI 드라이버의 작업과 같아야 한다는 것을 어떻게 알 수 있습니까?
B) 프로세스(특히 참조 소스)에서 장치 드라이버로의 전환이 발생하는 위치는 어디입니까? 예를 들어, ioctl
유사한 함수가 호출되면 해당 장치에서 호출한 명령어에 대한 일부 참조와 데이터의 일부 메모리 주소가 전달될 것으로 예상하지만 어디서 이런 일이 발생하는지 찾을 수 없습니다.
커널 소스 코드를 보면 실제로 찾을 수 있는 것은 새 FD를 할당하는 코드뿐이지만 파일 구조를 채우는 코드도 없고 장치 드라이버에 제어권을 전송하기 위해 적절한 파일 작업을 호출하는 코드도 없습니다.
죄송합니다. 설명이 너무 길지만 최대한 많이 배우고 싶고, C에 대한 기본적인 이해는 있지만 다른 사람의 코드를 이해하는 데 정말 어려움을 겪고 있습니다.
내가 은유적인 벽돌 벽에 부딪히는 것처럼 보이기 때문에 나보다 더 지식이 풍부한 누군가가 이러한 사항을 명확히 하는 데 도움을 줄 수 있기를 바랍니다. 어떤 조언이라도 대단히 감사하겠습니다.
편집하다: 다음 사항을 통해 내가 추구하는 기술적인 세부 사항을 명확히 할 수 있기를 바랍니다.
open
또는 파일 경로와 플래그를 사용하는 시스템openat
호출(후자는 디렉토리를 가리키는 FD도 전달함)- 이
O_CREAT
플래그도 전달되면 파일이 존재하지 않으면 "생성"됩니다. - 파일 경로를 기반으로 커널은 파일의 장치 유형을 식별할 수 있습니다.
- 장치 유형은 일반적으로 주 번호/부 번호로 식별됩니다. 기존 파일의 경우 이러한 장치 유형은 파일 시스템의 파일 inode 구조(구성원
i_rdev
) 및 파일 상태 구조(st_dev
장치 유형의 구성원) 에 저장됩니다. 파일이 있고st_rdev
파일 자체의 장치 유형 도 있습니다 .
실제로 내 질문은 다음과 같습니다.
개방형 시스템 호출 중 하나를 사용하여 파일이 생성되면 해당 inode 및 상태 구조도 생성되고 채워져야 합니다. 개방형 시스템 호출이 이를 수행하는 방법(이 시점에서 진행해야 하는 것은 파일 경로와 플래그뿐입니다) 상위 디렉토리의 inode 또는 stat 구조를 보고 거기에서 관련 구조 멤버를 복사합니까?
어떤 지점(즉, 소스 코드의 어느 곳)에서 이런 일이 발생합니까?
내가 이해한 바로는 이러한 개방형 시스템 호출이 호출될 때 VFS가 호출할 장치 드라이버 코드를 알 수 있도록 장치 유형을 알아야 합니다. 새 파일이 생성되고 파일 개체 구조에 장치 유형이 설정되지 않은 경우 어떻게 됩니까? 코드 이름이 무엇인가요?
순서는 다음과 같습니다.
사용자 프로세스는 새 파일 열기를 시도합니다. -> open('/tmp/foo', O_CREAT)
엽니다. -> "/tmp" 구조를 찾고 해당 장치 유형을 가져옵니다. -> 사용되지 않은 FD를 가져옵니다. -> 장치 유형을 상위 항목으로 설정하는 것을 포함하여 inode/stat 구조를 채웁니다. 장치 유형 -> 장치 유형에 따라 장치 드라이버 코드에 파일 작업/inode 작업 매핑 -> open
시스템 호출을 위해 장치 드라이버 코드 호출 -> 새 파일을 디스크에 쓰도록 디스크 컨트롤러에 적절한 지침 보내기 -> 대조, 대기 확인 -> FD를 사용자 호출 절차로 되돌리시겠습니까?
답변1
기본적으로 당신 말이 맞지만, 관련 없는 세부 사항이 섞여 있습니다(주로 C 작동 방식으로 인해).
프로그램 호출 open(2)
(시스템 호출, 즉 커널 호출) 이것이 수행되는 정확한 방법은 아키텍처 및 기타 세부 사항에 따라 다릅니다.
시스템 호출의 커널 내부 부분은 매개변수(여기서는 요청된 경로) 및 프로세스 자격 증명과 같은 기타 정보와 함께 호출됩니다(자세한 내용은 아키텍처, 정확한 OS 버전에 따라 다름...).
주어진 경로에서 커널은 주어진 경로에 어떤 파일 시스템이 마운트되어 있는지 알고 있습니다. 루트에서 아래쪽으로 일반적인 순회를 수행하여 호출자가 필요한 권한을 가지고 있는지, 디렉터리가 존재하는지 등을 확인할 수 있습니다. 여기에는 디스크에서 데이터를 읽는 것이 포함될 수 있습니다. 모든 것이 순조롭게 진행되면 파일 시스템 데이터 구조에 대한 관련 변경을 시작하여 새로 열린 파일을 기록하고, 공간을 예약하고, 디렉터리에 등록하는 등의 작업을 수행합니다.
파일 시스템 데이터 구조(현재 메모리에 있음)에 대한 변경 사항을 디스크에 기록해야 할 수도 있습니다(또는 네트워크를 통해 전송하거나...). 커널은 완료를 위해 쓰기 작업을 대기열에 추가하고(즉시 완료할 필요가 없는 경우) 이를 사용자에게 반환합니다.