ARP(주소 변환 프로토콜)은 DNS에서 논리주소를 물리주소로 변환하기 위해 사용됩니다.
변환단계는 크게 Broadcast frame으로 MAC주소로 호스트를 찾는 단계와, 해당 호스트가 Unicast frame으로 논리적인 주소인 IP를 반환시켜주는 단계로 나눌수 있습니다.
(예 : 호스트A로 호스트B의 IP를 확인하기 위한 과정)
[1단계]
[2단계]
: 해당 네트워크를 구성하는 하드웨어을 명시
: 해당 패킷이 ARP에서 어떤 역활을 하는 패킷인지를 나타냅니다.
80x86 프로세서에서는 세 종류의 메모리 주소가 있습니다.
세그먼트 구조에서 Segment Selectro + 오프셋 형식으로 나타냅니다.
기계어 명령어에서 피연산자나 명령어 주소를 지정할 때 사용되는 주소로 디버깅할때나 어셈블리에서 사용되는 주소가 이 주소입니다.
[논리 주소 예]
보통 16진수로 표기하며 0x00000000 ~ 0xffffffff 까지 지정할 수 있습니다.
[선형주소 예]
메모리의 실제주소입니다.
메모리 모델에 대해서 좀 더 자세한 설명 http://www.hanb.co.kr/network/view.html?bi_id=1321
위에 그림을 보시면 먼저 세그먼트로 나누어져 있는 메모리에 세그먼트 식별자와 오프셋값을 이용하여 찾아가면 해당 데이터의 선형 주소가 있고 그 선형 주소를 이용하여 페이징 되어 있는 메모리를 찾아가면 물리 주소가 있습니다.
세그먼테이션은 각 프로세스에 다른 선형 주소 공간을 할당하는 메모리 관리 기법입니다.
리눅스에서는 메모리 관리를 좀 더 간단하고 다른 대중적인 아키텍처와 호환하기 위해서 세그먼테이션을 매우 제한적으로 지원합니다.
세그먼트 디스크립터는 세그먼트 특징을 기술하는 8바이트 크기의 데이터입니다.
세그먼트 디스크립터는 GDT(Global Descriptor Table)이나 LDT(Local Descriptor Talbe)에 저장 되는데, GDT는 보통 하나만 정의하지만 LDT는 필요한 만큼 정의해서 사용할수 있습니다. GDT가 위치한 주소는 gdtr 프로세서 레지스터에, LDT는 lgtr 프로세서 레지스터에 들어가 있습니다.
이러한 세그먼테이션 디스크립터를 이용하여 세그먼테이션 유닛이 논리 주소를 선형 주소로 변환합니다. 세그먼테이션 유닛은 세그먼테이션를 처리하기 위한 하드웨어 적인 회로로써 다음과 같은 작업을 수행합니다.
또한 세그먼트 디스크립터로의 빠른 접근하기 위해서 80x86프로세서는 논리 주소를 선형 주소로 빠르게 변형하기 위하여 프로그래밍이 가능한 여섯개의 세그먼테이션 레지스터에 각각 프로그램이 불가능한 레지스터를 추가로 제공합니다.
세그먼트 셀렉터를 세그먼테이션 레지스터로 로딩할때마다 이 프로그래밍이 불가능한 레지스터에 세그먼트 디스크립터를 저장하여 프로세서가 GDT나 LDT에 접근하지 않고 바로 세그먼트에 접근 할 수 있도록 해줍니다.
페이징은 선형주소를 ‘페이지’라는 고정된 크기로 나누는 메모리 관리 기법입니다.
한 페이지에 있는 연속된 선형 주소는 연속된 물리 주소로 매핑되어 커널은 모든 선형 주소마다 물리 주소와 접근 권한을 지정하는 것이 아니라 페이지 단위로 지정합니다.
페이징 유닛은 램의 모든 영역이 ‘페이지 프레임’이라는 고정된 길이로 나뉘어져 있다고 생각하며, 각 페이지 프레임마다 페이지가 하나씩 들어가는 식으로 메모리를 관리합니다.
일반적으로 80x86에서는 4KB 크기의 페이지를 사용합니다.
선형 주소 변환은 ‘페이지 디렉토리’라고 하는 변환 테이블을 사용하는 단계와 ‘페이지 테이블’을 사용하는 단계로 이루어집니다. 이렇게 두 단계로 나누어 변환하는 이유는 프로세스마다 필요한 페이지 테이블이 램에서 차지하는 크기를 줄이기 때문입니다.
제어 레지스터 cr3에는 현재 사용 중인 페이지 디렉토리의 물리 주소가 들어 있습니다. 이를 좀 더 자세히 살펴 보겠습니다.
커널이 어떤 프로세스에 0x20000000에서 0x2003ffff까지 선형 주소를 할당하였고 0x20021406에 있는 값을 가져오려고 하겠습니다.
리눅스에서는 32비트와 64비트 아키텍처 양쪽 모두에 적합하도록 페이징을 3단계 걸쳐서 수행합니다. (커널 2.6.11부터는 네 단계의 페이징 모델이 채택되었습니다)
일반적인 32비트 아키텍처에서는 페이지 중간 디렉토리 필드의 길이가 0비트라고 표시하고 페이지 상위 디렉토리 및 페이지 중간 디렉토리를 기본적으로 제거합니다.
하지만 포인터 연결 과정에서 페이지 상위 디렉토리와 페이지 중간 디렉토리의 위치는 그대로 남겨놓아서 동일한 코드로 32비트와 64비트 아키텍처 모두 정상적으로 동작하게 됩니다.
물리 주소 확장 기능이 활성된 32비트 아키텍처에서는 페이지 전역 디렉토리가 80x86의 페이지 디렉토리 포인터 테이블에 해당되며, 페이지 상위 디렉토리는 제거되고 리눅스의 중간 디렉토리는 80x86의 페이지 디렉토리에, 리눅스의 페이지 테이블은 80x86의 페이지 테이블에 해당하게됩니다.
64비트 아키텍처에서는 3 또는 4 단계의 페이징이 사용됩니다.
IP 프로토콜은 오류제어에 대한 메커니즘이 포함되어 있지 않습니다.
단지 수신 주소로 패킷을 보내버릴뿐 해당 패킷이 안전하게 도착하였는지는 체크하지 못한다는 말입니다. 그렇기때문에 IP 프로토콜만으로는 올바른 데이터 전송을 할 수 없습니다. 이를 해결하기 위해 같이 사용하는 프로토콜이 바로 ICMP 프로토콜입니다.
즉, ICMP 프로토콜은 송신 호스트에게 IP 전달에 대한 다양한 메세지를 전달하기 위한 프로토콜입니다.
개념적으로는 TCP/IP 아키텍처중 Internet 계층에 속하며 IP 모듈내에 ICMP 모듈이 포함된 형태라고 보시면 됩니다.
총 8바이트의 헤더로 구성되어 있습니다.
Type에는 해당 ICMP 패킷이 어떠한 종류의 ICMP 메세지를 보내는 패킷인지 나타내는 것이고 Code에는 해당 종류의 메세지 중 세부사항 메세지를 의미하는 코드 값입니다.
Type에는 매우 많은 종류가 있는데, 그 중에 많이 사용하는 유형 몇가지만 알아볼 것입니다.
IP노드와 네트워크 간의 통신이 가능한지를 확인 하기 위해 사용됩니다. Echo Request(0)을 보내면 Echo Reply(8)이 응답하는 방식으로 동작합니다 (ex : ping 프로그램)
IP 패킷이 목적지에 제대로 전달되지 않은 경우 일반적으로 라우터에게 탐지가 되는데 이때 라우터가 출발지 호스트에게 보낼때 사용됩니다.
[Type3 코드 표]
라우터에서 받는 패킷의 전송 속도가 보내는 전송 속도보다 클 경우, 임시로 패킷을 버퍼에 임시로 저장을 하게 되는데, 버퍼가 다 차버릴 경우 그 뒤에 받는 패킷은 버릴수 밖에 없게 됩니다. 그렇기 때문에 버퍼가 다 차기 전에 어느정도 버퍼를 비워줘야 하는데 그러기 위해 라우터에게 패킷을 보내는 호스트에게 패킷을 천천히 보낼 것을 요청을 해야 합니다. 이 때 사용됩니다.
좀 더 최적에 가까운 경로가 탐지 되었을 경우 패킷을 보내는 호스트에게 이를 알림으로 패킷이 좀 더 최적의 가까운 경로로 보내질 수 있도록 유도할 때 사용됩니다.
[Type5 패킷 구조]
라우터에서 동적으로 생성되어 있는 라우터를 탐지하기 위해 사용됩니다.
[Type9 패킷 구조]
호스트에서 해당 지역에 존재하는 라우터 IP 주소를 획득하기 위해 사용됩니다.
TTL값이 0이 되어 패킷을 폐기하야 하거나 단편화된 패킷이 모두 도달하지 않아 재조립이 불가능할 때 출발지 호스트에게 이를 알리기 위해 사용됩니다.
저장매체가 없는 요청 호스트의 IP 주소를 알아내기 위해 사용됩니다.
RARP, BOOTP 또는 DHCP에서 사용할 것을 권장하고 있습니다.
[Type 15, 16 패킷 구조]
저장매체가 없는 특정 호스트가 서브넷 마스크를 획득하기 위해 사용됩니다.
[Type 17, 18 패킷 구조]
TOS : 서비스 유형
IP 패킷을 수신하는 곳에서 임의대로 이 값을 정할 수 있기때문에 요즘에는 거의 의미 없는 필드입니다. 왜냐하면 너도나도 전부 이 값을 높게 셋팅해서 보내면 전부 최우선 순위를 갖게 되기 때문에 우선순위라는 것 자체가 의미가 없어지게됩니다.
Total Length : 데이터를 포함한 전체 길이. 프레임 규격을 맞추기 위해 패딩(padding) 옵션을 사용하고자 할때 필요합니다. 패딩은 현재 프레임보다 작은 최소 프래임 규격을 갖는 네트워크에 전송하기 위해 가짜 데이터를 넣는 것입니다.
;ipl.nas
;---------------------------------------------------------------
; 플로피 디스크의 정보
ORG 0x7c00
JMP entry
DB 0x90
DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통은 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역의 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS " ; 디스크 이름(11바이트)
DB "FAT12 " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다(NASK에서는 18바이트만큼 0으로 채움)
; 프로그램 본체
entry:
mov ax, cs
mov ds, ax
mov ax, 0xB800
mov es, ax
mov di, 0
mov ax, word [msgBack]
mov cx, 0x7FF
paint:
mov word [es:di], ax
add di, 2
dec cx
jnz paint
mov di, 0
mov byte [es:di], 'A'
inc di
mov byte [es:di], 0x06
inc di
mov byte [es:di], 'B'
inc di
mov byte [es:di], 0x06
inc di
mov byte [es:di], 'C'
inc di
mov byte [es:di], 0x06
inc di
mov byte [es:di], '1'
inc di
mov byte [es:di], 0x06
inc di
mov byte [es:di], '2'
inc di
mov byte [es:di], 0x06
inc di
mov byte [es:di], '3'
inc di
mov byte [es:di], 0x06
jmp $
msgBack db '.', 0x67
times 510-($-$$) db 0
dw 0xAA55
;---------------------------------------------------------------
ORG 0x7c00
프로그램상의 오프셋의 기준 값을 0x7c00으로 설정하는 것입니다.
바이오스가 디스크의 MBR(첫 512 바이트)을 읽어들여 램의 물리 주소 0x07c00 번지에 복사한 후 0x700으로 점프 하게 됩니다. 그렇기 때문에 오프셋을 0x7c00으로 설정하여 그 다음부터 프로그램이 실행 될 수 있도록 하는 것입니다.
JMP entry
entry 레이블로 점프 합니다.
DB 0x90
DB "HELLOIPL" ; boot sector이름을 자유롭게 써도 좋다(8바이트)
DW 512 ; 1섹터 크기(512로 해야 함)
DB 1 ; 클러스터 크기(1섹터로 해야 함)
DW 1 ; FAT가 어디에서 시작될까(보통은 1섹터째부터)
DB 2 ; FAT 개수(2로 해야 함)
DW 224 ; 루트 디렉토리 영역의 크기(보통은 224엔트리로 한다)
DW 2880 ; 드라이브 크기(2880섹터로 해야 함)
DB 0xf0 ; 미디어 타입(0xf0로 해야 함)
DW 9 ; FAT영역의 길이(9섹터로 해야 함)
DW 18 ; 1트럭에 몇 개의 섹터가 있을까(18로 해야 함)
DW 2 ; 헤드 수(2로 해야 함)
DD 0 ; 파티션을 사용하지 않기 때문에 여기는 반드시 0
DD 2880 ; 드라이브 크기를 한번 더 write
DB 0,0,0x29 ; 잘 모르지만 이 값으로 해 두면 좋은 것 같다
DD 0xffffffff ; 아마, 볼륨 시리얼 번호
DB "HELLO-OS " ; 디스크 이름(11바이트)
DB "FAT12 " ; 포맷 이름(8바이트)
RESB 18 ; 우선 18바이트를 비어 둔다(NASK에서는 18바이트만큼 0으로 채움)
플로피 디스크에 대한 정보를 저장합니다. 자세한 내용은 아직 잘 모르겠습니다. 후에 추가해야 할듯..
entry:
mov ax, cs
mov ds, ax
cs(코드세그먼트)와 ds(데이터 세그먼트)를 같게 합니다.
이는 세그먼트를 지정하지 않고 데이터를 사용하면 CPU는 묵시적으로 DS에 있는 값을 세그먼트로 사용하겠다고 인식하게 됩니다. 만약 CS와 DS를 같게 해주지 않는다면 원치 않는 데이터를 읽거나 쓸게 될수도 있습니다.
mov ax, 0xB800
mov es, ax
es 세그먼트에 0xB800을 넣습니다.
0xB800를 넣는 이유는 컬러 텍스트 모드로 비디오 메모리를 사용하기 위해서 입니다.
컬러 텍스트 모드는 0xB800:0000번지에서 0xB800:FFFF 의 한 개의 세그먼트 영역을 사용하여 모니터에 글씨를 문자로 단위로 나타내거나 그림을 픽셀 단위로 나타냅니다.
글자를 나타내는 대에는 1바이트의 아스키 코드 값과 4비트의 배경색, 4비트의 글자색으로 총 2바이트로 나타냅니다. 예를 들어 화면의 젤 위의 첫번째 칸에 ‘A’라는 흰색바탕에 검은 글씨를 나타내고 싶다면
0xB800:0000번지에는 0x65를 넣고 0xB800:0001번지에는 0x70넣으면 됩니다.
mov di, 0
mov ax, word [msgBack]
mov cx, 0x7FF
di값은 시작 번지부터 다음 번지로 이동하기 위해 사용되는 변수고, ax 레지스터에는 msgBack이 가리니키는 값을 넣습니다 msgBack는 밑에 db ‘.’, 0x67 로 정의되어 있는데, 이는 갈색 바탕의 흰색 점입니다.
그리고 cx 레지스터에는 데이터를 넣을 길이입니다.
paint:
mov word [es:di], ax
add di, 2
dec cx
jnz paint
실제로 그려지는 부분입니다.
바로 위에서 ax레지스터에 갈색 바탕에 흰색 점을 뜻하는 값이 저장되어 있었습니다.
es:di 위치에 점을 찍고 di값을 증가 시킨후 cx값을 1 감소 시켰습니다. 그리고 zero 플래그를 체크한뒤 다시 paint 레이블로 점프를 합니다. 즉, cx값이 0이 될때까지 es:0부터 오프셋값을 2씩 증가시키면서 점을 찍는 코드가 되겠습니다.
mov di, 0
mov byte [es:di], 'A'
inc di
mov byte [es:di], 0x06
다시 di 값을 0으로 초기화 하여 화면 맨 위에 첫번째칸을 가리키기 한후 ‘A’라는 검은색 바탕에 갈색 글자를 나타내게 하였습니다. 그 뒤에 ‘B’, ‘C’, ‘1’, ‘2’, ‘3’ 도 마찬가지입니다.
코드 색상
0x0(0000) 검은색
0x1(0001) 파란색
0x2(0010) 녹색
0x3(0011) 하늘색
0x4(0100) 빨간색
0x5(0101) 보라색
0x6(0110) 갈색
0x7(0111) 흰색jmp $
$표시는 현재 수행하고 있는 명령어의 주소값을 가르킵니다. 즉, 현재 수행하고 있는 명령어의 주소값으로 점프하라는 명령어이므로 while(1)처럼 계속 반복하게 하는 코드입니다.
times 510-($-$$) db 0
dw 0xAA55
times 510-($-$$) db 0 명령은 현재 번지에서부터 509번까지 0으로 채우라는 명령입니다.
그리고 510번지에는 0x55를 511번지에는 0xAA를 넣게 되는데, 이것은 PC가 부팅할때 바이오스는 디스크의 MBR 부분을 램으로 복사하면서 이 곳이 프로그래머가 지정한 MBR인지 확인을 하는데 이때 체크하는 값이 MBR의 제 끝의 1워드 값입니다. 이 값이 “0xAA55”면 MBR으로 판단하게 됩니다.