** 한빛미디어의 '리눅스 커널의 이해'를 읽고 내용을 정리하는 글입니다. **


- 파일 디스크립터와 아이노드

 유닉스에서 장치 파일과 특수 파일을 제외하면 각 파일은 일련의 문자로 이루어진다. 파일에는 파일의 길이나 파일의 끝(EOF)을 나타내는 구분자와 같은 제어 정보는 들어있지 않다.

 파일 시스템이 파일을 다루는 데 필요한 정보는 모두 '아이노드(Inode)'라는 자료구조에 들어있으며, 각 파일은 각자 자신만의 아이노드를 가진다. 파일 시스템은 유닉스 시스템마다 크게 차이가 있을 수 있지만 적어도 POSIX 표준에서 규정하는 다음과 같은 속성을 항상 제공해야 한다.

- 파일 유형

- 파일과 연결된 하드 링크의 수

- 파일 길이 (바이트 단위)

- 장치 ID

- 아이노드 번호

- 파일 소유자의 UID

- 파일 소유 그룹의 GID

- 마지막 접근 및 수정 시간 등을 나타내는 여러가지 시간 기록

- 접근 권한과 파일 모드


- 접근 권한과 파일 모드

 0x01의 '사용자와 그룹' 절에서 정리했다시피 파일을 사용하는 사용자는 '파일 소유자', '파일 소유 그룹', '그 외의 사용자'의 세 부류가 있다. 이 세 부류 각각에 읽기, 쓰기, 실행이라는 세 가지 접근 권한 유형을 지정할 수 있으므로 파일과 관련된 접근 권한은 서로 다른 이진 플래그 9개로 이루어진다. 이밖에 suid(Set User ID), sgid(Set Group ID), sticky라는 세가지 추가 플래그가 파일 모드를 정의한다. 어떤 파일을 실행하는 프로세스는 보통 프로세스 소유자의 UID와 GID를 가진다. 하지만 suid나 sgid 플래그가 설정된 파일을 실행하면 프로세스는 각각 파일 소유자의 UID나 GID를 갖게 된다. sticky 플래그의 경우 실행이 종료된 후에도 프로그램을 메모리에 유지하도록 커널에 요청하는 플래그이나 더 이상 이는 쓰이지 않는다.


- 프로세스와 커널 모델

 0x01의 '프로세스' 절에서 프로세스는 User mode나 Kernel mode 둘 중 하나에서 동작한다고 정리하였다. User mode에서 동작 중일 때 프로세스는 커널 자료 구조나 커널 프로그램에 직접 접근할 수 없다. 그러나 커널 모드에서 동작 중일 때는 이러한 제한이 적용되지 않는다.

 커널은 '프로세스'의 개념이 아니라 '프로세스 매니저'의 개념으로 프로세스를 생성하고 제거하고 동기화하는 작업을 한다. '프로세스/커널' 모델에서는 프로세스가 커널 서비스를 필요로 할 때 '시스템 콜(System Call)'이라는 구조를 활용하도록 한다. 각 시스템 콜은 매개 변수 그룹을 설정한 후, 사용자 모드에서 커널 모드로 전환하는 하드웨어 고유의 CPU 명령어를 실행한다.


 유닉스 커널은 시스템 콜을 다루는 작업 외에도 많은 일을 수행하는데, 커널 루틴이 호출되는 경우는 다음과 같다.

- 프로세스가 시스템 콜을 호출한 경우

- 프로세스를 실행하던 CPU가 '예외(Exception)'을 발생시킨 경우 (커널이 대신하여 예외를 처리한다)

- 주변 장치가 특정 사건을 알려주기 위해 CPU에 '인터럽트 시그널(Interrupt Signal)'을 보내는 경우 (인터럽트 핸들러라는 커널 프로그램이 처리한다)

- 커널 스레드를 실행한 경우



- 프로세스 구현

 커널은 프로세스를 다루기 위해 각 프로세스를 '프로세스 디스크립터(Process Descriptor'로 나타낸다. 커널은 프로세스의 실행을 중단할 때 다음과 같은 여러 프로세서 레지스터의 현재 내용을 프로세스 디스크립터에 저장한다.

- 프로그램 카운터(PC)와 스택 포인터(SP) 레지스터

- 범용 레지스터

- 부동 소수점 레지스터

- CPU 상태 정보를 담고 있는 레지스터 (Processor Status Word)

- 메모리 관리 레지스터

 커널이 어떤 프로세스를 다시 실행하기로 결정하면 적절한 프로세스 디스크립터 필드를 CPU 레지스터로 읽어들인다.

 (이렇게 프로세스 디스크립터에 쓰고 읽는 과정이 곧 Context Switching이다)


