운영체제

컴퓨터 구조와 어셈블리 언어

mmalmmizal 2023. 9. 6. 10:40

컴퓨터 시스템의 하드웨어 구성

  • DMAC (Direct Memory Access Controller)

디스크 제어기의 DMAC

0x80000 읽기(1) 쓰기 (2)

0x80001 디스크 블록 번호

0x80002 메모리 주소

0x80003 바이트 수 기록

 

 

프로세스 P2 :

디스크의 200번 블록에서 1024 바이트를 읽어서 메모리의 0x5100 번지부터 채우라는 파일 요청

운영체제 :

mov $200, 0x80001

디스크 블록 번호 레지스터에 200 기록

mov $0x5100, 0x80002

메모리 주소 레지스터에 0x5100 기록

mov $1024, 0x80003

크기 레지스터에 1024를 기록

mov $1, 0x80000

읽기를 표시하는 값 기록

 

프로세서의 프로그래밍 모델

  • 8086 프로세서

마이크로프로세서 중의 하나

 

 

내부에는 범용 레지스터로 AX,BX,CX,DX,SI,DI 및 BP가 있고, 스택영역 관련 명령어를 실행할 때 사용하는 SP (Stack PointeR), 다음 명령어를읽어올 주소를 가지고 있는 IP(Instruction Pointer), ALU(Arithmetic and Logic Unit)를 통한 각종 연산 결과에 따른 프로세서 상태를 기록하는 FLAGS 레지스터 등이 존재.

외에도 CS, DS, SS등의 세그먼트 레지스터가 존재.

 

어셈블리 언어의 기초

표준이 없으므로 어셈블러마다 문법에 차이가 있음

  • 기본적인 표현 형식

명령어(command), 지시어 (directive), 레이블(label) 또는 설명문(comment)

 

지시어는 어셈블러에게 정보를 전달하는 목적으로 첫 칸을 . 으로 시작

레이블은 적절한 문자열 다음에 : 가 따라오는데, 변수의 주소 표시 or 분기 명령어가 나올 때 분기 주소를 표시

명령어는 프로세서가 실행해야 할 명령어를 표시하는데 첫 글자를 빈칸으로 시작하거나 레이블 다음에 따라올 수 있음

 

 

  • (gcc 컴파일러 패키지에 포함된) GNU 어셈블러의 어셈블리 언어 문법
종류 의미  
레이블 label: 표시된 위치의 주소  
명령어 addl %eax, %ebx 프로세서가 실행할 내용 EAX 레지스터의 값을 EBX 레지스터에 더해라
설명문 #comments 읽는 사람에게 정보 전달  
지시어  .global label 표시된 'label'주소를 다른 파일에서도 참조 가능  
지시어 .extern label 표시된 'label'은 다른 파일에서도 정의된 주소임.  

 

 

  • 어드레싱 모드

오퍼란드(operand, 명령어의 실행 대상)를 지정하는 여러 가지 표시 방식

 

(1) 직접주소 모드 (direct addressing mode)

(2) 레지스터 모드 (register mode)

(3) 간접주소 모드 (indirct addressing mode)

(4) 즉치모드 (immediate value mode)

 

즉치 $20 20 그 자체
레지스터 %eax eax 레지스터의 내용
직접주소 label label 주소의 메모리 값
  0x1234 0x1234 번지의 메모리 값
간접주소 (%esp) esp 레지스터의 값이 지정하는 주소의 메모리 내용
  8(%esp) esp 레지스터의 값(?)에 8을 더한 주소의 메모리 내용

 

 

 

  • 대표적인 명령어add는 16비트 단위 덧셈 연산

addl은 32비트 단위 덧셈 연산

l이 붙은 명령어는 ‘long’의 의미로 32비트 처리.

 

레지스터 이름에서도 기본적인 이름은 16비트 크기

‘e’가 덧붙은 레지스터는 extended의 의미로 32비트 크기 의미

jge : jumpt if greater than or equal) 크거나 같으면 ~ 주소로

 

 

  • 산술연산 명령어negl : 부호를 반대로 변경

addl, subl, cmpl 및 negl 등의 명령어는 실행 과정에서 발생하는 캐리, 부호, 오버플로우 등의 플래그 비트들을 FLAGS 레지스터에 저장함.

  • 스택 처리 명령어

기본적으로 높은 주소가 바닥, 낮은 주소가 꼭대기 쪽을 의미함.

  • 함수호출 명령어

ret : 함수로부터의 복귀 명령어

 

 

 

 

커널 모드와 유저 모드

CPU는 실행되는 내용이 운영체제 부분인지 일반 프로세스 부분인지에 따라 실행 권한이 달라질 필요가 있다.

  • 커널모드(kernel mode) : 운영체제 부분이 실행되고 있을 때 CPU 상태

system mode, supervisor mode, privilleged mode

  • 유저모드(user mode) : 프로세스 부분이 실행되고 있을 때 CPU 상태

CPU는 커널 모드와 유저 모드를 표시하는 특수 레지스터를 가짐.

