struct file 객체는 통일된 상위 핸들이지만, struct inode 객체는 파일을 위한 메타데이터 객체이다.struct file은 소켓이든 파일이든 공용, struct inode는 파일만을 위한 객체. 소켓이나 그외는 다른 객체를 사용)struct socket 이라는 객체가 생성된다.open(”a.txt”)라는 시스템 콜을 호출했다고 가정하고 이해해보자.struct inode {
umode_t i_mode; // 파일 타입 + 권한 bits (예: -rw-r--r--)
kuid_t i_uid; // 소유자
kgid_t i_gid; // 그룹
loff_t i_size; // 파일 크기
struct timespec64 i_atime, i_mtime, i_ctime; // 시간들
// 파일시스템이 관리하는 데이터 위치(디스크 블록/extent 등)로 이어지는 포인터들
void *i_private;
// 실제 파일 데이터 I/O를 수행할 때 사용할 함수 테이블(파일시스템 구현)
const struct inode_operations *i_op;
const struct file_operations *i_fop;
};파일, 소켓 등 같이 사용하는 공통 핸들러인데, 파일을 위한 커서 위치나 inode등은 왜 존재하는가?
- 리눅스는 역사적으로 ‘파일’이 기준 모델이라서 파일을 위한 필드가 존재하고, 다른 자원들은 필요없으면 사용하지 않는다.
struct file {
// 열린 파일의 현재 읽기/쓰기 위치(커서)
loff_t f_pos;
// open 플래그(O_RDONLY/O_WRONLY/O_NONBLOCK/O_APPEND 등)
unsigned int f_flags;
// 이 열린 상태가 어떤 inode(=어떤 파일)를 가리키는지
struct inode *f_inode;
// 이 열린 상태로 read/write/poll/ioctl 등을 할 때 호출될 함수 테이블
const struct file_operations *f_op;
// 구현체(소켓/파이프/디바이스)가 "자기 객체"를 걸어두는 자리
// 소켓이면 여기서 socket 객체로 이어짐
void *private_data;
};
open_fds 비트가 0인 자리를 찾아서struct files_struct {
atomic_t count;
bool resize_in_progress;
wait_queue_head_t resize_wait;
struct fdtable __rcu *fdt;
struct fdtable fdtab;
spinlock_t file_lock ____cacheline_aligned_in_smp;
unsigned int next_fd; // 다음 빈 자리 찾을 위치
unsigned long close_on_exec_init[1];
unsigned long open_fds_init[1]; // 현재 열려있는 fd인지
unsigned long full_fds_bits_init[1];
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};
// 의사코드 (이렇게 fd가 생성될 것이다.)
int get_unused_fd(struct files_struct *files)
{
spin_lock(&files->file_lock);
struct fdtable *fdt = files_fdtable(files);
int fd = find_next_zero_bit(fdt->open_fds, fdt->max_fds, files->next_fd);
if (fd >= fdt->max_fds) {
// fdtable 키워야 함 (max_fds 확장)
// resize_in_progress / resize_wait 같은 필드가 이때 관여
fd = expand_files(files, fd); // 개념
}
__set_bit(fd, fdt->open_fds);
if (close_on_exec_needed)
__set_bit(fd, fdt->close_on_exec);
files->next_fd = fd + 1;
spin_unlock(&files->file_lock);
return fd;
}
fdt → fd[fd] = filefd를 통해 struct file 객체를 찾는 것이다.fd만 넘긴다.fd table에서 fd에 대응하는 열린 객체를 찾기 위한 여정을 떠난다.files_struct 내부의 fdtable에 접근fdtable에서 fd[fd]를 통해 struct file* 을 획득struct fdtable {
unsigned int max_fds;
struct file __rcu **fd; // fd 배열: fd 번호 -> struct file*
unsigned long *close_on_exec;
unsigned long *open_fds;
...
};
struct files_struct {
...
struct fdtable __rcu *fdt; // 현재 사용중인 fdtable
struct fdtable fdtab; // 기본 내장 fdtable
...
struct file __rcu * fd_array[NR_OPEN_DEFAULT];
};struct file)을 중심으로 진행된다.read를 하든 write를 하든 열린 객체의 상태를 중심으로 진행하고, 참조하고 있는 inode와(또는 sock객체 등)과 함께 작업을 완료한다.