- 재진입 가능한 커널

 모든 유닉스 커널은 '재진입(Reentrant)'이 가능하다. 이것은 여러 프로세스를 동시에 커널 모드에서 실행할 수 있다는 의미이다. CPU가 커널 제어 경로를 실행하고 있을 때 인터럽트가 발생할 경우는 첫 번째 커널 제어가 종료되지 않은 상태에서 CPU는 인터럽트를 처리하기 위해 또 다른 커널 제어 경로를 시작한다. 이 커널 제어가 완료된 후 첫번째 커널 제어 경로를 처리하며 이때 두 커널 제어 경로는 같은 프로세스의 실행 컨텍스트에서 실행되어 이 과정에서 소요된 시스템 시간은 프로세스가 사용한 것으로 계산된다.



- 동기화와 임계 영역

 재진입 가능한 커널을 구현하려면 동기화를 이용해야 한다. 커널 제어 경로가 어떤 커널의 자료 구조를 가지고 작업하던 도중에 멈춘 경우, 이 자료 구조가 올바른 상태로 다시 설정되지 않는 한 다른 커널 제어 경로가 동일한 자료 구조를 사용할 수 없도록 해야 한다. 둘 이상의 프로세스를 어떻게 스케줄 하느냐에 따라 계산 결과가 달라질 수 있다면 해당 코드는 잘못된 것이며 이를 '경쟁 조건(Race Condition)'이 있다고 한다.

 모든 작업이 '원자적인 연산(Atomic Operation)'을 이용한다면 전역 변수에 안전하게 접근할 수 있다. 하지만 커널에는 이런 단일 연산으로 다룰 수 없는 많은 자료 구조가 있다. 예를 들어, 연결 리스트의 항목을 제거하는 경우 적어도 두 개의 포인터에 동시에 접근해야 하므로 단일 연산으로는 이를 수행할 수 없다. 어떤 코드 영역이든 일단 시작했다면 다른 프로세스가 해당 영역에 진입하기 전에 끝내야만 하는 영역을 '임계 영역(Critical Section)'이라고 한다.

 이런 문제를 해결하기 위해 몇 가지 동기화 기법을 채택하였는데, 이들은 각각 다음과 같다.

- 커널 선점 기능 제거

고전 유닉스는 커널을 비선점형으로 만드는 것으로 동기화 문제를 해결하였다. 비선점형 커널에서는 프로세스가 커널 모드에서 실행 중이면 이를 임의로 보류하거나 다른 프로세스로 교체할 수 없다. 하지만 멀티프로세서 시스템에서는 서로 다른 CPU에서 실행되는 두 커널 제어 경로가 동시에 같은 자료구조에 접근할 수도 있으므로 멀티프로세서 시스템에서 비선점성은 적합하지 못하다.

- 인터럽트 금지

유니프로세서 시스템을 위한 동기화 매커니즘으로 임계 영역에 들어가기 직전에 모든 하드웨어 인터럽트를 금지하고 임계 영역을 빠져나오는 즉시 다시 허용하는 것이다. 매우 간단한 매커니즘이지만 임계 영역이 크면 긴 시간동안 하드웨어의 동작을 멈추게 할 수도 있다는 문제가 있다.

- 세마포어

세마포어는 어떤 자료구조와 연계된 간단한 카운터로 모든 커널 스레드는 자료 구조에 접근하기 전에 해당 세마포어를 검사한다. 세마포어는 정수 변수와 대기 중인 프로세스 목록, 원자적으로 작동하는 up()과 down() 두 개의 메서드를 갖는다. 각 자료 변수는 모두 자신만의 세마포어를 가지며, 정수 변수는 초기값 1을 가진다. 커널 제어 경로가 자료구조에 접근하려고 할 때 먼저 해당 세마포어의 down()메서드를 실행한다. 이후 세마포어 정수 변수의 값이 음수가 아니면 해당 자료구조에 접근할 수 있고, 음수이면 커널 제어 경로를 실행 중이던 프로세스를 대기 프로세스 목록에 추가하고 대기한다. 먼저 자료구조에 접근해 사용 중이던 프로세스가 작업을 마치며 up() 메서드를 실행하면 대기 중인 프로세스중 하나는 작업을 계속할 수 있다.

- 스핀 락

스핀 락은 세마포어와 매우 유사하지만 프로세스 리스트가 없다는 점이 다르다. 프로세스가 사용하려는 자료구조를 이미 다른 프로세스가 사용하고 있다면 프로세스는 락이 풀릴 때까지 반복해서 명령어를 실행하며 회전(spin)한다.

블로그 이미지

__미니__

E-mail : skyclad0x7b7@gmail.com 나와 계약해서 슈퍼 하-카가 되어 주지 않을래?

댓글을 달아 주세요