
시스템콜
OS를 deep 하게 공부할때 선행하면 좋은 개념이다.
시스템 콜은 운영체제의 커널이 제공하는 서비스를 사용자 프로그램이 이용하기 위한 유일한 통로
사용자 모드에서 실행되는 프로그램이 파일 입출력, 프로세스 생성 등 특권 명령이 필요한 작업을 수행해야 할 때, 커널 모드로 전환하여 OS에게 해당 작업을 요청하는 방법
목적 : OS 자원에 대한 직접적인 접근을 제한하여 시스템의 안정성과 보호를 유지
응용 프로그래밍 인터페이스와 시스템 콜
프로그래머가 시스템 콜을 직접 호출하는 경우는 드물다. 대신 API(Application Programming Interface) 를 통해 접근한다.
시스템 콜
- 커널에게 서비스를 요청하는 실제 인터페이스
- 커널 내부에 존재
- 하나의 API 함수가 하나 이상의 시스템 콜을 호출할 수 있다
API
- 프로그래머가 시스템 콜에 접근하기 위해 사용하는 함수들의 집합
- 사용자 모드 라이브러리에 존재
- 대표적인 API 3가지:
- Windows API (Win32/Win64) : Windows 시스템용
- POSIX API : Unix/Linux/macOS 공통 표준
- Java API : JVM 기반, 플랫폼 독립적
동작과정
- 준비 (Preparation):
- 사용자 프로그램이 API(예:
read())를 호출하면, 라이브러리는 해당 요청에 맞는 시스템 콜 번호를 특정 레지스터에 넣음 - 추가적인 매개변수(파일 이름, 버퍼 주소 등)도 전달해야 하는데, 전달 방식은 3가지가 있다:
- 레지스터에 직접 전달 : 가장 단순하지만 레지스터 수보다 매개변수가 많으면 불가능
- 메모리 블록(테이블)에 저장 : 매개변수를 메모리에 넣고 시작 주소만 레지스터에 전달
- 스택에 push : 매개변수를 스택에 push하고 OS가 pop하여 가져감
- 사용자 프로그램이 API(예:
- 트랩 발생 (The Trap):
trap이나syscall명령을 실행. 이 순간 하드웨어가 실행 모드를 사용자 모드(1)에서 커널 모드(0)로 즉시 전환
- 커널 서비스 실행 (Kernel Execution):
- 커널은 레지스터에 저장된 시스템 콜 번호를 읽어 시스템 콜 테이블에서 실행할 루틴을 찾음
- 시스템 콜 테이블은 시스템 콜 번호를 인덱스로 사용하여 해당 서비스 루틴의 주소를 찾는 구조
- 매개변수의 유효성을 검사한 뒤 작업을 수행
- 결과 반환 및 복귀 (Return):
- 작업 결과나 상태 코드를 특정 레지스터에 저장
- 'Return from interrupt' 명령을 통해 모드 비트를 1(사용자 모드)로 되돌리고, 시스템 콜 바로 다음 지점부터 프로그램을 재개
시스템 콜에서 자주 쓰이는 용어
fd(File Descriptor) : 열린 파일이나 장치를 가리키는 정수 번호. open()이 반환하고, read/write/close 등이 이 번호로 대상을 식별한다- 0 = stdin(표준 입력), 1 = stdout(표준 출력), 2 = stderr(표준 에러)
- 새로 열면 3부터 순서대로 배정
ls -l /proc/$$/fd # 현재 프로세스의 fd 목록 확인
buf(Buffer) : 데이터를 임시로 담아두는 메모리 공간. read()는 파일에서 읽은 데이터를 buf에 저장하고, write()는 buf의 데이터를 파일에 쓴다path: 파일이나 디렉토리의 경로. open("/etc/hostname")처럼 대상을 지정할 때 사용- 절대 경로:
/etc/hostname(루트부터 전체 경로) - 상대 경로:
./test.txt(현재 위치 기준)
- 절대 경로:
pid(Process ID) : 프로세스를 식별하는 고유 정수. fork()가 자식의 pid를 반환하고, kill(pid)로 특정 프로세스에 신호를 보냄다mode: 파일의 접근 권한을 나타내는 값. chmod(path, 0755)처럼 8진수로 표현- r=4, w=2, x=1 → rwx=7, r-x=5, r--=4
offset: 파일 내에서 읽기/쓰기 위치를 나타내는 바이트 단위 값. lseek(fd, offset)로 위치를 이동size/count: 읽거나 쓸 데이터의 크기(바이트 수). read(fd, buf, 1024)면 최대 1024바이트 읽기flags: 동작 모드를 지정하는 옵션 값. open()에서 사용- O_RDONLY(읽기전용), O_WRONLY(쓰기전용), O_RDWR(읽기+쓰기)
- O_CREAT(없으면 생성), O_APPEND(끝에 추가), O_TRUNC(기존 내용 지우고 열기)
sig(Signal) : 프로세스에 보내는 신호. kill(pid, sig)로 전송- SIGTERM(15) = 정상 종료 요청, SIGKILL(9) = 강제 종료, SIGINT(2) = Ctrl+C
시스템 콜 인자 레퍼런스
프로세스 제어
fork()— 인자: 없음 / 반환: 부모에게 자식 pid, 자식에게 0, 실패 시 -1exec(path, args)— path: 실행할 프로그램 경로 ("/bin/ls"), args: 인자 배열 (["ls", "-la"])exit(status)— status: 종료 상태 코드. 0이면 정상, 0 외이면 오류wait(status)— status: 자식의 종료 상태를 저장할 포인터 / 반환: 종료된 자식의 pidabort()— 인자: 없음. 호출 시 현재 프로세스에 SIGABRT 전송getpid()— 인자: 없음 / 반환: 현재 프로세스의 pidkill(pid, sig)— pid: 대상 프로세스 ID, sig: 보낼 신호 번호 (9=SIGKILL, 15=SIGTERM)
파일 관리
open(path, flags, mode)— path: 파일 경로, flags: 열기 모드(O_RDONLY 등), mode: 생성 시 권한(0644) / 반환: fd, 실패 시 -1close(fd)— fd: 닫을 파일 디스크립터read(fd, buf, count)— fd: 읽을 대상, buf: 데이터 저장할 버퍼, count: 읽을 최대 바이트 수 / 반환: 실제 읽은 바이트 수write(fd, buf, count)— fd: 쓸 대상, buf: 쓸 데이터가 담긴 버퍼, count: 쓸 바이트 수 / 반환: 실제 쓴 바이트 수lseek(fd, offset, whence)— fd: 대상 파일, offset: 이동할 바이트 수, whence: 기준점 (SEEK_SET=파일 시작, SEEK_CUR=현재 위치, SEEK_END=파일 끝)unlink(path)— path: 제거할 파일 경로
장치 관리
ioctl(fd, request, arg)— fd: 장치 fd, request: 장치별 제어 명령 번호, arg: 명령에 전달할 인자
정보 유지
time(t)— t: 시간을 저장할 포인터 (NULL 가능) / 반환: 유닉스 타임스탬프 (초)gettimeofday(tv, tz)— tv: 시간을 저장할 구조체 (초 + 마이크로초), tz: 타임존 (NULL 가능)
통신
pipe(fd[2])— fd[2]: 파이프의 읽기(fd[0])/쓰기(fd[1]) 끝을 담을 배열shm_open(name, flags, mode)— name: 공유 메모리 이름 ("/my_shm"), flags: 열기 모드, mode: 권한mmap(addr, length, prot, flags, fd, offset)— addr: 매핑 시작 주소(NULL이면 OS가 결정), length: 매핑 크기, prot: 보호 모드(PROT_READ/WRITE), flags: 매핑 옵션(MAP_SHARED 등), fd: 대상 파일/공유메모리, offset: 시작 오프셋munmap(addr, length)— addr: 해제할 매핑 시작 주소, length: 해제할 크기socket(domain, type, protocol)— domain: 주소 체계(AF_INET=IPv4, AF_INET6=IPv6, AF_UNIX=로컬), type: 소켓 유형(SOCK_STREAM=TCP, SOCK_DGRAM=UDP), protocol: 보통 0send(fd, buf, len, flags)— fd: 소켓 fd, buf: 보낼 데이터, len: 데이터 크기, flags: 옵션(0이면 기본)recv(fd, buf, len, flags)— fd: 소켓 fd, buf: 받을 버퍼, len: 버퍼 크기, flags: 옵션
보호
chmod(path, mode)— path: 대상 파일 경로, mode: 설정할 권한 (0755 등)chown(path, owner, group)— path: 대상, owner: 새 소유자 UID, group: 새 그룹 GIDumask(mask)— mask: 새 파일 생성 시 제거할 권한 마스크 (0022 등) / 반환: 이전 umask 값setuid(uid)— uid: 설정할 유효 사용자 IDsetgid(gid)— gid: 설정할 유효 그룹 ID
시스템 콜 유형
프로세스 제어
- 프로세스의 생성, 실행, 종료, 일시 정지 등 생명주기를 관리
fork(): 현재 프로세스를 복제하여 자식 프로세스를 생성. 부모에게는 자식 PID를, 자식에게는 0을 반환
strace -f -e fork bash -c "ls" # fork 시스템콜 추적
ps -ef | grep bash # bash 프로세스와 자식 확인
exec(): 현재 프로세스의 메모리를 새로운 프로그램으로 교체하여 실행. fork() 후 자식에서 호출하는 패턴이 일반적
exec top # 현재 쉘이 top으로 대체됨 (돌아오지 않음)
exit(): 현재 프로세스를 정상 종료. OS에 종료 상태 값을 반환
exit 0 # 정상 종료 (상태코드 0)
exit 1 # 오류로 종료
echo $? # 직전 명령어의 종료 상태코드 확인
wait(): 부모 프로세스가 자식 프로세스의 종료를 기다림
sleep 5 & # 백그라운드로 5초 대기
wait $! # 방금 실행한 백그라운드 프로세스 종료 대기
echo "done"
abort(): 프로세스를 비정상 강제 종료
kill -SIGABRT 1234 # PID 1234에 SIGABRT 전송
kill -9 1234 # SIGKILL로 무조건 강제 종료
getpid(): 현재 프로세스의 PID(프로세스 ID)를 반환
echo $$ # 현재 쉘의 PID
echo $PPID # 부모 프로세스의 PID
파일 관리
- 파일의 생성, 삭제, 열기, 닫기, 읽기, 쓰기 등 파일 시스템 관련 작업을 관리
open(): 파일을 열고 파일 디스크립터(fd)를 반환
exec 3< /etc/hostname # fd 3으로 파일 열기
cat <&3 # fd 3에서 읽기
exec 3<&- # fd 3 닫기
ls -l /proc/$$/fd # 현재 프로세스의 열린 fd 목록
close(): 열린 파일 디스크립터를 닫음. 자원 해제read(): 파일에서 데이터를 읽어 버퍼에 저장write(): 버퍼의 데이터를 파일에 쓰기
cat /etc/hostname # 내부적으로 open+read+write+close
echo "hello" > test.txt # open(O_WRONLY|O_CREAT) + write + close
echo "world" >> test.txt # open(O_APPEND) + write + close
lseek(): 파일 내 읽기/쓰기 위치(오프셋)를 이동
dd if=/etc/hostname bs=1 skip=3 count=5 # 3바이트 건너뛰고 5바이트 읽기
unlink(): 파일의 링크를 제거. 링크가 0이 되면 파일 삭제
rm test.txt # 내부적으로 unlink() 호출
unlink test.txt # 직접 unlink 명령어
장치 관리
- I/O 장치의 요청, 해제, 제어 등을 관리
ioctl(): 장치별 고유한 제어 명령을 전달
ethtool eth0 # 네트워크 카드 상태/속도 (ioctl 기반)
hdparm -I /dev/sda # 디스크 장치 정보
read()/write(): 장치를 대상으로 데이터를 읽거나 쓰기 (파일과 동일한 인터페이스)
cat /dev/urandom | head -c 16 | xxd # 랜덤 장치에서 16바이트 읽기
echo "test" > /dev/null # null 장치에 쓰기 (버려짐)
request(): 장치 사용을 OS에 요청release(): 장치 사용을 해제
mount /dev/sdb1 /mnt/usb # 장치 요청 + 파일시스템 연결
umount /mnt/usb # 장치 해제
정보 유지 관리
- 시스템 시간, 날짜, 프로세스 정보 등 시스템 상태 정보를 얻거나 설정
getpid(): 현재 프로세스의 PID 반환time(): 1970년 1월 1일부터 경과한 초(유닉스 타임스탬프)를 반환gettimeofday(): 현재 시간을 마이크로초 단위까지 반환uname(): 시스템 정보(커널 버전, 호스트명 등)를 반환
date # 현재 날짜/시간 (time/gettimeofday)
date +%s # 유닉스 타임스탬프
uname -a # 커널 버전, 호스트명, 아키텍처 전체
uname -r # 커널 버전만
uptime # 시스템 가동 시간
통신
- 프로세스 간 통신을 위한 연결 생성, 메시지 송수신 등을 관리
pipe(): 부모-자식 프로세스 간 단방향 통신 채널 생성
ls -la | grep ".txt" # ls stdout → grep stdin
ps aux | sort -k 3 -rn | head -5 # CPU 상위 5개
shm_open(): 공유 메모리 객체를 생성/열기mmap(): 파일이나 공유 메모리를 프로세스 주소 공간에 매핑munmap(): mmap()으로 매핑된 영역을 해제
ls -la /dev/shm/ # 공유 메모리 객체 목록
ipcs -m # System V 공유 메모리 확인
socket(): 네트워크 통신 끝점(소켓) 생성send(): 소켓으로 데이터 전송recv(): 소켓으로 데이터 수신
nc -l 8080 # 서버: 8080 대기 (socket+bind+listen+accept)
nc localhost 8080 # 클라이언트: 연결 (socket+connect)
# 연결 후 텍스트 입력 → 상대방에게 전송 (send/recv)
ss -tlnp # 열린 TCP 소켓 확인
ss -ulnp # 열린 UDP 소켓 확인
보호
- 자원 접근 권한 관리
chmod(): 파일/디렉토리의 접근 권한(rwx)을 변경
chmod 755 script.sh # 소유자 rwx, 그룹/기타 r-x
chmod u+x script.sh # 소유자에게 실행 권한 추가
chmod -R 644 /var/www # 하위 전체 재귀적 적용
umask(): 새 파일의 기본 권한 마스크 지정
umask # 현재 umask 값 확인 (보통 0022)
umask 0027 # 새 파일: 640, 새 디렉토리: 750
chown(): 파일 소유자(owner)와 그룹(group) 변경
chown bob:developers file.txt # 소유자 bob, 그룹 developers
chown -R www-data:www-data /var/www # 웹 디렉토리 전체
setuid(): 프로세스의 유효 사용자 ID(EUID) 변경. 임시 권한 상승setgid(): 프로세스의 유효 그룹 ID(EGID) 변경
find / -perm -4000 -type f 2>/dev/null # SUID 설정된 파일 찾기
ls -la /usr/bin/passwd # -rwsr-xr-x (s = SUID)
sudo command # 임시 root 권한 상승 (내부적으로 setuid)
id # 현재 UID, GID, EUID 확인