ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • System call과 glibc
    2013. 2. 24. comments
    반응형

    System call이란?

    자, 일단 System call이란 사용자 공간(User Space)에서 시스템(Kernel)에게 필요한 기능을 직접 요청하는 것으로 프로세스 또는 디스크 I/O, 메모리 요청등 종류도 다양하다. 리눅스에서 사용자 공간에 제공되는 System call들은 다양한 아키텍쳐(i386, powerPC,..)등을 지원하며 약 300여개 정도 명령들을 지원한다. 


    ex) 리눅스 커널에서 x86 시스템에 대한 System call Table(arch/x86/kernel/syscall_table_32.S)

    ENTRY(sys_call_table)

        .long sys_restart_syscall   /* 0 - old "setup()" system call, used for restarting */

        .long sys_exit

        .long sys_fork

        .long sys_read

        .long sys_write

        .long sys_open      /* 5 */

        .long sys_close

        .long sys_waitpid

        .long sys_creat

        .long sys_link

        .long sys_unlink    /* 10 */

        .long sys_execve

        .long sys_chdir

    ..

    ..



    사용자 공간에서 직접 System call을 요청하는 것은 쉬운일이 아니다. 왜냐하면 System call을 요청할 때 CPU 아키텍쳐에 맞는 값(machine register)을 전달해야 하기 때문이다. 또한 잘못된 값을 전달했을 때의 커널에서의 오동작을 예방해야 한다. 그렇기 때문에 System call은 일반적으로 compiler와 System call를 wrap한 라이브러리를 통해서 아키텍쳐 종속적인 부분을 자동적으로 해결하고 처리할 수 있도록 제공되어진다.



    gcc 컴파일러와 glibc 라이브러리


    제목에서도 나왔듯이 리눅스에서 그 라이브러리는 glibc 이고 compiler는 gcc 다.

    posix, systemv의 C 표준 인터페이스를 제공하고 내부적으로는 시스템 아키텍쳐 별로 대한 구현이 각각 따로 되어 있다. glibc 소스코드를 다운받아서 내용을 찾아보면, 대부분의 System call 지원 함수들은 INLINE_SYSCALL매크로를 통하여 전달하는데, INLINE_SYSCALL들은 시스템 아키텍쳐마다 따로 구현이 되어 있음을 확인할 수 있다.


    ex) sysdeps/unix/sysv/linux/open64.c

    /* Open FILE with access OFLAG.  If OFLAG includes O_CREAT,     

       a third argument is the file protection.  */     

    int     

    __libc_open64 (const char *file, int oflag, ...)    

    {    

      int mode = 0;    

        

      if (oflag & O_CREAT)    

        {    

          va_list arg;    

          va_start (arg, oflag);    

          mode = va_arg (arg, int);    

          va_end (arg);    

        }    

        

      if (SINGLE_THREAD_P)    

        return INLINE_SYSCALL (open, 3, file, oflag | O_LARGEFILE, mode);    

        

      int oldtype = LIBC_CANCEL_ASYNC ();    

        

      int result = INLINE_SYSCALL (open, 3, file, oflag | O_LARGEFILE, mode);    

        

      LIBC_CANCEL_RESET (oldtype);    

        

      return result;    

    }    




    아래는 x86_64에 대한 INLINE_SYSCALL 매크로 구현 코드 부분이다. 결국 어셈코드로써 syscall이라는 명령을 사용하는것을 확인할 수 있다.

    # undef INLINE_SYSCALL

    # define INLINE_SYSCALL(name, nr, args...) \

      ({                                          \

        unsigned long int resultvar = INTERNAL_SYSCALL (name, , nr, args);        \

        if (__builtin_expect (INTERNAL_SYSCALL_ERROR_P (resultvar, ), 0))         \

          {                                       \

        __set_errno (INTERNAL_SYSCALL_ERRNO (resultvar, ));           \

        resultvar = (unsigned long int) -1;                   \

          }                                       \

        (long int) resultvar; })

      


    # define INTERNAL_SYSCALL_NCS(name, err, nr, args...) \

      ({                                          \

        unsigned long int resultvar;                          \

        LOAD_ARGS_##nr (args)                             \

        LOAD_REGS_##nr                                \

        asm volatile (                                \

        "syscall\n\t"                                 \

        : "=a" (resultvar)                                \

        : "0" (name) ASM_ARGS_##nr : "memory", "cc", "r11", "cx");            \

        (long int) resultvar; })

    # undef INTERNAL_SYSCALL

    # define INTERNAL_SYSCALL(name, err, nr, args...) \

      INTERNAL_SYSCALL_NCS (__NR_##name, err, nr, ##args)




    그렇다 보니, System call을 직접 구현하고 표준에 넣기란 간단하지 않다. 각 시스템 아키텍쳐에 맞게 모두 구현해줘야 하기 때문이다. 그래서 사용자 공간에서 커널에 무언가를 요청할 때에는 Character Device를 만들고 그 파일을 사용해서 서로 정보를 주고 받는다. 



    반응형

    댓글

Designed by Tistory.