시스템 프로그래밍은 시스템 콜에서 시작해서 시스템 콜로 끝난다.
시스템 콜이란 운영체제에 리소스나 서비스를 요청하려고 사용자 영역(텍스트 편집기나 게임...)에서 시작해서 커널 내부로 들어가는 함수 호출이다. 시스템 콜에는 read(), write() 같은 익숙한 함수부터, get_narea(), set_tid_address() 같은 생소한 함수 까지 그 범위가 다양하다.
리눅스에서 구현된 시스템 콜은 다른 대부분의 운영체제 커널에서 제공하는 시스템 콜보다 더 적다.
예를 들어 x86-64 아키텍처의 리눅스 시스템 콜은 약 300여 개인데 비해 마이크로소프트 윈도우에는 수천여 개의 시스템 콜이 있다. 리눅스 커널에서는 표준 시스템 콜을 Alph, x86-64, PowerPC 같은 개별 아키텍처별로 확장하여 구현하고 있다. 그래서 아키텍처별로 사용할 수 있는 시스템 콜이 조금씩 다를 수 있지만, 시스템 콜의 90% 이상은 모든 아키텍처에 구현되어 있다.
사용자 영역의 애플리케이션을 커널 영역으로 직접 연결하는 것은 불가능하다. 보안과 안정성의 이유로 애플리케이션은 커널 코드를 직접 실행하거나 커널 내부 데이터를 조작할 수 없다.
대신, 애플리케이션이 시스템 콜을 실행하려 한다는 '시그널'을 커널로 보낼 수 있다. 이 매커니즘을 통해야만 커널 내부로 진입하고, 커널이 허용한 코드를 실행한다. 물론 정확한 메커니즘은 아키텍쳐마다 다르다.
예를 들어, i386에서는 애플리케이션에서 소프트웨어 인터럽트 명령인 int에 0x80 값을 넘기면, 이 명령은 평소에는 보호된 커널 영역으로 들어가 소프트웨어 인터럽트 핸들러를 실행한다. 이때 값 int 0x80을 시스템 콜 핸들러 라고 한다.
애플리케이션은 실행할 시스템 콜과 매개 변수를 레지스터를 통해 전달한다. 시스템 콜은 0부터 시작하는 숫자로 나타내며, 시스템 콜을 호출하려면 레지스터에 해당 시스템 콜을 먼저 저장해야 한다. 예를 들어 i386 아키텍쳐에서 시스템 콜 5번(open())을 호출하려면, 응용프로그램은 int 명령을 실행하기 전에 eax 레지스터에 5를 저장해야한다.
매개변수 전달도 비슷한 방식으로 처리되는데 i386에서는, ebx, ecx, edx, esi, edi 레지스터에 순서대로 다섯 개의 매개 변수를 저장한다. 매개 변수가 다섯 개 이상 필요할 때는 레지스터 하나에 나머지 모든 매개 변수를 담은 사용자 영역의 버퍼를 가리키도록 한다. 물론 대부분의 시스템 콜에는 매개 변수가 두어 개만 있다.
아키텍처별로 시스템 콜을 처리하는 방식은 다르지만, 기본 원리는 같다. 보통 시스템 프로그래머가 커널이 시스템 콜을 처리하는 자세한 내용까지 알 필요는 없다. 이런 지식은 아키텍처의 표준 콜링 컨벤션(Standard Calling Convention, 호출 규약) 에 녹아 있으며, 컴파일러와 C 라이브러리에서 자동으로 처리된다.