reference: https://pages.cs.wisc.edu/~remzi/OSTEP/, 시스템 프로그래밍, 운영체제 수업(최종무 교수님)
"리눅스 커널 내부구조" / 백승재, 최종무
태스크가 ext2 파일시스템을 마운트한다고 가정해보면,
사용자의 마운트 요청을 받은 VFS는 ext2의 마운트 함수를 호출하면서 인자로 빈 수퍼 블록 객체를 넘긴다. 그러면 ext2는 자체적으로 구현한 파일시스템 내부 함수를 이용하여, 파티션 앞부분에 기록해두었던 수퍼 블록 구조체를 읽은 뒤 이를 바탕으로 VFS가 넘긴 수퍼 블록 객체의 내용을 채워서 리턴한다.
한편 사용자는 파일 이름을 인자로 sys_open() 시스템 콜을 요청할 수 있으며, 이 경우 VFS는 빈 아이노드 객체를 인자로 하여 ext2 내부의 open 함수를 호출한다. 그러면 ext2는 자체적으로 구현한 파일 시스템 내부 함수를 이용해, 필요한 정보를 아이노드 객체에 채워서 리턴한다. VFS는 이 아이노드 객체를 디엔트리 객체에 연결시켜 사용자의 태스크 구조(task_struct 구조체)와 연결시킨다.
task_struct에는 files라는 이름의 변수가 있으며, 이 변수는 files_struct라는 자료구조를 가리킨다. files_struct에는 fd_array라는 이름의 변수가 있는데 유닉스 계열 운영체제에서는 일반적으로 이것을 파일 디스크립터(file descriptor, fd)라고 부른다. sys_open()을 호출하면 리턴 값으로 정수형 fd가 반환된다. 이 fd가 fd_array 배열의 인덱스로 사용된다.
태스크 구조 - 파일 객체 - 디엔트리 객체 - 아이노드 객체 연결 구조
fd_array는 file이라는 자료구조에 대한 포인터를 갖는 배열이다. 일반적으로 이 배열의 첫 번째 항, 즉 fd_array[0]은 표준 입력(stdin)으로 설정되어 있고(어떤 형태의 파일이든 입력 표준으로 지정됨, 보통은 터미널(리눅스에선 파일로 표현)), fd_array[1]은 표준 입력(stdout)으로, 그리고 fd_array[2]는 표준 에러 출력(stderr)으로 설정되어 있다. 그리고 그 다음 인덱스부터는 태스크가 새로운 파일을 오픈할 때마다 할당되어 사용된다.
결국 fd_array의 각 항들은 파일 객체를 가리킨다. 유닉스 계열 운영체제에서는 이 자료구조를 흔히 '파일 테이블'이라 부른다. 즉 사용자 태스크에게 fd라는 정수로써 접근할 수 있도록 추상화 계층을 제공하는 역할을 한다.
파일 객체에는 여러 변수가 있으며 그 중 중요한 것은 f_dentry, f_pos, f_op이다.
- f_dentry: 디엔트리 객체를 가리킴. 디엔트리 객체는 다시 아이노드 객체를 가리킴.
- f_pos: 현재 파일에서 읽거나 쓸 위치를 나타냄. 파일이 처음 오픈되면 f_pos는 0으로 설정되며, 읽기 또는 쓰기 연산이 진행됨에 따라 읽혀진 또는 쓰여진 크기만큼 이동한다. offset을 저장하는 필드라 볼 수 있다. (lseek 시스템 콜을 통해 f_pos를 변경시킬 수 있다.)
- f_op: file_operations라는 자료구조를 가리키는 포인터이다.(디바이스 드라이버 part와 관련) 가상적인 파일 연산이 요청되면 커널은 현재 요청된 파일이 어떤 파일 유형인가를 보고, 각 파일에 적합한 파일 고유(file specific) 함수를 사용하는데, 이때 사용되는 구조가 file_operations이다. 즉 각 파일
유형에 적합한 파일 연산들이 이 변수에 등록되는 것.
아이노드 객체는 파일 당 하나씩 주어지며, 이 객체에는 해당 파일과 관련된 많은 정보가 들어있다.
- i_dev: inode가 실제 존재하고 있는 디스크 파티션을 나타냄.
- i_rdev: 파일이 장치 파일인 경우 관련되어 있는 디바이스 드라이버의 주번호를 나타냄.
- i_ino: inode의 고유한 번호를 나타냄.
- i_mode: inode가 관리하는 파일의 속성 및 접근 제어 정보를 유지.
- i_nlink: inode를 가리키고 있는 파일 수를 의미.
- i_size: 아이노드 객체에 해당되는 파일의 크기를 의미.
- i_op: inode_operations라는 자료구조를 가리키는 포인터. 사용자가 create(), mkdir() 등 파일시스템 메타데이터와 관련된 연산을 요청하면 커널은 요청한 연산이 어떤 파일시스템에서 발생했는지 파악, 적절한 파일시스템 고유(filesystem specific)한 함수를 호출하는데 이때 사용하는 구조가 inode_operations이다.
file_operations와 inode_operations는 함수 포인터들을 필드로 갖는다. 이 두 자료구조를 통해 리눅스는 다양한 파일과 파일시스템을 지원할 수 있다.
최종무 교수님 강의는 해당 학교 학생 분이라 들으신건가요?
어쩌다보니 최종무 교수님의 강의를 들을 일이 있었는데, 확실히 잘 가르쳐주셨습니다.