운영체제 개요

프로그램은 매우 단순한 일을 한다 : 명령어를 실행한다.

반입하고, 해석하고, 실행한다.

OS = 프로그램간의 메모리 공유를 가능케 하고, 장치와 상호작용을 가능케 하는 등의 일을 하는 소프트웨어

OS = 소프트웨어 = 자원관리자 = 자원을 가상화 한다.(cpu, 메모리, 디스크 등)

CPU 가상화 = 동시에 여러 프로그램을 실행 시키는 척 하는 것.

메모리 가상화 = 각 프로세스는 자신만의 가상주소공간을 갖는데, 이 공간을 물리 메모리로 매핑 하는 것.

운영체제의 설계와 구현에 중요한 목표는 성능이다. - 오버헤드를 최소화 하는 것

또 다른 목표는 응용 프로그램간의 보호(고립 원칙)이다.

그 외에는 에너지 효율성, 보안, 이동성이다.

 

 

프로세스의 개념

프로세스 : 실행 중인 프로그램 (프로그램은 디스크 상에 존재하며 실행을 위한 명령어와 정적 데이터 묶음이다.)

프로세스 생성 : 

1) 프로그램 코드와 정적 테이터를 메모리, 프로세스 주소 공간에 load 하기 

2) 특정 크기의 메모리 공간이 프로그램에 스택, 힙 용도로 할당되어야 한다.

3) 입출력 셋업과 관계된 초기화 작업 수행한다.

 

 

프로세스 상태

- 실행 (Running) : 프로세스는 명령어를 실행하고 있다.

- 준비 (Ready) : 실행할 준비가 되었지만, 다른 프로세스를 실행하고 있는 등의 이유로 대기 중이다.

- 대기 (Blocked) : 다른 사건을 기다리는 동안 프로세스 수행을 중단시키는 연산이다.

 

fork() , wait(), exec() 

fork() : 생성된 프로세스가 호출한 프로세스의 복사본이다.

자식 프로세스는 자신의 주소 공간, 자신의 레지스터, 자신의 PC값을 갖는다.  fork()로 부터 부모 프로세스는 생성된 자식 

프로세스의 PID를 반환받고, 자식 프로세스는  0을 반환받는다.

wait() : 이 시스템 콜을 부모 프로세스에 호출하지 않으면, 비결정성으로 인해 문제가 발생할 수 있다.

자식 프로세스가 종료한 후, 부모 프로세스가 종료될 수 있게 한다.

exec() : 다른 프로그램을 실행해야 할 때 사용한다.

 

쉘의 경우 , 사용자가 무언가를 입력하기를 기다린다. 대부분의 경우 쉘은 fork() 를 호출하여 새로운 자식 프로세스를 만든다. 

그런 후 exec()의 변형 중 하나를 호출하여 프로그램을 실행시킨 후 wait() 를 호출하여 명령어가 끝나기를 기다린다.

자식 프로세스가 종료되면 쉘은 wait() 로부터 리턴하고 다시 프롬프트를 출력하고 다음 명령어를 기다린다.

 

제한적 직접 실행 방식(제한 없음) : trap handler,  커널 모드 ~ 사용자 모드 까지 

커널은 부팅 시에 트랩 테이블을 만들고 이를 이용하여 시스템을 통제한다.

하드 웨어는 syscall 핸들러의 주소를 기억한다.

* 모든 시스템 콜은 자신의 고유 번호를 갖는다.

* 각 시스템 콜의 코드 위치는 운영체제만 알고 있다.

 

운영체제 : 실행 (커널 모드 ) 하드웨어 프로그램 (사용자 모드)
운영체제가 프로그램을 실행 했을 때,
1) 프로세스 목록에 항목을 추가한다.
2) 프로그램을 위한 메모리를 할당한다.
3) 프로그램을 메모리에 탑제한다.
4) argv를 사용자 스택에 저장한다.
return-from-trap
   
  커널 스택으로부터  레지스터를 복원한다.
사용자 모드로 이동한다.
main으로 분기한다.
 
    main() 을 실행한다. 
시스템 콜을 호출한다.
운영체제로 trap 한다.
  레지스터를 커널 스택에 저장한다.
커널 모드로 이동한다.
트랩 핸들러로 분기한다.
 
trap을 처리한다.
syscall의 임무를 수행한다.
return-from-trap
   
  커널 스택으로부터  레지스터를 복원한다.
사용자 모드로 이동한다.
트랩 이후의 PC로 분기한다.
 
    main에서 리턴한다.
