proc_root_init()

 

proc 파일 시스템 준비

다음 그림은 proc 파일 시스템의 초기화 및 마운트에 대한 함수 처리 흐름이다.

 

proc_root_init()

fs/proc/root.c

void __init proc_root_init(void)
{
        int err;

        proc_init_inodecache();
        err = register_filesystem(&proc_fs_type);
        if (err)
                return;

        proc_self_init();
        proc_thread_self_init();
        proc_symlink("mounts", NULL, "self/mounts");

        proc_net_init();

#ifdef CONFIG_SYSVIPC
        proc_mkdir("sysvipc", NULL);
#endif
        proc_mkdir("fs", NULL);
        proc_mkdir("driver", NULL);
        proc_mkdir("fs/nfsd", NULL); /* somewhere for the nfsd filesystem to be mounted */
#if defined(CONFIG_SUN_OPENPROMFS) || defined(CONFIG_SUN_OPENPROMFS_MODULE)
        /* just give it a mountpoint */
        proc_mkdir("openprom", NULL);
#endif
        proc_tty_init();
        proc_mkdir("bus", NULL);
        proc_sys_init();
}

마운트하여 사용할 수 있도록 커널 내부에 proc 루트 파일 시스템을 생성하고 초기화한다.

  • 코드 라인 5에서 proc_inode 구조체를 자주 사용하므로 이의 kmem 캐시를 준비한다.
  • 코드 라인 6~7에서 “proc” 파일 시스템을 등록한다. 파일 시스템들은 name을 유니크 키로 사용하는 전역 file_systems(next로 연결된다)에 단방향으로 등록순으로 연결된다.
  • 코드 라인 9에서 inode용 정수 id를 발급받아 self_inum에 대입한다.
    • inode용 IDA는 전역 proc_inum_ida를 통해 관리된다.
  • 코드 라인 10에서 inode용 정수 id를 발급받아 thread_self_inum에 대입한다.
  • 코드 라인 11에서 “self/mounts”를 가리키도록 mounts라는 이름으로 스태틱 링크를 /proc에 생성한다.
    • 예) # ls /proc/mounts -la
      lrwxrwxrwx 1 root root 11 Apr 16 16:55 /proc/mounts -> self/mounts
  • 코드 라인 13에서 “self/net”를 가리키도록 net라는 이름으로 스태틱 링크를 /proc에 생성한다. 그런 후 네트워크 네임스페이스 서브시스템에 등록한다.
    • 예) # ls /proc/net -la
      lrwxrwxrwx 1 root root 8 Apr 16 16:56 /proc/net -> self/net
  • 코드 라인 15~17에서 /proc/sysvipc 디렉토리를 생성한다.
  • 코드 라인 18에서 /proc/fs 디렉토리를 생성한다.
  • 코드 라인 19에서 /proc/driver 디렉토리를 생성한다.
  • 코드 라인 20에서 /proc/nfsd 디렉토리를 생성한다.
  • 코드 라인 21~24에서 /proc/openprom 디렉토리를 생성한다.
  • 코드 라인 25에서 /proc/tty 디렉토리를 생성하고 그 하위 디렉토리에 다음을 추가로 생성한다.
    • /proc/tty/ldisc 및 /proc/tty/driver 디렉토리를 생성한다.
    • /proc/drivers 및 /proc/ldiscs 라는 이름으로 파일을 생성하고 proc 동작이 되도록 파일 오퍼레이션을 연결한다.
  • 코드 라인 26에서 /proc/bus 디렉토리를 생성한다.
  • 코드 라인 27에서 /proc/sys 디렉토리를 생성하고 proc 디렉토리 오페레이션이 되도록 연결한다. 그런 후 그 하위 디렉토리에 다음 디렉토리들을 추가로 생성하고 해당 디렉토리 내에 관련 proc 파일들을 연결한다.
    • /proc/sys/kernel 디렉토리 및 하위 proc 파일들
    • /proc/sys/vm 디렉토리 및 하위 proc 파일들
    • /proc/sys/fs 디렉토리 및 하위 proc 파일들
    • /proc/sys/debug 디렉토리 및 하위 proc 파일들
    • /proc/sys/dev 디렉토리 및 하위 proc 파일들

 

register_filesystem()

fs/filesystems.c

/**
 *      register_filesystem - register a new filesystem
 *      @fs: the file system structure
 *
 *      Adds the file system passed to the list of file systems the kernel
 *      is aware of for mount and other syscalls. Returns 0 on success,
 *      or a negative errno code on an error.
 *
 *      The &struct file_system_type that is passed is linked into the kernel 
 *      structures and must not be freed until the file system has been
 *      unregistered.
 */

int register_filesystem(struct file_system_type * fs)
{
        int res = 0;
        struct file_system_type ** p;

        BUG_ON(strchr(fs->name, '.'));
        if (fs->next)
                return -EBUSY;
        write_lock(&file_systems_lock);
        p = find_filesystem(fs->name, strlen(fs->name));
        if (*p)
                res = -EBUSY;
        else
                *p = fs;
        write_unlock(&file_systems_lock);
        return res;
}

