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

사물인터넷을 위한 리눅스 프로그래밍 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...) 실행

+ Recent posts