시스템 콜의 유형에 대해 설명해 주세요.
시스템 콜은 커널이 제공하는 기능에 따라 프로세스 제어, 파일 관리, 장치 관리, 정보 유지, 통신, 메모리 관리 6가지로 분류됩니다.
유형 전체 요약
| 유형 | 역할 | 대표 시스템 콜 |
|---|---|---|
| 프로세스 제어 | 프로세스 생성·실행·종료·대기 | fork(), execve(), exit(), wait() |
| 파일 관리 | 파일 열기·읽기·쓰기·닫기 | open(), read(), write(), close() |
| 장치 관리 | 하드웨어 장치 제어 | ioctl(), read(), write() |
| 정보 유지 | 시스템·프로세스 정보 조회·설정 | getpid(), time(), uname() |
| 통신 | 프로세스 간 데이터 교환 | pipe(), socket(), shmget() |
| 메모리 관리 | 메모리 할당·해제·매핑 | mmap(), brk(), munmap() |
1. 프로세스 제어
프로세스의 생명주기(생성 → 실행 → 대기 → 종료)를 관리합니다.
| 시스템 콜 | 설명 |
|---|---|
fork() | 현재 프로세스를 복제하여 자식 프로세스 생성 |
execve() | 현재 프로세스를 새 프로그램으로 교체 |
exit() | 프로세스 종료 |
wait() / waitpid() | 자식 프로세스 종료 대기 |
clone() | 스레드 생성 (공유 범위 지정 가능) |
getpid() | 현재 프로세스 PID 조회 |
kill() | 프로세스에 시그널 전송 |
// Java 예시: 외부 프로세스 실행
Process p = Runtime.getRuntime().exec("ls -al");
int exitCode = p.waitFor(); // 자식 종료 대기
내부 시스템 콜:
fork() → 자식 프로세스 생성
execve("/bin/ls", ...) → ls 프로그램으로 교체
wait4() → 부모가 자식 종료 대기
exit() → 자식 프로세스 종료
┌────────────┐ fork() ┌────────────┐ execve() ┌──────────┐
│ 부모(JVM) │ ───────→ │ 자식(JVM) │ ────────→ │ ls -al │
└─────┬──────┘ └────────────┘ └────┬─────┘
│ wait4() │ exit()
▼ ▼
자식 종료 감지 프로세스 종료
2. 파일 관리
파일의 생성·읽기·쓰기·삭제·속성 변경 등을 처리합니다.
| 시스템 콜 | 설명 |
|---|---|
open() | 파일 열기, 파일 디스크립터 반환 |
read() | 파일에서 데이터 읽기 |
write() | 파일에 데이터 쓰기 |
close() | 파일 디스크립터 닫기 |
lseek() | 파일 내 읽기/쓰기 위치 이동 |
stat() | 파일 메타 정보 조회 (크기, 권한 등) |
unlink() | 파일 삭제 |
rename() | 파일 이름 변경 |
mkdir() / rmdir() | 디렉토리 생성 / 삭제 |
// Java 예시: 파일 읽기/쓰기
FileInputStream fis = new FileInputStream("input.txt"); // open()
byte[] buf = new byte[1024];
int n = fis.read(buf); // read()
fis.close(); // close()
FileOutputStream fos = new FileOutputStream("output.txt"); // open()
fos.write("hello".getBytes()); // write()
fos.close(); // close()
파일 디스크립터 테이블:
┌────┬──────────────┐
│ fd │ 파일 │
├────┼──────────────┤
│ 0 │ stdin │ (표준 입력)
│ 1 │ stdout │ (표준 출력)
│ 2 │ stderr │ (표준 에러)
│ 3 │ input.txt │ ← open()으로 생성
│ 4 │ output.txt │ ← open()으로 생성
└────┴──────────────┘
3. 장치 관리
하드웨어 장치를 파일처럼 다루며, 장치별 세부 제어를 수행합니다.
| 시스템 콜 | 설명 |
|---|---|
ioctl() | 장치별 고유 제어 명령 (범용) |
read() / write() | 장치에서 데이터 읽기/쓰기 |
mknod() | 장치 파일(디바이스 노드) 생성 |
Linux에서는 장치도 파일로 취급 ("Everything is a File")
/dev/sda → 하드디스크
/dev/tty → 터미널
/dev/null → 블랙홀 (쓰면 버려짐)
/dev/random → 랜덤 데이터
사용 예:
open("/dev/sda", O_RDONLY) → 디스크를 파일처럼 열기
read(fd, buf, 512) → 디스크 섹터 읽기
ioctl(fd, HDIO_GET_IDENTITY) → 디스크 모델명 등 하드웨어 정보 조회
// Java 예시: 시리얼 포트 통신 (장치 관리)
// Linux에서 /dev/ttyUSB0 장치 파일을 열어 통신
FileInputStream serial = new FileInputStream("/dev/ttyUSB0"); // open()
int data = serial.read(); // read() → 장치에서 데이터 수신
serial.close(); // close()
4. 정보 유지
시스템이나 프로세스의 정보를 조회하거나 설정합니다.
| 시스템 콜 | 설명 |
|---|---|
getpid() / getppid() | 현재/부모 프로세스 PID 조회 |
getuid() / getgid() | 사용자/그룹 ID 조회 |
time() / clock_gettime() | 현재 시간 조회 |
gettimeofday() | 마이크로초 단위 시간 조회 |
uname() | OS 이름, 버전, 아키텍처 조회 |
sysinfo() | 메모리 사용량, 프로세스 수 등 조회 |
// Java 예시
long pid = ProcessHandle.current().pid(); // getpid()
long now = System.currentTimeMillis(); // clock_gettime(CLOCK_REALTIME)
long nano = System.nanoTime(); // clock_gettime(CLOCK_MONOTONIC)
String os = System.getProperty("os.name"); // uname()
clock_gettime 시스템 콜의 두 가지 시계:
CLOCK_REALTIME → 실제 시간 (1970년 기준 경과 시간)
시스템 시간 변경에 영향 받음
→ System.currentTimeMillis()
CLOCK_MONOTONIC → 단조 증가 시간 (부팅 이후 경과)
시스템 시간 변경에 영향 안 받음
→ System.nanoTime()
5. 통신
프로세스 간 데이터를 교환하는 IPC(Inter-Process Communication) 기능을 제공합니다.
| 시스템 콜 | 방식 | 설명 |
|---|---|---|
pipe() | 파이프 | 부모-자식 간 단방향 통신 |
socket() / bind() / listen() / accept() | 소켓 | 네트워크 또는 로컬 프로세스 간 통신 |
connect() / send() / recv() | 소켓 | 클라이언트 측 연결·송수신 |
shmget() / shmat() | 공유 메모리 | 메모리 영역을 프로세스 간 공유 |
msgget() / msgsnd() / msgrcv() | 메시지 큐 | 메시지 단위로 데이터 교환 |
semget() / semop() | 세마포어 | 프로세스 간 동기화 |
// Java 예시: 소켓 통신
// 서버
ServerSocket server = new ServerSocket(8080); // socket() + bind() + listen()
Socket client = server.accept(); // accept()
InputStream in = client.getInputStream();
int data = in.read(); // recv()
// 클라이언트
Socket socket = new Socket("localhost", 8080); // socket() + connect()
OutputStream out = socket.getOutputStream();
out.write(65); // send()
IPC 방식 비교:
┌────────────┬──────────────┬──────────────┬──────────────┐
│ │ 파이프 │ 소켓 │ 공유 메모리 │
├────────────┼──────────────┼──────────────┼──────────────┤
│ 통신 범위 │ 같은 호스트 │ 네트워크 가능 │ 같은 호스트 │
│ 방향 │ 단방향 │ 양방향 │ 양방향 │
│ 속도 │ 중간 │ 중간 │ 가장 빠름 │
│ 동기화 │ 자동 (블로킹) │ 자동 (블로킹) │ 직접 구현 필요 │
│ 관계 제한 │ 부모-자식 │ 제한 없음 │ 제한 없음 │
└────────────┴──────────────┴──────────────┴──────────────┘
6. 메모리 관리
가상 메모리 할당·해제·매핑을 담당합니다.
| 시스템 콜 | 설명 |
|---|---|
brk() / sbrk() | 힙 영역 끝 주소 확장/축소 (소규모 할당) |
mmap() | 가상 메모리 영역 매핑 (대규모 할당, 파일 매핑) |
munmap() | mmap으로 매핑한 영역 해제 |
mprotect() | 메모리 영역 권한 변경 (R/W/X) |
mlock() | 페이지를 물리 메모리에 고정 (스왑 방지) |
// Java 예시
// 1. 일반 객체 할당 → JVM이 내부적으로 brk() 또는 mmap() 호출
Object obj = new Object();
// 2. 대규모 메모리 → mmap()
byte[] huge = new byte[100 * 1024 * 1024]; // 100MB
// 3. 메모리 매핑 파일 → mmap()
FileChannel fc = FileChannel.open(Path.of("data.bin"));
MappedByteBuffer mbb = fc.map(FileChannel.MapMode.READ_ONLY, 0, fc.size());
brk() vs mmap():
[brk()]
힙 영역의 끝 주소를 이동하여 확장
┌──────────┬──────────┬─────────┐
│ 코드 │ 데이터 │ 힙 →→ │
└──────────┴──────────┴────┬────┘
│
brk 포인터 이동
[mmap()]
가상 주소 공간의 빈 영역에 새로운 매핑 생성
┌──────┬──────┬──────┬────────┬───────┐
│ 코드 │ 데이터 │ 힙 │ mmap() │ 스택 │
└──────┴──────┴──────┴────────┴───────┘
↑ 새로 매핑된 영역
일반적으로:
소규모 할당 (< 128KB) → brk()
대규모 할당 (≥ 128KB) → mmap()
6가지 유형의 관계
사용자 프로세스가 커널에 요청하는 모든 것
┌─────────────────────────────────────────────────────┐
│ 시스템 콜 │
├──────────┬──────────┬──────────┬──────────┬─────────┤
│ 프로세스 │ 파일 │ 장치 │ 정보 유지 │ 통신 │
│ 제어 │ 관리 │ 관리 │ │ │
│ │ │ │ │ │
│ fork() │ open() │ ioctl() │ getpid() │ pipe() │
│ exec() │ read() │ read() │ time() │ socket()│
│ exit() │ write() │ write() │ uname() │ shmget()│
│ wait() │ close() │ │ │ │
├──────────┴──────────┴──────────┴──────────┴─────────┤
│ 메모리 관리 │
│ mmap() / brk() / munmap() │
└─────────────────────────────────────────────────────┘