EXPORT_SYMBOL(register_filesystem);

file_system_type 구조체를 전역 &file_systems의 마지막에 추가한다. name(이름)에 “.”이 있거나 이미 사용한 이름이 있는 경우 에러가 반환된다.

 

아래 그림은 rpi2 시스템에 등록되는 파일 시스템들을 보여준다. 그 외에 arm64에서 사용되는 hgetlbfs 및 임베디드 시스템의 플래시 파일 시스템으로 자주 사용되는 jffs2 파일 시스템도 별도로 표기하였다. (그 외의 파일 시스템은 자주 사용하지 않으므로 생략)

 

proc 파일 시스템 마운트

proc_mount()

fs/proc/root.c

static struct dentry *proc_mount(struct file_system_type *fs_type,
        int flags, const char *dev_name, void *data)
{
        int err;
        struct super_block *sb;
        struct pid_namespace *ns;
        char *options;

        if (flags & MS_KERNMOUNT) {
                ns = (struct pid_namespace *)data;
                options = NULL;
        } else {
                ns = task_active_pid_ns(current);
                options = data;

                if (!capable(CAP_SYS_ADMIN) && !fs_fully_visible(fs_type))
                        return ERR_PTR(-EPERM);

                /* Does the mounter have privilege over the pid namespace? */
                if (!ns_capable(ns->user_ns, CAP_SYS_ADMIN))
                        return ERR_PTR(-EPERM);
        }

        sb = sget(fs_type, proc_test_super, proc_set_super, flags, ns);
        if (IS_ERR(sb))
                return ERR_CAST(sb);

        if (!proc_parse_options(options, ns)) {
                deactivate_locked_super(sb);
                return ERR_PTR(-EINVAL);
        }

        if (!sb->s_root) {
                err = proc_fill_super(sb);
                if (err) {
                        deactivate_locked_super(sb);
                        return ERR_PTR(err);
                }

                sb->s_flags |= MS_ACTIVE;
        }

        return dget(sb->s_root);
}

커널에 등록된 proc 파일 시스템을 마운트한다.

  • 코드 라인 9~22에서 다음 커널 경로를 통해 호출되어 MS_KERNMOUNT 플래그가 붙은 경우 인자로 전달 받은 ns(네임 스페이스)를 알아온다. 그 외의 경우 태스크에서 사용되는 ns를 알아온다.
    • mq_init_ns(), pid_ns_prepare_proc() 및 init_hugetlbfs_fs() 함수를 사용하는 경우 다음 경로를 통해 ns를 포함하여 커널 마운트 플래그가 사용된다.
      • ns 사용: kern_mount_data() -> vfs_kern_mount() -> mount_fs()
    • simple_pin_fs() 함수를 사용하는 경우 다음 커널 경로를 통해 ns에 null이 대입된 상태로 커널 마운트 플래그가 사용된다.
      • ns=null: vfs_kern_mount() -> mount_fs()
  • 코드 라인 24~26에서 인자로 요청한 동일한 파일 시스템 타입의 수퍼 블럭들이 proc_test_super() 함수의 결과와 동일하면 proc_set_super() 함수를 호출한다.
    • proc_test_super() 함수를 통해 ns와 동일한 수퍼블럭인지 체크
    • proc_set_super() 함수를 통해 해당 수퍼 블럭을 가상 파일 시스템에 사용하는 anon 타입으로 할당하고 수퍼 블럭의 s_fs_info에 pid 값을 지정한다.
  • 코드 라인 28~31에서 커널 마운트가 아닌 경우 인자로 전달 받은 옵션 문자열을 파싱한다. proc 파일 시스템은 다음 두 개의 옵션을 파싱하여 사용한다.
    • “hidepid=<0~2>”
      • 0: 모든 유저에게 /proc/<pid>/*이 다 보이고 읽을 수 있는 커널 디폴트 설정이다.
      • 1: 모든 유저에게 /proc/<pid>/*이 다 보이지만 owner 유저만 읽을 수 있는 설정이다.
      • 2: owner 유저만 /proc/<pid>/*이 보이고 읽을 수 있는 설정이다.
    • “gid=XXX”
      • hidepid=0을 사용하면서 지정된 그룹이 모든 프로세스 정보를 수집할 수 있게 한다.
    • 참고:
  • 코드 라인 33~41에서 수퍼 블록에서 루트가 지정되지 않은 루트 inode를 생성하고 초기화한 후 지정한다.
  • 코드 라인 43에서 루트 inode에 대한 참조 카운터를 증가시키고 리턴한다.

 

다음 그림은 proc_mount() 함수에 도달하는 과정과 그 이후의 처리를 보여준다.

 

참고

댓글 남기기