32비트 인텔 CPU를 가정한다.
bios 는 다루지 않는다.
키보드, 디스크, 화면을 다룬다.
general purpose registers
EAX, EBX, ECX, EDX
stack registers
ESP, EBP
EBP 는 base pointer 인데, 현대의 컴파일러들은 EBP 를 거의 사용하지 않는다.
other registers
EIP, EFLAGS(bit vector of flags, stroes things like carry, ...)
메모리의 값을 바로 메모리에 넣을 수는 없고 반드시 레지스터를 거쳐야 한다.
운영체제에 따라 RAX, EAX, AX 로 나눌 수 있고 이전 버전과 호환되어야 한다. AX 는 AH + AL 로 나눌 수 있다.
jmp : 무조건 점프
jz : zero 면 점프
jnz : zero 가 아니면 점프
jne : 다르면 점프 (jump not equal)
jle : jump less than or equal? 작거나 같으면 점프인 듯
zxx 명령어는 대충 이런 식...
메모리는 Interrupt Vector Table, BIOS Code, Memory mapped devices 가 먼저 들어가고 남은 메모리에 운영체제 코드가 적재되어야 한다.
인텔 아키텍처에서 각 주소는 한 바이트를 가리킨다.
byte : 1byte
word : 2byte
dword (double word) : 4byte
byte ordering
big endian 과 little endian 방법이 있다.
리틀 엔디안은 인텔에서 사용하고 나머지는 빅 엔디안을 사용하는데 PC 는 리틀 엔디안이 표준, 서버나 네트워크는 빅 엔디안을 사용하고 있다. 그래서 네트워크로 패킷을 보낼 때에는 바꿔서 보내야 한다.
리틀 엔디안은 주소 체계가 거꾸로 되어 있음.
ab 12 00 00 -> 000012ab
디바이스와 운영체제가 통신하는 3 가지 방법
- I/O Port
- Memory mapped
- interrupt
I/O Port Example
I/O Port 는 0x3F8 이라 가정
<Output>
mov eax, 1
mov dx, 0x3F8
out dx, al <-- start
inc eax
cmp eax, 32
jne start
<Input>
mov ebx, 0
mov dx, 0x3F8
in dx, ax <-- start
mov [esp+ebx], ax
inc ebx
cmp ebx, 31
jle start
out dx, al : dx 포트에 al 값을 출력
in dx, ax : dx 포트에서 입력 받은 값을 ax 에 저장
Mamory Mapped Devices
CPU 와 I/O Device 가 램의 일정 영역을 공유한다.
frame : 한 화면
buffer : 화면에 직접 뿌리는 것이 아니라 버퍼에 입력하면 CPU 가 읽어서 그래픽 카드가 출력한다.
Keyboard Controller
2 가지 byte 를 갖는다.
keycode : 입력된 키 값
status : 상태 값 (key 누르면 1, CPU 에서 읽으면 0)
우리는 프레임 버퍼에 쓰기만 하고, 화면에 출력하는 건 비디오 카드가 한다.
<output>
fbuf = 0xF000
str : 'Hello World'
mov eax, str <-- begin
mov ebx, 11
mov ecx, 0xF000
mov edx, byte [eax] <-- loop
inc eax
mov [ecx], byte edx
inc ecx
sub ebx, 1
jnz_loop
jmp done <-- done
프레임 버퍼에 직접 접근하는 이 프로그램은 OS 가 아니다.
문자열, 프레임 버퍼를 가져와서 문자열 길이만큼 반복하여 출력하는 코드이다. 읽은 값은 edx 에 임시로 저장한 후 edx 에서 버퍼 프레임에 넣어준다.
입력 받을 때마다 화면에 출력하는 코드교수님이 프레임 버퍼에 출력한다고 하는 편이 더 전문적인 느낌이 날 것 같다고 말씀하셔따
<input>
framebuf = 0xF000
status = 0xF800
keycode = 0xF801
mov eax, 0xF000 <-- begin
mov ebx, byte [0xF800] <-- loop
cmp ebx, 0
jz loop
mov ebx, byte [0xF801]
mov [eax], byte ebx
inc eax
mov [0xF800], 0
jmp loop
프레임 버퍼를 가져온다.
status 값을 확인하고 1이면 키 값을 가져와 프레임 버퍼에 출력하고 0으로 만들어 준 뒤 돌아간다. 0이면 그냥 돌아간다.
함수가 매개변수를 저장하는 방법
add(1, 2) 라면 2, 1 순서로 스택에 넣는다. 스택에서 뺄 때는 역순이 되기 때문이다. 매개변수는 각각 eax, ebx, ... 에 넣던데 매개변수가 5개 이상이면 어떻게 처리하는 거지...?
반환값은 eax 에 저장한다.
입력한 값을 출력하는 OS 를 만들어 보자!!
이것은 API 형태로 제공할 것이다. getkey() 와 putchar() 를 만들어 보자!
mov edx, byte [status] <-- getkey
cmp edx, 0
jz getkey
mov [status], 0
mov eax, byte [keycode]
ret
키 입력이 들어왔으면 키 값을 반환하고, 안 들어왔다면 들어올 때까지 기다리는 코드이다.
<putchar>
putchar: mov eax, word [esp+4]
mov ebx, dword [bufptr]
mov [ebx], word eax
add ebx, 1
mov [bufptr], ebx
ret
bufptr 은 프레임 버퍼 포인터를 저장하고 있는 포인터 변수이다. bufptr 자체는 프레임 버퍼 포인터의 주소값이고 [bufptr]
은 프레임 버퍼의 주소이고 [ebx]
가 키 값인 것 같다.
putchar 의 매개변수(2바이트)를 하나 받아서 그 값을 버퍼프레임에 출력한다.
2바이트를 읽었으니 add ebx, 1 이 아니라 add ebx, 2 가 되어야 할 것 같은데 이 부분은 잘 모르겠다... C언어의 포인터 연산이랑 같은 개념인 걸까...?
그렇게 새로운 입력 코드는 다음과 같다.
call getkey <-- loop
push eax
call putchar
pop eax
jmp loop
push eax 는 매개변수이다.
왜 이런 까다로운 짓을 할까?
개발에 익숙하다면 오히려 함수화 하는 게 좋다는 것을 바로 알아챌 것이다. 개발에서 함수화 시키는 것과 같은 이점을 갖는다.
- Reusability
재사용성이 좋다. 다른 곳에서도 getkey, putchar 함수를 사용할 수 있다. - Abstraction
사용자는 프레임 버퍼, 출력에 대해 신경쓰지 않아도 된다. - Portability
OS 내부 코드가 바뀌어도 함수를 사용하는 방법은 그대로이기 때문에 코드를 사용자는 코드를 바꾸지 않아도 된다.
쉘을 만들고 싶어여!
왜 그런 걸 만들려고 하는 거야?
cmd 같은 것을 쉘이라 하는데, 한 라인씩 읽는다. 읽은 값이 명령어라면 운영체제에서 실행하면 되지만 파일이라면 프로그램을 찾아서 메모리에 로드 해 줘야 한다.
프로그램은 어디에 로드해야할까?
하드웨어, 운영체제, IVT, BIOS 가 사용하고 남은 메모리에 로드해야 한다.
Disk Controller
디스크는 블록 디바이스이다. 블록 단위로 읽어들인다. (512byte, 4kb)
블록 주소에 B, cmd/status 에 'W'를 쓰면 블록 B 에 작성하라는 뜻이고 'R'이면 블록 B 에서 읽으라는 뜻이다.
File System Structures
struct dirent {
bool valid;
char name[16];
int start;
int len;
}
start = 3 이고 len = 3 이라면 디스크에서 3, 4, 5 블럭을 읽는다.
우리가 만든 운영체제의 문제점은 무엇일까?
- 예외 처리가 안 되어있다.
- 주소값에 직접 접근하고 있다. (getkey, putchar)
운영체제 코드가 바뀌면 어플리케이션도 동작하지 않을 것이다. 이 문제를 해결하기 위해서는 인터럽트를 사용하고 가상 메모리를 사용해야 한다.
- 인천대학교 컴퓨터공학부 3학년 전공필수 과목인 박문주 교수님의 운영체제 강의를 듣고 작성한 글입니다.
'공부 > OS' 카테고리의 다른 글
[운영체제] 프로세스 스케줄링 1 (process scheduling) (0) | 2021.04.19 |
---|---|
[운영체제] 프로그램 로딩(program loading) (0) | 2021.04.19 |
[운영체제] 스레드(thread) (0) | 2021.04.19 |
[운영체제] 컨텍스트 스위칭은 언제 일어나야 하는가? (0) | 2021.04.18 |
[운영체제] 컨텍스트 스위칭(context switching) (0) | 2021.04.18 |