펜티엄 프로세서의 Descriptor 레지스터의 PL(privilege Level) 2비트가 이 용도로 사용됨.

4단계(높0~3낮)로 구분된 모드, 낮은 수준의 모드에서는 높은 수준의 모드를 위한 부분은 사용하지 못함.

운영체제의 커널이 실행될 때는 0

프로세스가 실행될 때는 3으로 설정.

리눅스는 1, 2 수준의 값은 사용하지 않음.

CPU에서는 커널 모드에서만 실행 가능한 명령어 (privileged instruction) 집합을 별도로 가지고 있음.

상태 레지스터의 모드 부분 변경이나 인터럽트 금지 및 해제 등을 위한 명령어가 이 범주에 속함.

만약 유저 모드에서 이런 특권 명령을 실행하면 실행 모드 위반의 인터럽트가 발생하고, 운영체제는 그 처리 루틴에서 실행되던 프로세스를 강제종료 시킴.

 

 

 

모드 전환과 시스템 콜 함수

  • 모드 전환

 

모드를 표시하는 비트들을 유저 모드를 의미하는 값으로 변경

 

유저모드 -/-> 커널모드

유저모드에서는 비트들을 임의로 변경할 수 없음.

 

*상태 레지스터의 모드 부분을 변경하는 명령어는 커널 모드에서만 실행이 가능한 특권 명령으로 프로세서가 설계됨.

 

 

 

  • 커널 모드 전환

프로세서들은 인터럽트가 발생하면 커널 모드로 전환되고 커널 부분에 미리 등록된 인터럽트 처리 루틴 (ISR : Interrupt Service Routine)을 실행하도록 설계됨.

유저 모드에서 프로세스가 실행되다가 커널 부분을 실행해야 할 경우 강제로 인터럽트 발생시키는 명령어 (펜티엄의 경우 int)를 실행함으로써 커널 모드로 전환.

 

  • 소프트웨어 인터럽트 : 특정 명령어에 의해 의도적으로 발생시키는 인터럽트

 

  • 모드 복귀

인터럽트 발생 시 당시의 모드 값을 스택, 특정 레지스터에 저장해 두었다가 인터럽트로부터 복귀하는 명령어 (펜티엄의 경우 iret)

 

  • 시스템 콜 함수

운영체제의 특정 기능의 실행을 요청하는 함수

일반 함수는 호출될 때 CPU의 모드 변경 없이 해당 함수로 이동하여 실행하고 원래 위치로 돌아와 실행을 계속함. 그러나 시스템 콜 함수의 경우에는 CPU실행 모드를 커널 모드로 변경해야 하므로 소프트웨어 인터럽트를 이용하여 인터럽트 처리 루틴에서 지정된 함수를 처리하고, 원래 위치로 돌아오도록 함.

리눅스 write 시스템 콜 함수에서 소프트웨어 인터럽트 사용 예)

 

 

.globl write 
.extern errno

#함수 호출 : write(int fd, char * buffer, int size)

write: #write 함수의 시작 주소 
		pushl %ebx                          #스택에 ebx 저장 
		movl 8(%esp), %ebx                  #fd값을 ebx로 읽어 들임
		movl 12(%esp), %ecx                 #buffer 값을 ecx로 읽어 들임
		movl 16(%esp), %edx                 #size 값을 edx로 읽어 들임
		movl $4, %eax                       #4라는 값 자체를 eax에 저장 (write를 지정하는 번호)
		int $0x80                           #시스템 콜을 위한 인터럽트 0x80번 발생
		cmpl $0, %eax                       #eax 반환값을 0이라는 값 자체와 비교 
		jge .L1                             #음수(에러)가 아니면, .L1로 이동 
	  negl %eax                           #음수이면 양수로 부호 변경(오류 번호)
		movl %eax, errno                    #errno에 오류 번호 기록
		movl $-1, %eax                      #반환 값을 -1로 설정 

.L1 : popl %ebx                         #스택에 저장해 둔 ebx 값 복구
			ret                               #write를 호출한 위치로 복귀

 

ebx값 들어오기 전 스택

esp 반환 주소
esp+4 fd
esp+8 buffer
esp+12 size

 

pushl %ebx 실행 후 (스택 증가)

esp ebx 값
esp+4 반환 주소
esp+8 fd
esp+12 buffer
esp+16 size

인터럽트 발생과 커널의 실행

  1. CPU는 유저 모드에서 프로세스 A의 코드가 저장된 메모리 부분에서 명령어를 읽어 오고 실행함
  2. 하드웨어 인터럽트가 발생하거나 프로세스 A 자신이 시스템 콜 함수를 호출하며 소프트웨어 인터럽트가 발생하면
  3. 커널 모드로 전환되면서 대응되는 인터럽트 처리 루틴(ISR)을 실행. 정상적으로 ISR이 완료되면
  4. ISR의 끝부분에 있는 iret 명령어를 실행하면서 인터럽트가 발생할 당시의 모드 및 실행 위치로 되돌아가서
  5. 프로세스 A가 계속 실행됨.