목차
프로그램이 파일을 읽는 코드는 한 줄이다. 하지만 그 한 줄 안에서 프로그램은 사실 파일을 직접 읽지 않는다. OS에게 “읽어 달라”고 부탁한다. 그 부탁의 정체가 시스템 콜이다.
프로그램은 파일을 직접 못 읽는다
read(file) 한 줄이면 파일 내용이 손에 들어온다. 그런데 디스크는 여러 프로그램이 함께 쓰는 공용 하드웨어다. 아무 프로그램이나 디스크를 직접 주무르면, 남의 파일을 덮어쓰거나 하드웨어를 망가뜨릴 수 있다.
그래서 OS는 규칙을 세운다 - 하드웨어는 OS만 직접 만진다. 프로그램은 원하는 걸 OS에게 부탁하고, OS가 대신 해준다. 이 부탁의 창구가 시스템 콜(system call) 이다.
두 개의 모드: 유저와 커널
이 규칙을 하드웨어가 강제한다. CPU는 두 가지 모드로 돈다.
- 유저 모드(user mode) - 우리 프로그램이 도는 모드. 자기 메모리만 만질 수 있고, 하드웨어엔 직접 못 닿는다.
- 커널 모드(kernel mode) - OS의 핵심(커널)이 도는 모드. 하드웨어를 직접 다루고 뭐든 할 수 있다.
프로그램은 늘 유저 모드에서 돈다. 파일·네트워크·새 프로세스처럼 하드웨어나 특권이 필요한 일은 유저 모드에서 못 한다. 그럴 때만 커널 모드로 잠깐 넘어간다.
시스템 콜: 커널에게 부탁하는 창구
시스템 콜은 유저 모드 프로그램이 커널 모드로 잠깐 넘어가 일을 부탁하는 정해진 창구다.
프로그램이 read를 부르면 커널 모드로 넘어가고, 커널이 디스크에서 실제로 읽어 결과를 돌려준 뒤, 다시 유저 모드로 내려와 프로그램이 이어 간다. 프로그램 입장에선 함수 한 번 부른 것 같지만, 그 안에서 모드 경계를 넘었다 왔다.
왜 굳이 경계를 두나
이 경계가 앞 글들의 격리와 한 몸이다. 가상 메모리가 프로세스끼리 메모리를 못 넘보게 했듯, 유저/커널 모드는 프로그램이 하드웨어와 OS의 영역을 못 넘보게 한다.
덕분에 프로그램 하나가 잘못돼도 하드웨어나 다른 프로그램을 직접 망가뜨릴 수 없다 - 기껏해야 자기 유저 모드 안에서만 탈이 난다. 커널이라는 문지기를 반드시 거치게 해서, 시스템 전체를 한 프로그램의 실수로부터 지킨다.
경계를 넘는 건 공짜가 아니다
문지기를 거치는 데는 비용이 든다. 유저 → 커널로 넘어갈 때 CPU는 모드를 바꾸고, 커널이 안전하게 일하도록 상태를 정돈한다. 일을 마치면 다시 유저 모드로 돌아오며 정돈을 되돌린다. 이 왕복 자체가 CPU 시간을 먹는다.
한 번은 미미하다. 하지만 시스템 콜을 엄청 자주 부르면 이 왕복 비용이 쌓인다. “1바이트씩 백만 번 읽기”가 “한 번에 크게 읽기”보다 훨씬 느린 이유가 여기 있다 - 앞은 경계를 백만 번 넘고, 뒤는 몇 번만 넘는다.
무엇이 시스템 콜인가
우리가 무심코 쓰는 많은 것이 사실 시스템 콜이다.
- 파일 - 열기·읽기·쓰기·닫기.
- 네트워크 - 소켓 열기, 보내기·받기.
- 프로세스 - 새 프로세스 만들기, 끝나길 기다리기.
- 메모리 - 힙을 더 달라고 요청하기.
공통점은 혼자 힘으로 안 되고 OS의 손이 필요한 일이라는 것이다. 순수한 계산(더하기·반복문)은 시스템 콜이 아니다 - 유저 모드에서 CPU만으로 되니까. 하지만 바깥세상(디스크·네트워크·다른 프로세스)과 닿는 순간 커널을 거친다.
그래서 I/O엔 이유 있는 느림이 있다
파일·네트워크 작업이 계산보다 느린 데는 두 겹의 이유가 있다. 하나는 시스템 콜로 경계를 넘는 비용이고, 더 큰 하나는 커널이 실제로 디스크나 네트워크의 응답을 기다리는 시간이다. 디스크가 데이터를 찾고 네트워크 저편이 답할 때까지, 그 일을 시킨 스레드는 멈춰 서서 기다린다.
이 “커널 안에서 기다리는” 동안 스레드가 무엇을 하는지 - 그게 다음 글 블로킹 I/O의 핵심이다. 시스템 콜이 바로 돌아오지 않고 한참 안 돌아오는 그 순간이 블로킹이다.
실무에서: 시스템 콜을 줄이면 빨라진다
- 모아서 한 번에 - 작게 여러 번 부르지 말고 크게 한 번 부른다. 로그를 한 줄씩 쓰지 않고 모아서 쓰거나, 버퍼에 담아 뭉쳐 내보내는 게 이 원리다. 경계 넘는 횟수를 줄인다.
- 어떤 시스템 콜을 얼마나 부르나 본다 - 프로그램이 실제로 어떤 시스템 콜을 얼마나 자주 부르는지 추적하는 도구가 있다. 느린 원인이 과도한 I/O 호출인지 여기서 드러난다.
- I/O 비용을 계산 비용과 다르게 본다 - “코드 줄 수”가 아니라 “바깥세상과 몇 번 닿나”가 성능을 좌우할 때가 많다. 파일·네트워크·DB 호출이 그 지점이다.
정리
- 프로그램은 하드웨어를 직접 못 만진다 - OS에게 부탁해야 하고, 그 창구가 시스템 콜이다.
- CPU는 유저 모드(내 프로그램)와 커널 모드(OS)로 나뉘고, 하드웨어·특권이 필요한 일만 커널 모드로 잠깐 넘어간다.
- 이 경계가 프로그램의 실수로부터 시스템을 지키지만, 넘나듦 자체가 비용이라 시스템 콜을 자주 부르면 느려진다.
- 파일·네트워크·프로세스·메모리 요청이 시스템 콜이고, 순수 계산은 아니다 - 바깥세상과 닿을 때 커널을 거친다.
다음 글은 그 파일·소켓·연결을 프로그램이 무엇으로 가리키는지 - 파일 디스크립터로 간다.