목차
프로그램이 파일을 읽는 코드는 한 줄이다. 하지만 그 한 줄 안에서 프로그램은 사실 파일을 직접 읽지 않는다. 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)로 나뉘고, 하드웨어·특권이 필요한 일만 커널 모드로 잠깐 넘어간다.
- 이 경계가 프로그램의 실수로부터 시스템을 지키지만, 넘나듦 자체가 비용이라 시스템 콜을 자주 부르면 느려진다.
- 파일·네트워크·프로세스·메모리 요청이 시스템 콜이고, 순수 계산은 아니다 - 바깥세상과 닿을 때 커널을 거친다.
다음 글은 그 파일·소켓·연결을 프로그램이 무엇으로 가리키는지 - 파일 디스크립터로 간다.