System Call에 대해 알아보자
System Call
시스템 프로그래밍
- System Call: OS가 제공하는 기능들을 사용하는 것- 하드웨어를 제어하거나,
- 다른 프로세스와의 통신을 수행하거나,
- 시스템 정보에 접근, 수정하거나,
- 시스템을 제어하는 기능 등
 
- 대표적인 시스템 콜- 화면 출력: printf() <- C 라이브러리. 내부에서 write() system call 사용
- 파일 제어: open(), close(), read(), write()
- 동적 메모리 할당: malloc() <- C 라이브러리. 내부에서 brk(), mmap() 사용
- 네트워크 통신: socket(), send(), receive()
 
System Call vs. Library function
- 시스템 호출- OS Kernel이 제공하는 서비스를 이용해 프로그램을 작성할 수 있도록 제공되는 프로그래밍 인터페이스
- 기본적인 형태는 C 언어의 함수 형태로 제공- 리턴값 = 시스템호출명(인자, …);
 
- C Library 가 이러한 형태로 편리하게 이용할 수 있게 제공해주는 것
 
- 라이브러리 함수- 라이브러리 : 미리 컴파일된 함수들을 묶어서 제공하는 특수한 형태의 파일- /lib,- /usr/lib에 위치하며- lib*.a또는- lib*.so(static object) 형태로 제공
 
- 자주 사용하는 기능을 독립적으로 분리하여 구현해둠으로써 프로그램의 개발과 디버깅을 쉽게하고 컴파일을 좀 더 빠르게 할 수 있다
- OS 커널과 무관하게 단순한 C 코드를 수행- 예) strcpy() : 문자열 복사를 위한 연산을 수행. 편의를 위한 라이브러리 함수
 
 
- 라이브러리 : 미리 컴파일된 함수들을 묶어서 제공하는 특수한 형태의 파일
- 두 가지 시스템 콜 호출 방법- 직접 시스템콜 호출: 본래 시스템콜은 특수한 방식(trap)을 통해 호출하여야 함- 이유1: 시스템콜의 코드가 일반 사용자가 접근할 수 없는 OS 커널 영역에 존재하기 때문
- 이유2: 시스템콜은 다수 존재하고, 번호로 구분하기 때문에 직관적이지 않음
- 굳이 하려면? : syscall() 함수 이용
 
- 라이브러리 함수 사용- C 라이브러리는 시스템콜을 간편히 사용할 수 있게 도와주는 여러 함수 제공
- 예) printf() : 화면 출력을 위해서는 본래 write() 시스템 콜을 사용해야 함
- 일반적으로 거의 모든 경우에 라이브러리 함수를 통해 시스템콜을 이용함
 
 
- 직접 시스템콜 호출: 본래 시스템콜은 특수한 방식(trap)을 통해 호출하여야 함

syscall()의 사용
 1#define _GNU_SOURCE
 2#include <unistd.h>
 3#include <sys/types.h>
 4#include <sys/syscall.h>
 5#include <stdio.h>
 6
 7int main() {
 8        printf("My pid: %d\n", getpid());
 9        printf("My pid: %d by syscall()\n", (int)syscall(39));
10        sleep(1);
11        return 0;
12}
- getpid()- 현재 프로세스의 PID를 반환
- C 라이브러리 함수
- 이 정보는 OS가 관리하기 때문에, 시스템콜을 통해 수행됨
- 이때 sys_getpid 라는 이름의 시스템콜을 이용하며, 해당 시스템콜 번호는 39
 
man page에서의 섹션 구분

printf의 경우 section이 1과 3으로 나뉘어지므로 $ man printf와 $ man 3 printf로 나눠서 볼 수 있다.
Error handling
시스템 호출의 오류 처리방법
- 결과값: 성공하면 0을 리턴, 실패하면 -1을 리턴
- 실패 시, 전역변수 errno에 오류 코드 저장- Extern 을 이용해 C 라이브러리와 사용자 프로그램이 전역 변수를 공유할 수 있음
- Extern: 해당 소스 파일의 외부에서 선언한 변수를 인용해서 사용하는 것
 
- 오류 코드의 확인: errno 유틸리티 사용 <- moreutils설치 후$ errno -l
- 예시
 1#include <unistd.h>
 2#include <stdio.h>
 3#include <errno.h>
 4
 5extern int errno;
 6
 7int main() {
 8        if (access("unix.txt", F_OK == -1) {
 9                printf("errno=%d\n", errno);
10        }       
11        return 0;
12}  
라이브러리 함수의 오류 처리방법
- 오류가 발생하면 NULL을 리턴, 함수의 리턴값이 int 형이면 -1 리턴
- errno 변수에 오류 코드 저장
- 예시
 1#include <stdlib.h>
 2#include <stdio.h>
 3#include <errno.h>
 4
 5extern int errno;
 6
 7int main() {
 8        FILE *fp;
 9        if((fp = fopen("unix.txt", "r")) == NULL) {
10                printf("errno=%d\n", errno);
11                return 1;
12        }
13        fclose(fp);
14
15        return 0;
16}
- fopen()- File stream 을 여는 C 라이브러리 함수
- 해당 파일이 없으면 에러가 나고, NULL 을 리턴
 
보다 편리한 오류 처리
- 오류 메시지 출력 : perror(3)
- Errno 에 따라 에러 메시지를 출력함
- 예시
 1#include <unistd.h>
 2#include <stdio.h>
 3
 4int main() {
 5        if (access("unix.txt", F_OK) == -1) {
 6                perror("my message");
 7                return 1;
 8        }
 9        return 0;
10}