trap(exit () 를 통하여)
프로세스의 메모리를 반환한다.
프로세스 목록에서 제거한다.
   

 

* trap 과 Interrupt 의 차이점 

trap 은 사용자 모드에서의 exception (예외) 이다.

0으로 나누거나 잘못된 메모리 액세스로 인해 발생한다.

커널 루틴(시스템 콜)을 호출하는 일반적인 방법이기도 하다. 사용자 코드보다 더 높은 우선순위로 실행되기 때문이다.  

처리는 동기식(따라서 사용자 코드가 일시 중단되고 이후에 계속됨). 

어떤 의미에서 그것들은 "활성"이다.. 대부분의 경우 코드는 트랩이 발생할 것으로 예상하고 이 사실에 의존한다.

 

인터럽트는 하드웨어 (하드 디스크와 같은 장치, 그래픽 카드, I / O 포트 등)에 의해 생성 된 것이다.

인터럽트 처리기가 결국 발생할 때까지 기다려야 하기 때문에

비동기식(즉, 사용자 코드의 예측 가능한 위치에서 발생하지 않음) 또는 "수동적"이다.

 

⚠ 문제점 

직접 실행의 문제점은 프로세스 전환이 가능해야 한다는 것이다.

프로세스가 실행 중이라는 것은 운영체제는 실행 중이지 않다는 것을 의미한다.

운영체제가 실행하고 있지 않다면 어떻게 프로세스를 전환할 수 있을까? 할 수 없다.

그럼, OS는 어떻게 CPU를 다시 획득하여 프로세스를 전환 할 수 있는가?

 

- 협조 방식 : 시스템 콜 호출시 까지 대기

    이 방식은 각 사용자 프로세스가 비정상적인 행동은 하지 않을 것으로 가정한다.

    즉, CPU를 장기간 사용해야하는 프로세스들은 주기적으로 CPU를 반납할 것이라 믿는다..

    이 시스템은 근본적으로 수동적이다. OS는 제어권을 얻기위해 시스템 콜을 기다리거나, 불법적인 연산을 대기해야 한다.

 

- 비협조 방식 : 운영체제가 제어권 확보 

    타이머 인터럽트를 이용하는 것이다.

    타이머를 인터럽트 신호를 발생시킨다. OS는 수행 중인 프로세스를 중단시키고 인터럽트 핸들러를 실행한다.

    이 과정에서, 제어권이 자연스럽게 운영체제로 넘어가게 된다.

 

제한적 직접 실행 방식(타이머 인터럽트)

문맥의 저장과 복원

운영체제가 제어권을 획득하면, 중요한 결정을 내려야 한다. 

즉, 현재 실행 중인 프로세스를 계속 실행할 것인지, 다른 프로세스로 전환할 것인지를 결정해야 한다.

이 결정은 스케줄러(scheduler) 라는 부분에 의해 내려진다.

 

다른 프로세스를 실행하기로 결정하면, 문맥교환(context swith)라 불리는 코드를 실행한다.

* 문맥교환은 현재 레지스터 값들을 스택에 저장

* 인터럽트는 레지스터 값, 복귀 주소등을 PCB에 저장

 

운영체제 : 부트 (커널 모드) 하드웨어  
트랩 테이블을 초기화 한다    
  syscall 핸들러의 주소를 기억한다.
타이머 핸들러의 주소를 기억한다.

 
인터럽트 타이머를 시작시킨다.    
  타이머를 시작시킨다.
X mesc 지난 후 CPU를 인터럽트 한다
 
운영체제 : 실행 (커널 모드) 하드웨어 프로그램 (사용자 모드)
    프로세스 A 
  타이머 인터럽트
A의 레지스터를 A의 커널 스택에 저장
커널 모드로 이동
트랩 핸들러로 분기
 
트랩을 처리한다.
switch() 루틴 호출
A 의 레지스터를 Proc 구조에 저장
B 의 Proc 구조에서 레지스터를 복원
B의 커널 스택으로 전환
return-from-trap
   
  B의 커널 스택을 B 레지스터로 저장
사용자 모드로 이동
B 의 PC로 분기
 
    프로세스 B

 

* 타이머 인터럽트가 일어 났을 때, 사용자 레지스터가  하드웨어에 의해 스택에 저장

* 프로세스 전환 결정 되었을 때, 커널 레지스터운영체제에 의하여 PCB에 저장

 

 

 

 

 

 

 

 

 

 

 

 

 

EOD

 

블로그 이미지

hjc_

୧( “̮ )୨

,