커널 모델과 프로세스
CPU는 사용자 모드와 커널 모드 중 하나에서 동작한다. 비로 두 개 이상의 모드를 지원하는 CPU도 존재하지만, 표준 유닉스 커널은 커널 모드와 사용자 모드만 사용한다. CPU는 사용자 모드와 커널 모드 간 전환하는 명령어를 제공하고, 프로그램은 대부분의 시간을 사용자 모드에서 작동한다.
커널이란 프로세스의 관리자이다. 커널의 여러 루틴은 프로세스를 생성하고 제거하고 동기화한다. 프로세스가 시스템 콜을 통해 커널 서비스를 이용할 수 있도록 한다.
유닉스 시스템에서는 커널 스레드라 하는 몇 가지의 특권 프로세스가 있다. 이들은 커널 모드에서 실행되고, 사용자와 상호 작용하지 않으며, 시스템이 작동하는 동안 쭉 살아 있다.
커널 루틴이 호출되는 경우는 여러가지가 존재한다. 프로세스가 시스템 콜을 호출하거나, 프로세스를 실행하던 CPU가 예외를 발생시키거나, 주변 장치에서 인터럽트 시그널을 보내거나, 커널 스레드가 실행될 때이다.
프로세스
커널은 각 프로세스를 프로세스 디스크립터로 나타낸다. 프로세스의 실행을 중단할 때 현재 내용을 기억하기 위해 프로세스 디스크립터에, 프로그램 카운터와 스택 포인터와 각종 레지스터를 저장한다.
커널이 어떤 프로세스를 다시 실행할 때에는 프로세스 디스크립터 필드에서 CPU 레지스터를 로드한다. 그리고 저장된 프로그램 카운터로부터 다시 실행을 재개한다. 프로그램 카운터는 마지막에 실행한 명령어의 다음 명령어를 가리킨다.
각 프로세스는 자신의 개인 주소 공간에서 실행된다. 사용자 모드에서 실행되는 프로세스는 자신만의 스택과 데이터, 코드 영역을 사용한다. 이 주소 공간의 일부를 프로세스 사이에서 공유하는 경우도 있다. 파이프와 같이 프로세스가 주소 공간을 공유할 것을 요구하기도 하고, 커널이 메모리 절약을 위해 자동으로 공유하기도 한다.
커널은 재진입이 가능하다. 이 말은, 커널이 다중 프로세서 환경이나 인터럽트를 처리하는 환경에서 안전하게 동시에 처리될 수 있음을 뜻한다. 다시 말해, 커널 코드가 어느 한 프로세스에서 실행되고 있을 때 다른 프로세스나 인터럽트가 해당 커널 코드를 동시에 실행하더라도 문제 없이 작동된다.
커널은 재진입이 가능하므로 여러 커널 제어 경로가 차례로 실행될 수 있다. 이 경우 각 커널 제어 경로는 해당 프로세스의 커널 스택을 사용하게 된다.
동기화와 임계 영역
커널이 재진입 가능하려면 동기화를 이용해야 한다. 하나의 자원에 여러 프로세스가 동시에 접근하려 할 때, 아토믹하지 않으면 여러 문제가 발생할 수 있다. 예를 들어, a = b + c를 할 때 아직 b + c값을 a에 값을 저장하지 않은 상태에서 다른 프로세스가 a에 접근하려 하면 문제가 발생한다.
이렇듯 어떤 자원에 두 개 이상의 프로세스가 접근하려 하는 것을 경쟁 조건(race condition)이라 한다. 또, 다른 프로세스가 그 영역에 진입하기 전에 각 프로세스를 끝내야 하는 코드 영역을 임계 영역(critical region)이라 한다.
이를 해결하기 위해 몇 가지 동기화 기법을 채택할 수 있다.
비선점형 커널
멀티프로세서 시스템에서는 비효율적인 방법이다. 고전 유닉스 커널은 동기화 문제 해결을 위해 비선점형으로 만들어졌는데, 프로세스가 커널 모드에서 실행 중이라면 이를 다른 프로세스로 교체할 수 없다. 또 커널 모드에 있는 프로세스가 CPU를 반납할 때에는 모든 자료 구조가 온전한 상태에 있다는 것을 보장해야 한다. 멀티프로세서 시스템에서는 서로 다른 CPU가 같은 자료 구조에 접근할 수 있으므로 비효율적이다.
인터럽트 금지
이는 멀티프로세서 시스템에서는 불가능한 방법이다. 임계 영역에 들어가기 직전에 모든 하드웨어 인터럽트를 금지시키고 임계 영역을 빠져나오면 다시 허용한다. 만약 임계 영역이 크다면 모든 하드웨어의 동작이 멈춰버릴 수 있는 위험이 있다.
세마포어
가장 많이 쓰이는 방법이다. 각 세마포어는 정수 변수와 기다리고 있는 프로세스 목록, down과 up이라는 두 개의 원자적인 메소드가 존재한다. down 메소드는 세마포어 값을 감소시키고 결과값이 0 미만이라면 프로세스를 세마포어 목록에 추가하고 스케줄러를 호출해 블록시킨다. up은 세마포어 값을 증가시키고 이 결과값이 0 이상이라면 세마포어 목록에 있는 하나 이상의 프로세스를 활성화시킨다.
보호를 받는 각 자료 구조는 자신만의 세마포어를 가지고, 초기값은 1을 가진다. 커널 제어 경로에서 자료 구조에 접근할 때 먼저 해당 세마포어에 down 메소드를 실행한다. 새로운 세마포어의 값이 음수가 아니라면 그 자료구조에 접근할 수 있고, 그렇지 않다면 대기한다.
스핀락
만약 대기 시간이 짧은 경우, 즉 임계 영역이 작은 경우에는 세마포어보다 스핀락이 더 효율적인 방법이다. 이는 프로세스 목록이 없는 락으로, 프로세스가 사용하려는 락을 다른 프로세스가 사용하려고 잠그면 프로세스는 락이 풀릴 때까지 반복문을 수행하며 멈춘다. 즉, spin한다.
프로세스나 커널 제어 경로를 동기화할 때, 프로세스가 완전히 정지되어 버리는 데드락에 빠져버릴 수도 있다. 보통 두 개 이상의 락이 순환적으로 작동하여 발생한다.
커널 디자인과 관련하여, 커널 세마포어 유형의 수가 많을 때 데드락의 위험성이 생긴다. 리눅스를 포함한 여러 운영체제는 이 문제를 제한된 수의 세마포어를 사용하고 세마포어에 대한 요청을 오름차순으로 정렬하여 피하고 있다.
'현생 > 리눅스 커널' 카테고리의 다른 글
유닉스 커널 개요 - 3. 메모리 관리 (0) | 2024.08.23 |
---|---|
유닉스 커널 개요 - 2. 시그널, 프로세스 간 통신, 프로세스 관리 (0) | 2024.08.22 |
파일 관련 시스템 콜 (0) | 2024.08.20 |
유닉스 파일 시스템 (0) | 2024.08.19 |
운영체제 기초 개념 (0) | 2024.08.18 |