유저 공간의 애플리케이션 코드에서 시스템 호출을 통해
커널을 통해 어떻게 디바이스 파일에 접근하여 장치를 사용하는지 함수 호출을 따라가보았다.

사물인터넷을 위한 리눅스 프로그래밍 with 라즈베리파이(서영진)
1. 애플리케이션 호출
fd = open("/dev/sk_dev", O_RDWR);2. 시스템 호출

AT_FDCWD : 현재 작업 디렉토리에서 상대경로로 파일을 연다.
커널이 제공하는 매크로 (→ syscall 테이블에 매핑되어 호출.)


do_sys_open→do_sys_openat2→do_filp_open


do_filp_open→path_openat→do_open→vfsopen

nd : 경로 파싱 과정의 상태 저장소
#define EMBEDDED_LEVELS 2 struct nameidata { struct path path; struct qstr last; struct path root; struct inode *inode; /* path.dentry.d_inode */ unsigned int flags, state; unsigned seq, next_seq, m_seq, r_seq; int last_type; unsigned depth; int total_link_count; struct saved { struct path link; struct delayed_call done; const char *name; unsigned seq; } *stack, internal[EMBEDDED_LEVELS]; struct filename *name; struct nameidata *saved; unsigned root_seq; int dfd; vfsuid_t dir_vfsuid; umode_t dir_mode; } __randomize_layout;3. ** 가상 파일 시스템 do_open->vfs_open**


open.c/vfs_open

4. inode로 파일 연결 do_dentry_open
- do_dentry_open
static int do_dentry_open(struct file *f,
int (*open)(struct inode *, struct file *))
{
static const struct file_operations empty_fops = {};
struct inode *inode = f->f_path.dentry->d_inode;
int error;
path_get(&f->f_path);
f->f_inode = inode;
f->f_mapping = inode->i_mapping;
f->f_wb_err = filemap_sample_wb_err(f->f_mapping);
f->f_sb_err = file_sample_sb_err(f);
if (unlikely(f->f_flags & O_PATH)) {
f->f_mode = FMODE_PATH | FMODE_OPENED;
f->f_op = &empty_fops;
return 0;
}
if ((f->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ) {
i_readcount_inc(inode);
} else if (f->f_mode & FMODE_WRITE && !special_file(inode->i_mode)) {
error = file_get_write_access(f);
if (unlikely(error))
goto cleanup_file;
f->f_mode |= FMODE_WRITER;
}
/* POSIX.1-2008/SUSv4 Section XSI 2.9.7 */
if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode))
f->f_mode |= FMODE_ATOMIC_POS;
f->f_op = fops_get(inode->i_fop);
if (WARN_ON(!f->f_op)) {
error = -ENODEV;
goto cleanup_all;
}
error = security_file_open(f);
if (error)
goto cleanup_all;
error = break_lease(file_inode(f), f->f_flags);
if (error)
goto cleanup_all;
/* normally all 3 are set; ->open() can clear them if needed */
f->f_mode |= FMODE_LSEEK | FMODE_PREAD | FMODE_PWRITE;
if (!open)
open = f->f_op->open;
if (open) {
error = open(inode, f);
if (error)
goto cleanup_all;
}
f->f_mode |= FMODE_OPENED;
if ((f->f_mode & FMODE_READ) &&
likely(f->f_op->read || f->f_op->read_iter))
f->f_mode |= FMODE_CAN_READ;
if ((f->f_mode & FMODE_WRITE) &&
likely(f->f_op->write || f->f_op->write_iter))
f->f_mode |= FMODE_CAN_WRITE;
if ((f->f_mode & FMODE_LSEEK) && !f->f_op->llseek)
f->f_mode &= ~FMODE_LSEEK;
if (f->f_mapping->a_ops && f->f_mapping->a_ops->direct_IO)
f->f_mode |= FMODE_CAN_ODIRECT;
f->f_flags &= ~(O_CREAT | O_EXCL | O_NOCTTY | O_TRUNC);
f->f_iocb_flags = iocb_flags(f);
file_ra_state_init(&f->f_ra, f->f_mapping->host->i_mapping);
if ((f->f_flags & O_DIRECT) && !(f->f_mode & FMODE_CAN_ODIRECT))
return -EINVAL;
/*
* XXX: Huge page cache doesn't support writing yet. Drop all page
* cache for this file before processing writes.
*/
if (f->f_mode & FMODE_WRITE) {
/*
* Depends on full fence from get_write_access() to synchronize
* against collapse_file() regarding i_writecount and nr_thps
* updates. Ensures subsequent insertion of THPs into the page
* cache will fail.
*/
if (filemap_nr_thps(inode->i_mapping)) {
struct address_space *mapping = inode->i_mapping;
filemap_invalidate_lock(inode->i_mapping);
/*
* unmap_mapping_range just need to be called once
* here, because the private pages is not need to be
* unmapped mapping (e.g. data segment of dynamic
* shared libraries here).
*/
unmap_mapping_range(mapping, 0, 0, 0);
truncate_inode_pages(mapping, 0);
filemap_invalidate_unlock(inode->i_mapping);
}
}
return 0;
cleanup_all:
if (WARN_ON_ONCE(error > 0))
error = -EINVAL;
fops_put(f->f_op);
put_file_access(f);
cleanup_file:
path_put(&f->f_path);
f->f_path.mnt = NULL;
f->f_path.dentry = NULL;
f->f_inode = NULL;
return error;
}해당 inode를 파일에 연결한다.(f→f_path 참조 수 증가, 페이지 캐시용 mapping설정 등)


드라이버가 등록한 file operation을 읽어옴.

/dev/my_dev/에서 open이 null이면 op→에 등록된 .open 함수, (드라이버 등록한 함수,my_open,user_open...) 실행

'Linux' 카테고리의 다른 글
| 메모리 맵 I/O - mmap()으로 매핑하여 GPIO 직접 제어해보기 (2) | 2025.08.17 |
|---|---|
| 문자 디바이스 파일 (주 번호와 부번호 할당, 번호 등록과 사용) (2) | 2025.08.17 |
| 리눅스 디바이스 드라이버 - module_init() 모듈 등록부터 사용자 호출까지 (1) | 2025.08.17 |
| syscall번호를 테이블에 넣어 커널에 빌트인하여 사용 (2) | 2025.08.17 |
| U-Boot 부팅 흐름 과정 요약 2 -Linux kernel 로드 (3) | 2025.08.16 |