컴퓨터 구조의 문제점    

 

 컴퓨터 구조의 가장 큰 문제는 CPU와 메모리, 주변 장치 간 작업 속도가 다르다는 것이다. 메모리와 주변 장치는 시스템 버스(FSB)로 연결되어 있고, CPU(ALU, 제어 장치, 레지스터)는 CPU 내부 버스(BSB)로 연결되어 있다. 문제는 CPU 내부 버스의 속도가 시스템 버스의 속도보다 빠르기 때문에 메모리를 비롯한 주변 장치의 속도가 CPU의 속도를 따라가지 못한다. CPU에 비하면 메모리가 느리고 프로그램과 데이터를 보관하는 하드디스크는 메모리보다 느리다. 

 

 이런 문제로 장치간 속도를 개선하고 시스템 작업 속도를 올리기 위해 기술이 개발되었는데 버퍼, 캐시가 이에 해당된다.  

 


    버퍼(Buffer)    

 

 버퍼(Buffer)는 속도 차이가 있는 두 장치 사이에서 그 차이를 완화해준다. 예를 들어, 사과 50개를 마트에 옮긴다고 가정해보자. 한 번에 하나씩 옮기면 50번을 왕복해야 하지만 사과 50개를 트럭에 실어 옮기면 한 번에 옮길 수 있다. 이 때, 버퍼는 트럭에 비유된다. 

 

 입출력에서 데이터를 가져오는 경우에도 마찬가지이다. 느린 입출력 장치에서 데이터를 읽을 때마다 하나씩 전송하면 작업량에 비해 실제로 전송되는 데이터 양이 매우 작다. 따라서, 일정량의 데이터를 모아 전송하면 적은 노력으로도 많은 양의 데이터를 옮겨 속도의 차이를 완화할 수 있다.

 

 또한, 버퍼는 소프트웨어적으로도 사용되는데 대표적인 예는 동영상 스트리밍이다. 동영상은 네트워크에서 데이터가 들어오는 시간과 플레이어가 재생되는 시간의 속도 차이가 발생한다. 플레이어가 재생되는 도중에 데이터가 도착하지 않으면 동영상이 끊기는데,  동영상 데이터 일정부분을 버퍼에 넣으면 이러한 현상을 방지해준다.

 


    스풀(Spool)    

 

 스풀(Spool)은 CPU와 입출력장치가 독립적으로 동작하도록 고안된 소프트웨어적인 버퍼이다. 대표적으로는 프린터에 사용되는 스풀러(Spooler)이다. 스풀러는 인쇄할 내용을 순차적으로 출력하는 소프트웨어로 출력 명령을 내린 프로그램과 독립적으로 동작한다. 

 

 예를 들어, 워드프로세서로 작업을 하고 프린터로 출력할 때, 프린터 출력 작업과 문서 작업을 동시에 할 수 있는데 이는 스풀러를 사용했기 때문이다. 스풀러가 없다면 프린트 출력 작업과 문서 작업이 독립적으로 진행되지 못하기에 출력 작업이 끝날 때까지 문서작업을 할 수 없다. 

 

 또한, 스풀은 버퍼와 차이점이 있다. 버퍼는 어떤 프로그램이 사용하는 데이터든 버퍼가 차면 이동한다. 즉, 프로그램들 사이에 버퍼를 공유한다. 하지만, 스풀은 한 인쇄물이 완료될 때까지 다른 인쇄물이 끼어들 수 없다. 즉, 프로그램간에 배타적이다. 

 


 

    캐시(Cache)    

 

 캐시(Cache)메모리와 CPU 간의 속도 차이를 완화하기 위해 메모리의 데이터를 미리 가져와 저장해두는 임시 장소이다. 캐시는 필요한 데이터를 모아 한꺼번에 전달하는 버퍼의 일종으로 CPU가 앞으로 사용할 것이라고 예상되는 데이터를 미리 가져다 놓는다. 이 때, 미리 가져오는 작업을 미리 가져오기(prefetch)라고 한다. 예를 들면, 장독대에 있는 간장을 미리 간장통에 가져다 놓는 것을 들 수 있다.

 

 캐시는 CPU 안에 있으며 CPU 내부 버스의 속도로 작동하기 때문에 느린 시스템 버스의 속도로 동작하는 메모리의 단점을 보완해준다. 

 

 캐시는 메모리 내용 중 일부를 미리 가져오고, CPU는 메모리에 접근해야 할 캐시를 먼저 방문해 원하는 데이터를 찾아본다. 만약 캐시에 데이터가 존재하면 캐시 히트(Cache hit)라고 하며 그 데이터를 사용한다. 하지만, 원하는 데이터가 없으면 메모리로 가서 데이터를 찾는데 이를 캐시 미스(Cache Miss)라고 한다. 캐시가 히트되는 적중률을 캐시 적중률(Cache hit ratio)라고 한다. 

 

 

 

 컴퓨터의 성능을 향상하려면 캐시 적중률이 높아야 한다. 캐시 적중률을 높이는 방법 중 하나는 캐시의 크기를 늘리는 것이다. 또한, 앞으로 많이 사용될 데이터를 가져오는 것이다. 현재 위치에 가까운 데이터가 멀리 있는 데이터보다 사용될 확률이 더 높다는 지역성(Locality) 이론이 있다.  

 

 캐시를 가져오는 것 뿐만 아니라 캐시에 있는 데이터가 변경되는 경우 이를 반영해야 하는 문제도 있다. 캐시는 메모리에 있는데이터를 임시로 가져온 것이기 때문에 캐리에 있는 데이터가 변경되면 메모리에 있는 원래 데이터를 변경해야 한다. 변경하는 방법에는 즉시 쓰기와 지연 쓰기 방식이 있다. 

 

  •  즉시 쓰기(Write through): 캐시에 있는 데이터가 변경되면 이를 즉시 메모리에 반영하는 방식, 메모리와의 빈번한 데이터 전송으로 인해 성능이 느려진다는 단점이 있지만, 급작스러운 정전에도 데이터를 잃지 않는다는 장점이 있다. 
  • 지연 쓰기(Write back): 변경된 내용을 주기적으로 반영하는 방식, 메모리와의 전송 횟수가 줄어 시스템의 성능을 향상할 수 있으나 메모리와 캐시된 데이터 사이의 불일치가 발생할 수 있다. 

 

 


 

    저장장치의 계층 구조    

 

 가격과 컴퓨터 성능 사이의 타협점으로 저장장치의 계층 구조(Storage hierarchy)가 존재한다. 저장장치의 계층 구조는 속도가 빠르고 값이 비싼 저장장치를 CPU 쪽에 두고, 값이 싸고 용량이 큰 저장 장치를 반대쪽에 배치하여 적당한 가격으로 빠른 속도와 큰 용량을 동시에 얻는 방법이다. 

 

 CPU와 가까운 쪽에 레지스터나 캐시를 배치하여 CPU가 작업을 빨리 진행할 수 있게한다. 비교적 속도가 느린 메모리에서 작업한 내용을 하드디스크와 같이 저렴하고 용량이 큰 저장장치에 영구적으로 저장한다. 이처럼 저장장치 계층 구조는 사용자가 저렴한 가격으로 용량은 하드디스크처럼 사용하고 작업 속도는 레지스터처럼 빠르도록 만들어 준다. 

 

 

 

 하지만 저장장치의 계층 구조는 중복되는 데이터의 일관성을 유지하는 것이 문제가 될 수 있다. 변경된 내용을 즉시 반영하지 않고 일정 기간 모았다가 한 꺼번에 메모리에 반영하는 지연 쓰기의 경우나 협업 중인 다른 작업에서 데이터를 읽으려 하거나 갑자기 전원이 꺼지는 경우가 해당한다. 

 


 

    인터럽트    

 

 컴퓨터에는 많은 주변장치가 있기 때문에 CPU가 모든 입출력에 관여하면 작업 효율이 현저하게 떨어진다. 이러한 문제를 해결하기 위해 등장한 것이 인터럽트(Interrupt) 방식이다. 인터럽트는 요리사 옆에 주방 보조를 두는 것과 같다. CPU 작업과 저장장치의 데이터 이동을 독립적으로 운영함으로써 시스템의 효율을 높인다. 즉, 데이터의 입출력이 이루어지는 동안 CPU는 다른 작업을 할 수 있다. 이 때, 입출력 관리자 또는 장치 관리자가 주방 보조에 해당한다. 

 

 [인터럽트 동작 방식]

  1. CPU가 입출력 관리자에게 입출력 명령을 보낸다.
  2. 입출력 관리자는 명령받은 데이터를 메모리에 가져다놓거나 메모리에 있는 데이터를 저장장치로 옮긴다.
  3. 데이터 전송이 완료되면 입출력 관리자는 완료 신호를 CPU로 보낸다. 

 이 때, 입출력 관리자가 CPU에게 보내는 완료 신호를 인터럽트라고 한다. CPU는 다른 작업을 하다가 완료 신호를 받으면 하던 작업을 중지하고 옮겨진 데이터를 처리한다. 인터럽트 방식에서는 많은 주변 장치중 어떤 것의 작업이 끝났는지를 CPU에 알려주기 위해 인터럽트 번호를 사용한다. 인터럽트 번호는 완료 신호를 보낼 때 장치의 이름 대신 사용하는 장치의 고유 번호로서 운영체제마다 다르다. 윈도우의 경우 인터럽트 번호를 IRQ(Interrupt ReQuest)라고 부른다. 

 

 CPU는 입출력 관리자에게 여러 개의 입출력 작업을 동시에 시킬 수 있다. 이 경우 여러 작업이 동시에 완료되고 그 때마다 인터럽트를 여러 번 사용해야 하는데 이는 비호율적이다. 그래서 여러 개의 인터럽트를 하나의 배열로 만든 인터럽트 벡터(Interrupt Vector)를 사용한다.

 

 인터럽트 동작 방식은 다음과 같다. 입출력 요청 - 데이터 전송 - 인터럽트 발생 순으로 진행되는데 인터럽트 0번과 2번의 작업이 완료되어 인터럽트 0번과 2번이 동시에 발생했다는 것을 알 수 있다. CPU가 인터럽트 벡터를 받으면 0번과 2번의 작업을 동시에 처리한다.

 

 

 입출력 작업을 알리는 인터럽트 외에도 다양한 종류의 인터럽트가 있는데 사용자가 컴퓨터의 전원 버튼을 눌러 강제 종료하면 CPU는 하던 일을 모두 멈추고 처리 중인 데이터를 안전하게 보관한 뒤 시스템을 종료해야 한다. 또한, 메모리에서 실행중인 어떤 작업이 자신에게 주어진 메모리 영역을 넘어서 작업을 하려 하거나 0으로 숫자를 나누면 인터럽트가 발생한다. 

 

 

 [참고]

  • 직접 메모리 접근(Direct Memory Access, DMA): 메모리는 접근 권한을 가진 CPU만 접근이 가능하고 입출력 관리자는 접근이 불가능하다. 따라서, 입출력 관리자에게는 CPU의 허락없이 직접 메모리에 접근할 수 있는 권한을 부여한다. 
  • 메모리 매핑 입출력(Memory Mapped I/O): 직접 메모리 접근을 사용하면 메모리가 복잡해진다. CPU가 사용하는 데이터와 입출력 장치가 사용하는 데이터가 섞여 있다. 이를 위해 CPU가 사용하는 메모리 공간직접 메모리 접근을 통해 들어오거나 나가는 데이터를 위한 공간을 분리한다. 
  • 사이클 훔치기(Cycle stealing): 만약 CPU와 입출력 관리자가 동시에 메모리에 접근하려고 하면 누군가는 양보를 해야한다. 이는 보통 CPU가 메모리 사용 권한을 양보하는데 이는 CPU의 작업 속도보다 입출력장치의 속도가 더 느르기 때문이다. 이러한 상황을 사이클 훔치기라고 한다. 

'운영체제 > 기초' 카테고리의 다른 글

9. 프로세스  (0) 2020.10.16
8. 병렬처리  (0) 2020.10.16
6. CPU와 메모리(2)  (0) 2020.10.14
5. CPU와 메모리(1)  (0) 2020.10.06
4. 하드웨어의 기본 구성  (0) 2020.10.05

    버스의 종류    

 

 버스는 CPU와 메모리, 주변장치 간 데이터를 주고받을 때 사용한다. 버스의 종류에는 제어 버스, 주소 버스, 데이터 버스가 있다.

 

  • 제어 버스(Control Bus): 다음에 어떤 작업을 할지 지시하는 제어 신호가 오고 간다. 예를 들어, 메모리에서 데이터를 가져올 때는 읽기 신호를 보내고, 처리한 데이터를 메모리로 옮겨놓을 때는 쓰기 신호를 보낸다. 즉, 제어 버스의 신호는 CPU, 메모리, 주변 장치와 양방향으로 오고 간다.
  • 주소 버스(Address Bus): 메모리의 데이터를 읽거나 쓸 때 어느 위치에서 작업할 것인지를 알려주는 위치 정보가 오고 간다. 주소 버스는 메모리 주소 레지스터와 연결되어 있으며 단방향이다. 따라서, CPU에서 메모리나 주변 장치로 나가는 주소 정보는 있지만 주소 버스를 통해 CPU로 전달되는 정보는 없다. 
  • 데이터 버스(Data Bus): 메모리 버퍼 레지스터와 연결되어 있으며 양방향이다. 

 

 

 

 버스의 대역폭은 한 번에 전달할 수 있는 데이터의 최대 크기를 말하는데 CPU가 한 번에 처리할 수 있는 데이터의 크기와 같다. 흔히 32bit CPU, 64bit CPU라고 하는데, CPU가 한 번에 처리할 수 있는 데이터의 최대 크기를 말한다. 32bit의 경우 버스의 대역폭도 32bit이고 레지스터의 크기, 메모리에 한 번에 저장할 수 있는 데이터의 크기도 32bit이다. 

 

 참고로 CPU가 한 번에 처리할 수 있는 데이터 최대 크기를 워드(word)라고 한다. 이는 버스의 대역폭과 메모리에 한 번에 저장되는 단위도 워드라는 것을 알 수 있다. 32bit CPU에서 1워드는 32bit이다. 


    메모리의 종류    

 

 모든 프로그램은 메모리에 올라와야 실행될 수 있다. 메모리에는 실행에 필요한 프로그램과 데이터가 존재하며 CPU와 협업하며 작업이 이루어진다. 메모리를 이루는 소자 하나의 크기는 1bit이고 0또는 1이 저장된다. 그러나 소자 하나만으로는 데이터를 표현할 수 없어 메모리 주소는 바이트 단위로 지정되고, 메모리에서 데이터를 읽거나 쓸 때에는 워드 단위로 움직인다. 

 

 메모리는 크게 두 종류로 읽고 쓸 수 있는 램(RAM, Random Access Memory)와 읽기만 가능한 롬(ROM, Random Only Memory)로 구분된다. 

 

 

 

 램은 또한 전력이 끊기면 데이터가 사라지는 휘발성 메모리와 전력이 끊겨도 데이터를 ㅂ관할 수 있는 비휘발성 메모리로 나뉜다. 

 

  • 휘발성 메모리(Volatility Memory)에는 동적 램(Dynamic RAM)과 정적 램(Static RAM)이 있다. 동적 램은 저장된 0과 1의 데이터가 일정 시간이 지나면 사라지기에 다시 재생해야 한다. 정적 램은 전력이 공급되는 동안에만 데이터를 보관할 수 있다. 속도는 빠르지만 가격이 비싸다. 일반적으로 메인 메모리는 동적 램을, 캐시 같은 고속 메모리에는 정적 램을 사용한다. 

 

  • 비휘발성 메모리(Non-Volatility Memory)에는 플래시 메모리(Flash Memory)와 FRAM, PRAM 등이 있다. 플래시 메모리는 디지털 카메라, MP3 플레이어, USB 드라이버 같이 전력이 없어도 데이터를 보관하는 저장장치로 사용한다. 하지만, 플래시 메모리의 각 소자는 최대 사용 횟수가 제한되어 많이 사용하면 기능을 잃는다. 따라서, SD 카드나 USB 드라이버를 오래 사용하면 성능이 저하되거나 데이터를 잃어 버릴 수 있다. 또한, 하드디스크를 대신하도록 만든 SSD는 가격이 비싸지만 데이터 접근 속도, 내구성 때문에 컴퓨터, 노트북, 스마트폰 등에 많이 사용한다.

 

 롬은 전력이 끊겨도 데이터를 보관하는 것이 가능하지만 데이터를 한 번 저장하면 바꿀 수 없다는 단점이 있다. 예를 들어 빈 CD는 CD-ROM이라고도 하며 데이터를 한 번만 저장할 수 있다. 롬의 종류에는 마스크 롬, PROM, EPROM 등이 있다. 

 


 

    메모리 보호    

 

  현대의 운영체제는 시분할 기법을 사용하여 여러 프로그램을 동시에 실행하므로 사용자 영역이 여러 개의 작업 공간으로 나뉘어져 있다. 이러한 상황에서 메모리 보호는 매우 중요한 문제이다. 만약 메모리가 보호되지 않으면 어떤 작업이 다른 작업의 영역을 침범하여 프로그램을 파괴하거나 데이터를 지울 수 있으며 최악의 경우에는 시스템을 멈출 수 있다. 이처럼 운영체제 영역이나 다른 프로그램 영역으로 침범하려는 악성 소프트웨어를 바이러스(Virus)라고 한다. 

 

 운영체제도 소프트웨어 중 하나이므로 CPU를 사용하는 작업 중 하나이다. 사용자의 작업이 진행되는(사용자 프로세스가 CPU를 차지하는) 동안 운영체제는 잠시 중단된다. 따라서, 운영체제가 중단된 상태에서 메모리를 보호하려면 하드웨어의 도움이 필요하다. 

 

 메모리를 보호하기 위해 CPU는 현재 진행 중인 작업의 메모리 시작 주소를 경계 레지스터(Bound Register)에 저장한 후 작업을 한다. 또한, 현재 진행 중인 작업이 차지하고 있는 메모리의 크기, 즉 마지막 주소까지의 차이를 한계 레지스터(Limit Register)에 저장한다. 그리고 사용자의 작업이 진행되는 동안 이 두 레지스터의 주소 범위를 벗어나는지 하드웨어적으로 점검해서 메모리를 보호한다. 

 

 B 작업의 메모리 시작 주소 140은 경계 레지스터에, B 작업의 크기인 40은 한계 레지스터에 저장된다. B 작업이 데이터를 읽거나 쓸 때마다 CPU는 해당 작업이 경계 레지스터와 한계 레지스터의 주소값 안에서 이루어지는지 검사한다. 만약 두 레지스터의 값을 벗어나면 인터럽트(Interrupt)가 발생한다. 인터럽트가 발생하면 모든 작업이 중단되고 CPU는 운영체제를 깨워 인터럽트를 처리하도록 시킨다. 운영체제가 실행되면 해당 프로그램을 강제 종료시킨다. 이처럼 메모리 영역은 하드웨어와 운영체제의 협업에 의해 보호받는다. 

 


    부팅(Booting)    

 

  응용프로그램은 운영체제가 메모리에 올려서 실행하는데, 운영체제는 누가 메모리에 올려서 실행될까? 운영체제도 CPU에 의해 실행되기 때문에 누군가에 의해 메모리에 올라와야 한다. 컴퓨터를 켰을 때 운영체제를 메모리에 올리는 과정을 부팅(Booting)이라고 한다.

 

 

 사용자가 컴퓨터의 전원을 키면 롬(ROM)에 저장된 바이오스(Bios)가 실행된다. 바이오스는 CPU, 메모리, 하드디스크, 키보드, 마우스와 같은 주요 하드웨어가 제대로 작동하는지 확인한다. 만약 이상이 있으면 삐 소리와 함께 오류 메세지를 출력하고, 이상이 없으면 하드디스크 마스터 부트 레코드(Master Boot Record, MRB)에 저장된 작은 프로그램을 메모리로 가져와 실행한다. 

 

 마스터 부트 레코드는 하드디스크의 첫 번째 섹터를 가리키며, 운영체제를 실행하기 우한 부트스트랩(Bootstrap) 코드가 이 곳에 저장되어 있다. 부트스트랩 코드는 운영체제를 메모리에 가져와 실행하는 역할을 하는 작은 프로그램이다.

 

 마스터 부트 레코드에 저장되어 있는 부트스트랩이 메모리에 올라오면 하드디스크에 저장된 운영체제를 메모리로 불러온다. 따라서, 마스터 부트 레코드가 손상되면 운영체제를 실행할 수 없다. 이는 컴퓨터 바이러스의 좋은 공격 대상이 된다. 따라서, 일부 바이오스는 사용자 옵션 부분에 운영체제 보호 항목을 만들어 마스터 부트 레코드를 수정할 수 없게 한다. 

 

'운영체제 > 기초' 카테고리의 다른 글

8. 병렬처리  (0) 2020.10.16
7. 컴퓨터 성능 향상 기술  (0) 2020.10.14
5. CPU와 메모리(1)  (0) 2020.10.06
4. 하드웨어의 기본 구성  (0) 2020.10.05
3. 운영체제의 구조  (0) 2020.10.05

 

    IP 주소의 구조   

 

  IPv4주소는 32비트이며 32비트를 8비트로 단위로 나눠서 표현하는 것을 옥텟(Octet)이라고 하는데 32비트를 4개의 옥텟으로 나누어 10진수로 표현한다.

 

 

  또한, 이 IP 주소는 네트워크 주소(네트워크 ID)와 호스트 주소(호스트 ID)로 나뉘는데 네트워크 주소는 모든 호스트들을 관리하기가 어렵기 때문에 네트워크에 범위를 그룹(호스트들의 집합)으로 지정해 어떤 네트워크인지를 나타내고 호스트 주소는 네트워크 그룹 내에서 어느 컴퓨터인지를 나타낸다. 이 두 가지 정보가 합쳐져서 IP 주소가 된다.

 

 IP 주소 = 네트워크 주소(네트워크 ID) + 호스트 주소(호스트 ID)

 

 


    IP 주소 클래스(Class)   

 

  IP 주소는 네트워크 주소를 크게 만들거나 호스트 주소를 작게 만들어 네트워크 크기를 조정할 수 있다. 이 네트워크 크기는 클래스(Class)라는 개념으로 구분한다. 

 

 

 

  일반 네트워크에서는 A~C 클래스를 사용할 수 있다. A 클래스는 처음 8비트가 네트워크 주소이고 나머지 24비트가 호스트 ID이다. A 클래스의 1옥텟의 범위는 00000001~11111111로 10진수로는 1~127이 된다. 2~4옥텟의 범위는 각각 00000000~11111111로 0~255가 된다. 

 

 따라서, A 클래스의 네트워크 ID와 호스트 ID의 32비트 전체 범위는 1.0.0.0 ~ 127.255.255.255가 된다. 

 

 

 

 B 클래스는 처음 16비트가 네트워크 ID이고 나머지 16비트가 호스트 ID 이다. B 클래스의 1옥텟의 범위는 10000000~10111111로 10진수로는 128~191이 된다. 2~4옥텟의 범위는 각각 00000000~11111111로 0~255가 된다. 

 

 따라서, B 클래스의 네트워크 ID와 호스트 ID의 32비트 전체 범위는 128.0.0.0 ~ 191.255.255.255가 된다. 

 

 

 

 C 클래스는 처음 24비트가 네트워크 ID이고 나머지 8비트가 호스트 ID이다. C 클래스의 1옥텟의 범위는 11000000~11011111로 10진수로는 192~223이 된다. 2~4옥텟의 범위는 각각 00000000~11111111로 0~255가 된다. 

 

 따라서, C 클래스의 네트워크 ID와 호스트 ID의 32비트 전체 범위는 192.0.0.0 ~ 223.255.255.255가 된다. 

 

 

 

 

 여기서 주의할 점은 공인 IP 주소와 사설 IP 주소가 정해져 있다는 것이다.

 

 

 가정 LAN에서는 주로 C 클래스의 사설 IP 주소인 192.168.X.X가 주로 사용된다. 윈도우에서는 검색 창에 cmd를 입력 후 명령 프롬프트 창에서 ipconfig 명령어를 입력하면 사용할 수 있다. 

 

 

 [참고]

  • 공인 IP: 전 세계에서 유일한 IP로 ISP가 제공하며 외부에 공개되어 있기 때문에 인터넷에 연결된 다른 장비로부터 접근이 가능하다. 
  • 사설 IP: 한 네트워크 안에서 사용되는 IP 주소 IPv4의 부족으로 인해 모든 네트워크가 공인 IP를 사용하는 것은 불가능하기 때문에 네트워크 안에서 라우터를 통해 할당받는 가상의 주소이다. 외부에서 접속할 수 없다. 

 


 

    네트워크 주소와 브로드캐스트 주소   

 

  IP 주소에는 네트워크 주소와 브로드캐스트 주소도 존재하는데, 이 두 주소는 특별한 주소로 컴퓨터나 라우터가 자신의 IP로 사용해서는 안 된다.

 

 트워크 주소는 호스트 ID가 0(2진수로 00000000)인 주소이고 전체 네트워크에서 작은 네트워크를 식별하는데 사용된다. 즉, 그 작은 네트워크를 대표하는 주소가 된다. 

 

 브로드 캐스트 주소는 호스트 ID가 255(11111111)인 주소이다. 해당 네트워크에 있는 컴퓨터나 장비 모두에게 한 번에 데이터를 전송하는데 사용되는 IP 주소이다. 즉, 해당 네트워크에 데이터를 전송하려면 호스트 ID에 255를 설정하면 된다. 

 

 

 


 

    서브넷의 구조   

 

  A 클래스 네트워크는 호스트 ID가 24비트여서 IP 주소를 1677만 7214개를 사용할 수 있다. 만약에 이 수 많은 컴퓨터에 브로드캐스트 패킷을 전송하면 모든 컴퓨터에 전송하는데 이는 네트워크가 정체되고 지연을 유발할 것이다.

 

 이처럼 A 클래스의 대규모 네트워크를 작은 네트워크로 분할하여 브로드캐스트로 전송되는 패킷의 범위를 줄일 수 있는데, 이처럼 네트워크를 분할하는 것을 서브넷팅(Subneting)이라고 하고, 분할된 네트워크를 서브넷(Subnet)이라고 한다. 

 

 그러면 기존의 네트워크 ID와 호스트 ID로 구성되어 있던 것이 네트워크 ID, 서브넷 ID, 호스트 ID로 나눠지게 된다. 즉, 호스트 ID에서 비트를 빌려 서브넷으로 만들 수 있다.

 

 


 

    서브넷 마스크   

 

  IP 주소를 서브넷팅하면 어디까지가 네트워크 ID이고 어디부터가 호스트 ID를 판단하기 힘들다. 이를 위해 서브넷 마스크라는 값을 사용한다. 즉, 서브넷 마스크는 네트워크 ID와 호스트 ID를 식별하기 위한 값이다. 이 값은 프리픽스(Prefix) 표기법으로 사용할 수 있는데 슬래시(/)로 비트 수를 나타낸다. 

 

 예를 들어, C 클래스 IP 주소를 서브넷팅해보면 C 클래스는 일반적으로 네트워크 ID가 24비트이다. 하지만, 호스트 ID에서 4비트를 빌려 네트워크 ID를 28비트로 만들면 네트워크 ID는 24비트, 서브넷 ID는 4비트, 호스트 ID는 4비트가 된다. 밑의 예제에서는  255.255.255.240/28로 표현할 수 있다.

 

 

 

 즉, 서브넷 마스크는 IP 주소의 네트워크 부분만을 나타나게 하여 같은 네트워크인지 판별하게 하는 마스크이다.

 

 


 

    라우터의 구조   

 

  그렇다면 네트워크를 분리하는 역할은 누가할까? 라우터(Router)가 수행한다. 

 

 

 

 네트워크를 분할한 다음에 컴퓨터 한 대가 다른 네트워크로 접속하려면 어떻게 해야 할까? 예를 들어 위의 그림에서 192.168.1.0/24 네트워크의 첫 번째 컴퓨터에서 192.168.2.0/24 네트워크의 첫 번째 컴퓨터로 데이터를 전송하려면 라우터의 IP 주소를 설정해야 한다. 이것은 네트워크의 출입구를 설정하는 것으로 기본 게이트웨이(Default Gateway)를 설정해야  한다. 

 

 기본 게이트웨이는 컴퓨터의 네트워크를 설정하는 화면에 있다. 윈도우 10의 경우 "자동으로 IP 주소 받기"를 체크하면 기본 게이트웨이가 자동으로 설정된다. 

 

 

 "자동으로 IP 주소 받기"를 설정하면 192.168.1.0/24 네트워크의 첫 번째 컴퓨터의 기본 게이트웨이가 설정되었지만 이것만으로는  192.168.2.0/24 네트워크의 첫 번째 컴퓨터에 데이터를 보낼 수 없고 추가로 라우터의 라우팅(Routing)의 기능이 필요하다. 

 


 

    라우팅이란?   

 

  라우팅(Routing)은 경로 정보를 기반으로 현재의 네트워크에서 다른 네트워크로 최적의 경로를 통해 데이터를 전송한다. 해당 경로 정보는 라우팅 테이블(Routing table)에 등록되어 있다. 

 

 

 

  라우팅 테이블에 경로 정보를 등록하는 방법은 수동 등록과 자동 등록이 있다. 수동으로 등록하는 방법은 소규모 네트워크에 적합하고, 자동으로 등록하는 방법은 대규모 네트워크에 적합하다. 자동으로 등록하는 방법은 라우터 간에 경로 정보를 서로 교환하여 라우팅 테이블 정보를 자동으로 수정해 준다.

 

 이처럼 라우터 간에 라우팅 정보를 교환하기 위한 프로토콜을 라우팅 프로토콜(Routing Protocol)이라고 한다. 이 라우팅 프로토콜을 설정하여 라우터 간에 경로 정보를 서로 교환하고 그것을 라우팅 테이블에 등록해 나간다. 라우팅 프로토콜에는 RIP, OSPE, BGF 등이 있고 각각 다른 특징을 가지고 있다. 

 

 

   네트워크 간의 연결 구조   

 

 데이터링크 계층에서는 이더넷 규칙을 기반으로 데이터를 전송하지만 같은 네트워크에 있는 컴퓨터끼리만 가능했다. 인터넷이나 다른 네트워크에 있는 목적지로 데이터를 전달하려면 네트워크 계층의 기술이 필요하다. 아래 그림과 같이 데이터링크 계층의 기능만으로는 해당 네트워크 안에서만 통신이 가능하다는 것을 알 수 있다. 

 

 

 

 

 이렇게 네트워크 간의 통신을 가능하게 해주는 역할을 네트워크 계층에서 해준다. 데이터링크 계층에서 한 컴퓨터에서 다른 컴퓨터로 데이터를 전송하기 위해 스위치라는 장비가 필요했던 것처럼 네트워크 계층에서는 다른 네트워크로 데이터를 전송하기 위해 라우터(Router)라는 네트워크 장비가 필요하다.

 

 

이미지출처: 위키피디아

 

 라우터는 데이터의 목적지가 정해지면 해당 목적지까지 어떤 경로로 가는 것이 좋은지 알려주는 역할을 한다. 하지만, 데이터를 보내려는 목적지를 모르면 라우터도 경로를 알려주지 못한다. 데이터링크 계층에서에서는 MAC 주소만으로도 통신할 수 있었지만 MAC 주소만으로 다른 네트워크에 데이터를 보낼 수 없다. 따라서 네트워크를 식별할 수 있는 다른 주소가 필요한데 그 주소를 IP 주소(IP Address)라고 한다. 

 


   IP란?   

 

 IP 주소는 어떤 네트워크의 어떤 컴퓨터인지를 구분할 수 있도록 해주는 주소이다. 따라서, IP 주소만 있다면 다른 네트워크에 있는 목적지를 지정할 수 있다. 하지만, IP 주소로 목적지를 지정하는 것뿐만 아니라 데이터를 어떤 경로로 보낼지도 결정해야 한다. 이처럼 목적지 IP 주소까지 어떤 경로로 데이터를 보낼지 결정하는 것라우팅(Routing)이라고 한다. 이 결정을 라우터가 수행하는데 라우터에는 라우팅 테이블(Routing Table)이 있어서 경로 정보를 등록하고 관리한다. 

 

 네트워크 계층에는 IP(Internet Protocol)라는 프로토콜이 있는데 통신 규격인 TCP/IP의 그 IP이다. 데이터링크 계층에서 이더넷 헤더를 붙이는 캡슐화를 해서 프레임을 만든 것처럼 네트워크 계층에서는 IP 헤더를 붙여 캡슐화를 진행한다. 

 

 

IP 헤더를 붙여 캡슐화를 진행해 만든 것을 IP 패킷이라고 한다. IP 헤더에는 정말 많은 정보가 포함되어 있는데 중요한 점은 출발지 IP 주소와 목적지 IP 주소가 담겨져 있다는 것이다. 

 


   IP 주소의 버전  

 

 여기서 의문이 드는 것은 IP 주소는 누가 만들고 어떻게 제공받는 것인가이다. IP 주소는 바로 인터넷 서비스 제공자(ISP)가 제공한다. ISP는 통신사를 예로들 수 있는데 우리나라에선 KT, SKT, LG U+ 등에 해당한다. 

 

 IP는 IPv4IPv6라는 두 가지 버전이 존재한다. 원래는 IPv4만 사용했었는데 이는 32비트로 되어 있어서 IP 주소를 약 43억개를 만들 수 있어 충분하다고 생각했다. 하지만 인터넷이 널리 보급되면서 IP 주소가 부족해졌고 현대에는 128비트로 구성된 IPv6를 많이 사용한다. 

 

 그리고 IPv4 주소는 주소의 수가 고갈될 수 있기 때문에 인터넷에 직접 연결되는 컴퓨터나 라우터에는 ISP가 제공하는 공인 IP 주소를 할당하고 회사나 가정의 랜에 있는 컴퓨터에는 사설 IP 주소를 할당한다. 

 

 예를 들어 아래 그림과 같이 랜 안에 컴퓨터가 여러대 있지만 공인 IP 주소를 컴퓨터 한 대당 할당하면 한정되어 있는 주소가 빨리 소진될 것이다. 따라서, 공인 IP 주소는 라우터에만 할당하고 랜 안에 있는 컴퓨터들에는 랜의 네트워크 관리자가 사설 IP 주소를 할당하거나 라우터의 DHCP(Dynamic Host Configuration Protocol) 기능을 사용하여 IP 주소를 자동으로 할당한다. 이런 방식으로 공인 IP의 주소를 절약할 수 있다. 

 

 


   IP 주소의 구조  

 

  데이터 링크 계층에서 MAC 주소는 48비트 였고 16진수로 표현한다. 반면에, 공인 IP 주소와 사설 IP 주소는 모두 32비트이며 10진수로 표현한다.

 

 IP 주소가 32비트이기 때문에 사람이 한 눈에 보기에는 어렵다. 따라서 32비트를 8비트 단위로 나눠서 표현하는데 이 때 8비트를 옥텟(Octet)이라고 부른다. 32비트를 4개의 옥텟으로 나누고 좀 더 읽기 쉽도록 10진수로 변환한다. 

 

 

 

 

 

    스프링부트에서 데이터베이스 다루기   

 

 스프링부트에서 데이터베이스를 다루는 방법에는 MyBatis와 같은 SQL 매퍼(SQL Mapper)를 이용해서 데이터베이스 쿼리를 작성하는 방법과 JPA와 같은 자바 표준 ORM(Object Relational Mapping)을 이용해서 객체지향적으로 프로그래밍 하는 방법이 있다. ORM은 객체를 매핑하는 것이고, SQL 매퍼는 쿼리를 매핑하는 것이다. 

 


 

    관계형 데이터베이스와 JPA   

 

 웹 애플리케이션을 개발하다보면 Oracle, MySQL과 같은 관계형 데이터베이스(Relational Database, RDB)를 많이 사용한다. 그러다 보니 객체를 관계형 데이터베이스에서 관리하는 것이 중요하다.

 

 관계형 데이터베이스가 웹 서비스의 중심이 되면 모든 코드는 SQL 중심으로 돌아간다. 즉, 애플리케이션 코드보다 SQL 코드가 더 많아진다는 것이다. 이는 관계형 데이터베이스가 SQL을 통해서만 인식하기에 각 테이블마다 기본적인 CRUD(Create, Read, Update, Delete) SQL을 매번 생성해야 한다. 핵심은 관계형 데이터베이스를 사용해야만 하는 상황에서는 매번 생성해야 하는 SQL을 피할 수 없다는 것이다. 

 

 이러한 SQL은 두 가지 문제점이 있는데 첫 번째는 위에서 설명한 CRUD SQL의 반복적인 작업을 해야하는 것이고 두 번째는 패러다임 불일치 문제이다. 패러다임 불일치 문제는 관계형 데이터베이스와 객체지향 프로그래밍 언어의 패러다임이 서로 다르다는 점에서 발생한다. 

 

 관계형 데이터베이스는 어떻게 데이터를 저장할지에 초점을 맞추는데 반해 객체지향 프로그래밍 언어는 기능과 속성을 한 곳에서 관리한다. 

 

 아래 코드는 객체지향 프로그래밍에서 부모가 되는 객체를 가져오는 방법이다. 

 

User user = findUser();
Group group = user.getGroup();

 

 여기서는 User와 Group의 관계가 부모-자식 관계임을 알 수 있다. group은 User가 속한 Group을 가져온 코드라고 알 수 있기 때문이다. 하지만, 여기서 관계형 데이터베이스가 추가된다면 다음과 같다.

 

User user = userDao.findUser();
Group group = groupDao.findGroup(user.getGroupId());

 

 여기서는 User와 Group을 따로 조회하기 때문에 User와 Group이 상속, 1:N 등 어떤 관계인지 알 수가 없다. 따라서, 다양한 객체 모델링을 데이터베이스로는 구현할 수 없다는 것이다. 

 

 JPA는 이러한 문제점을 해결하기 위해 등장했는데, 서로 다른 패러다임을 일치시켜주는 역할을 한다. 즉, 객체지향적으로 프로그래밍을 하면서 관계형 데이터베이스에 맞게 SQL을 대신 생성해서 실행해주는 역할을 한다. 객체 중심으로 개발을 하게 되면 생산성이 향상되고 유지 보수하기가 쉬워진다. 

 


 

    Spring Data JPA   

 

  JPA(Java Persistence API)는 인터페이스이면서 자바 표준 명세서이다. JPA를 사용하기 위해서는 Hibernate, Eclipse Link와 같은 구현체가 필요하다. 하지만 Spring에서는 JPA를 사용할 때 이 구현체를 직접 다루진 않는다. 

 

 구현체들을 좀 더 쉽게 사용하고자 추상시Spring Data JPA라는 모듈을 이용하여 다룬다. 이들의 관계는 다음과 같다.

 

 

 

  • 구현체 교체의 용이: Hibernate 외에 다른 구현체로 쉽게 교체하기 위함. 트렌드가 바뀌어 새로운 JPA 구현체가 떠오를 때, Spring Data JPA를 쓴다면 아주 쉽게 교체할 수 있다. 이는 Spring Data JPA 내부에서 구현체 매핑을 지원해주기 때문이다.
  • 저장소 교체의 용이: 관계형 데이터베이스 외에 다른 저장소로 쉽게 교체하기 위함. 초기에는 트래픽이 적어 모든 기능을 관계형 데이터베이스로 처리했지만 트래픽이 점점 많아져 감당이 안될 때 NoSQL로 교체가 필요하다. Spring Data JPA를 사용하면 의존성 교체만 해주면 된다. 이는 Spring Data의 하위 프로젝트들은 기본적인 CRUD의 인터페이스가 같기 때문에 가능하다. Spring Data JPA, Spring Data MongoDB, Spring Data Redis 등 하위 프로젝트들은 save(), findAll() 등을 인터페이스로 갖고 있다. 

 

  이러한 용이성들 때문에 Spring 팀에서도 Spring Data 프로젝트를 권장하고 있다. 하지만, 실무에서 JPA를 잘 사용하지 않은 이유는 높은 러닝 커브를 들 수 있다. 객체지향 프로그래밍과 데이터베이스를 둘 다 이해해야 하기 때문이다. 하지만 그만큼 JAP를 사용해서 얻는 보상이 크기 때문에 많이 바뀌고 있는 추세이다. 

   CPU의 기본 구성   

 

 CPU는 명령어를 해석하여 실행하는 장치로 산술논리 연산장치, 제어장치, 레지스터로 구성된다. 요리를 하는 과정에서 빗대어보면 요리사에 해당한다. 

 

  • 산술논리 연산장치(Arithmetic and Logic Unit, ALU): 데이터의 덧셈, 뺄셈, 곱셈, 나눗셈과 같은 산술 연산과 AND, OR 같은 논리 연산을 수행한다. 요리에 비유하면 요리사가 재료를 손질하는 것이다.
  • 제어장치(Control Unit): CPU에서 작업을 지시하는 부분으로 요리에 비유하면 요리사가 머릿속으로 '도마 위의 재료를 가져와라', '양파 껍질을 미리 벗겨 놓아라' 등의 지시가 이루어지는 것이다. 
  • 레지스터(Register): CPU 내에 데이터를 임시로 보관하는 곳이다. 요리에 비유하면 재료를 냄비에 필요한 만큼 잠시 넣어두는 것이다.

   CPU의 명령어 처리 과정   

 

 간단한 덧셈 프로그램으로 산술논리 연산장치, 제어장치, 레지스터가 어떻게 협업하는지 살펴보면 다음과 같다. 

 

int a = 2, b=3, sum;
sum = a + b;

 

 위의 소스코드는 사람이 인식할 수 있지만 CPU는 0과 1의 2진수로 이루어진 기계어만 인식하기 때문에 컴파일러를 통해 기계어로 바꿔야한다. 기계어는 사람이 인식하기 어려운 언어이므로 위의 소스코드를 어셈블리어로 바꾸어 보면 다음과 같다.

 

LOAD mem(100), register 2;
LOAD mem(120), register 3;
ADD register 5, register 2, register 3;
MOVE register, mem(160);

 

 CPU가 연산을 하려면 필요한 데이터를 CPU로 가져와 임시로 보관해야 하는데 이 때에는 레지스터를 사용하여 메모리 100번지와 120번지에 있는 값을 레지스터 2와 3으로 가져온다. 데이터가 준비되면 산술논리 연산장치에서 두 값을 더하여 그 결과값을 레지스터 5에 저장하고 레지스터 5에 저장된 값을 최종적으로 메모리의 160번지로 옮긴다. 

 

 제어장치는 위의 명령어들을 해석하여 제어 신호를 보내고 CPU 내의 데이터 흐름을 조절한다. 가령 위의 예에서 '메모리에서 데이터를 가져와라', '덧셈을 실행하라', '덧셈한 결과값을 메모리로 옮겨놔라'라는 신호이다. 아래 그림은 위의 소스 코드를 CPU의 명령어 처리 과정으로 나타낸 것이다. 

 


   레지스터의 종류   

 

 레지스터의 종류에는 여러 가지가 있는데, 위의 예에서 예를 들었던 레지스터는 데이터 레지스터(DR)와 주소 레지스터(AR)이다. 

 

  • 데이터 레지스터(Data Register): 메모리에서 가져온 데이터를 임시로 보관할 때 사용한다. CPU에 있는 대부분의 레지스터가 데이터 레지스터이기 때문에 일반 레지스터라고도 불린다.
  • 주소 레지스터(Address Register): 데이터 또는 명령어가 저장된 메모리의 주소를 저장한다. 

 이 밖에도 특별한 용도로 사용되는 레지스터도 있는데 이를 특수 레지스터라고 부른다. 이는 사용자가 임의로 변경할 수 없다.

 

  • 프로그램 카운터(Program Counter): CPU는 다음에 처리해야 할 명령어를 알아야 한다. 따라서, 프로그램 카운터는 다음에 실행할 명령어의 주소를 가리킨다. 명령어 포인터라고도 불린다.
  • 메모리 주소 레지스터(Memory Address Register): 메모리에서 데이터를 가져오거나 보낼 때 주소를 지정하기 위해 사용한다. 명령어를 처리하는 과정에서 필요한 메모리 주소를 저장한다.
  • 메모리 버퍼 레지스터(Memory Buffer Register): 메모리에서 가져온 데이터나 메모리로 옮겨 갈 데이터를 임시로 저장한다. 항상 메모리 주소 레지스터와 함께 동작한다. 

  위의 어셈블리어 코드 1행은 아래 그림과 같다.

 

  ① 프로그램 카운터에는 현재 실행 중인 코드의 행 번호 1이 저장되어 있고 이 번호는 제어장치에 전송된다.

  ② 제어장치가 명령어 레지스터에 있는 명령어를 해석하여 메모리에 있는 데이터를 가져오라는 제어 신호를 보낸다.

  ③ 메모리 주소 레지스터에는 100이 저장되고, 메모리 관리자는 메모리의 100번지에 저장된 값을 메모리 버퍼 레지스터로 가

      져온다.

  ④ 제어장치는 메모리 버퍼 레지스터에 저장된 값을 레지스터 2로 옮긴다. 

 

 

 

'운영체제 > 기초' 카테고리의 다른 글

7. 컴퓨터 성능 향상 기술  (0) 2020.10.14
6. CPU와 메모리(2)  (0) 2020.10.14
4. 하드웨어의 기본 구성  (0) 2020.10.05
3. 운영체제의 구조  (0) 2020.10.05
2. 운영체제의 역사  (0) 2020.09.28

   ARP란 무엇인가?   

 

 ARP(Address Resolution Protocol)목적지 컴퓨터의 IP 주소를 이용하여 MAC 주소를 찾기 위한 프로토콜이다. 이더넷 프레임을 전송하려면 목적지 컴퓨터의 MAC 주소를 알아야 하는데 출발지 컴퓨터가 목적지 MAC 주소를 알아내기 위해 네트워크에 브로드캐스트하는데 이것을 ARP 요청(ARP Request)라고 한다. 

 

 여기서 브로드캐스트(Broadcast)는 출발지 컴퓨터에 연결된 모든 컴퓨터에 요청을 보내는 방식을 말한다. 이 요청에 대해 지정된 IP 주소를 가지고 있지 않은 컴퓨터는 응답하지 않고 지정된 IP 주소를 가지고 있는 컴퓨터는 MAC 주소를 응답으로 보낸다. 이것을 ARP 응답(ARP Response)라고 한다. 이 때에는 유니캐스트를 사용한다.  유니캐스트(Unicast)는 자신의 MAC 주소와 목적지의 MAC 주소를 첨부하여 전송하는 방식을 말한다. 

 

 ARP 응답으로 출발지 컴퓨터는 MAC 주소를 얻게 되고 이더넷 프레임을 만들 수 있게 된다. 또한, 출발지 컴퓨터는 MAC 주소와 IP 주소의 매칭 정보를 메모리에 보관하는데 이를 ARP 테이블(ARP Table)이라고 한다. 이후 데이터 통신은 자신의 컴퓨터에 보관된 ARP 테이블을 참고하여 전송한다. 

 

 하지만 IP 주소가 변경되면 해당 MAC 주소도 함께 변경되므로 제대로 통신할 수 없다. 따라서 ARP 테이블에서는 보존 기간을 ARP 캐시로 지정하고 일정 기간이 지나면 삭제하고 다시 ARP 요청을 한다.

 

 윈도우에서 ARP 캐시의 내용을 확인하려면 cmd 창에서 arp -a 명령어를 사용하면 되고 ARP 캐시를 강제로 삭제하려면 arp -d 명령어를 사용하면 된다. 

 

 

[정리]

 

  1. ARP: 네트워크 계층 주소와 데이터 링크 계층 주소 사이의 변환을 담당하는 프로토콜이며 IP 주소를 물리 주소인 MAC 주소로 변환하는데 사용한다.
  2. ARP 캐시: 가장 최근에 변환한 IP와 하드웨어 주소(MAC 주소)를 매핑하려 보관하고 있는 램의 한 영역이다.
  3. ARP 요청: IP 주소를 대치할 수 있는 물리 주인 MAC 주소를 찾아내기 위해 보내는 브로드캐스트 패킷 요청이다.
  4. ARP 응답: ARP 요청에 대한 응답으로 요청한 IP 주소에 대한 물리 주소인 MAC 주소가 실려 있는 유니캐스트 패킷 응답이다.

 

 

 

 

    롬복(Lombok)이란?   

 

 롬복은 자바로 개발할 때 자주 사용되는 코드인 Getter, Setter, 기본 생성자, toString 등을 어노테이션으로 자동 생성해준다. 참고로 이클립스의 경우에는 롬복 설치가 번거롭지만, 인텔리제이에서는 플러그인 덕분에 쉽게 설정이 가능하다. 

 


 

    롬복(Lombok) 플러그인 설치하기   

 

 롬복 플러그인을 설치하기 전에 build.gradle에 다음의 코드를 추가해야 한다.

 

 

 

 build.gradle에 코드를 추가했다면 맨 오른쪽에 Gradle 버튼을 누르고 아래 빨간색으로 표시된 새로고침 버튼을 눌러주어 라이브러리(의존성)를 내려 받는다.

 

 

 라이브러리를 다 받았다면 롬복 플러그인을 설치해야 한다. 윈도우는 [ctrl + shift + A], 맥은 [command + shift + A]를 눌러  Action 탭을 선택한다. 그리고 plugins를 검색하고 선택한다. 그 후 Marketplace 탭에서 "lombok"을 검색하고 [install] 버튼을 눌러 설치한다. 설치가 완료되면 아래 오른쪽에 팝업창이 뜨면서 Enable annotation processing이 뜬다. 클릭 해주고 인텔리제이를 재시작 해준다.

 

 

 만약 팝업창이 뜨지 않았다면 [File]-[Settings]-[Build, Execution, Deployment]-[Compiler]-[Annotation Processor]로 가서 [Enable annotation processing]에 체크를 해준다.

 

 


 

    기존 코드 롬복으로 리팩토링 하기   

 

 먼저, main 폴더의 web 패키지에 dto 패키지를 추가하고 HelloResponseDto를 생성하고 다음과 같이 코드를 작성한다.

 

 

package com.daldalhada.springboot.web.dto;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class HelloResponseDto {
    private final String name;
    private final int amount;
}

 

 

1. @Getter

  • 선언된 모든 필드의 get 메소드를 생성해준다.

2. @RequiredArgsConstructor

  • 선언된 모든 final 필드가 포함된 생성자를 생성해 준다.
  • final이 없는 필드는 생성자에 포함되지 않는다. 

 

 

 그리고 잘 작동하는지 확인하기 위해 간단한 테스트 코드를 작성한다. test 폴더의 web 패키지에 dto 패키지를 추가하고 HelloResponseDtoTest를 생성하고 다음과 같이 코드를 작성한다.

 

 

 

package com.daldalhada.springboot.web.dto;

import org.junit.Test;
import static org.assertj.core.api.Assertions.assertThat;

public class HelloResponseDtoTest {
    @Test
    public void lombok_test() {
        //given
        String name = "test";
        int amount = 1000;

        //when
        HelloResponseDto dto = new HelloResponseDto(name, amount);

        //then
        assertThat(dto.getName()).isEqualTo(name);
        assertThat(dto.getAmount()).isEqualTo(amount);
    }
}

 

1. assertThat

  • assertj라는 테스트 검증 라이브러리의 검증 메소드로 검증하고 싶은 대상을 메소드 인자로 받는다.
  • 메소드 체이닝이 지원되어 isEqualTo와 같은 메소드를 이어서 사용할 수 있다.

2. isEqualTo

  • assertThat에 있는 값과 isEqualTo 값을 비교해서 같을 때만 성공한다.

※참고: 여기서 Junit의 기본 aseertThat이 아닌 assertj의 assertThat을 사용한 이유는 추가적인 라이브러리가 필요하지 않고 자동완성이 좀 더 확실하게 지원되기 때문이다. 

 

작성된 테스트 메소드를 실행해보면 정상적으로 기능이 수행되는 것을 알 수 있다. @Getter로 get 메소드가, @RequiredArgsConstructor로 생성자가 자동으로 생성되었다. 

 

 

 

이제 HelloController에도 새로 만든 ResponseDto를 사용하도록 코드를 추가한다.

 

package com.daldalhada.springboot.web;

import com.daldalhada.springboot.web.dto.HelloResponseDto;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @GetMapping("/hello")
    public String hello() {
        return "hello";
    }

    @GetMapping("/hello/dto")
    public HelloResponseDto helloDto(@RequestParam("name") String name,
                                     @RequestParam("amount") int amount) {
        return new HelloResponseDto(name, amount);
    }
}

 

1. @RequestParam

  • 외부에서 API로 넘긴 파라미터를 가져오는 어노테이션이다.
  • 여기서는 name이란 이름으로 넘긴 파라미터를 String name에 저장한다. amount도 마찬가지이다.

 

 이제 HelloController도 테스트를 하기 위해 HelloControllerTest에도 다음과 같이 코드를 추가한다. 

 

 

package com.daldalhada.springboot;

import com.daldalhada.springboot.web.HelloController;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;

import static org.hamcrest.Matchers.is;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;

@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HelloController.class)
public class HelloControllerTest {

    @Autowired
    private MockMvc mvc;

    @Test
    public void hello_Return() throws Exception {
        String hello = "hello";

        mvc.perform(get("/hello"))
                .andExpect(status().isOk())
                .andExpect(content().string(hello));
    }

    @Test
    public void helloDto_Return() throws Exception {
        String name = "hello";
        int amount = 1000;

        mvc.perform(
                get("/hello/dto")
                        .param("name", name)
                        .param("amount", String.valueOf(amount)))
                .andExpect(status().isOk())
                .andExpect(jsonPath("$.name", is(name)))
                .andExpect(jsonPath("$.amount", is(amount)));
    }
}

 

1. param

  • API 테스트할 때 사용될 요청 파라미터를 설정한다. (단, 값은 String 값만 허용)
  • 따라서 숫자/날짜 등의 데이터도 문자열로 변경해야 한다.

2. jsonPath

  • JSON의 응답값을 필드별로 검증할 수 있는 메소드
  • $를 기준으로 필드명을 명시한다. 여기서는 name과 amount를 $.name과 $.amount로 검증한다. 

 

 

 이제 추가된 API도 테스트를 실행해보면

 

 

 JSON이 리턴되는 API도 통과하는 것을 볼 수 있다.

 

   하드웨어의 구성  

 

 컴퓨터는 중앙처리장치(CPU), 메인메모리, 입출력장치, 저장장치로 구성된다. 여기서 중앙처리장치와 메인메모리필수장치로 분류된다. 나머지 부품은 주변장치라고 불린다. 

 

 메인메모리는 전력이 끊기면 데이터를 잃어버리기 때문에 데이터를 영구히 보관하려면 하드디스크나 USB 메모리를 사용해야 한다. 여기서 메인메모리는 제1저장장치(First Storage), 하드디스크나 USB 메모리는 제2저장장치(Second Storage) 또는 보조저장장치라고 부른다. 보통 메인메모리를 메모리, 보조저장장치를 저장장치로 지칭한다. 

 

   1. CPU와 메모리

      - CPU는 명령어를 해석하고 실행하는 장치로 인간의 두뇌에 해당한다. 

      - 메모리는 작업에 필요한 프로그램과 데이터를 저장하는 저장소로 바이트(Byte) 단위로 분할되어 있으며 분할 공간 마다

        주소로 구분한다. 

 

   2. 입출력장치

      - 입력장치는 외부의 데이터를 컴퓨터에 입력하는 장치로 키보드, 마우스, 스캐너, 터치스크린이 해당한다.

      - 출력장치는 처리한 결과를 사용자가 원하는 형태로 출력하는 장치로 프린터, 모니터, 스피커 등이 있다. 

 

   3. 저장장치

      - 메모리는 전자의 이동으로 데이터를 처리하지만 하드디스크나 CD와 같은 저장장치는 구동장치가 있는 기계이므로 속도

        가 느리다. 하지만, 저장장치는 메모리보다 저렴하고 용량이 크고 전원 꺼짐과 상관없이 데이터를 영구적으로 저장할 수

        있다. 대표적인 예로은 DVD, USB 드라이버, SD 카드, SSD 등이 있다.

 

  4. 메인보드

      - 컴퓨터의 다양한 부품은 버스로 연결된다. 버스는 일정한 규칙에 따라 각 장치에 데이터를 전송하는 역할을 한다. 즉, 버스

        는 각 장치를 연결하는 선의 집합이자 데이터가 지나다니는 통로이다. 

      - 메인보드는 CPU와 메모리 등 다양한 부품을 연결한 커다란 판으로 버스가 복잡하게 얽혀있다. 메인보드에는 각종 부품을

        꽂을 수 있는 단자가 있는데 이 단자에 그래픽카드, 사운드카드, 랜카드 등을 장착할 수 있다.

 

 

이미지 출처: 위키피디아


 

   폰노이만 구조  

 

 오늘날 대부분의 컴퓨터는 폰노이만 구조를 따르는데, 폰노이만 구조는 아래 그림과 같이 CPU, 메모리, 입출력장치, 저장장치가 버스로 연결되어 있는 구조를 말한다. 폰노리만 구조에서 가장 중요한 특징은 "모든 프로그램은 메모리에 올라와야 실행할 수 있다."는 것이다. 예를 들어, 워드 파일은 하드디스크에 저장되지만 프로그램과 데이터가 저장장치에서 바로 실행되지는 않는다. 저장장치에 있는 프로그램을 실행하려면 프로그램이 메모리에 올라와야 한다. 운영체제 또한 프로그램이기 때문에 메모리에 올라와야 실행이 가능하다. 

 

 

 

 


 

   하드웨어 사양 관련 용어  

 

  • 클록(Clock)은 CPU의 속도와 관련된 단위이다. CPU가 작업할 때 일정한 박자를 통해 수행하는데 이 박자를 만들어 내는 것이 클록이다. 클록이 일정 간격으로 틱(Tick)을 만들면 거기에 맞추어 CPU 안의 모든 구성 부품이 작업을 한다. 틱은 펄스(Pulse)라고도 한다. 버스에는 여러 개의 부품이 연결되어 있는데, 메인보드의 클록이 틱을 보낼 때마다 데이터를 보내거다 받는다.
  • 헤르츠(Hz)는 틱이 발생하는 속도를 나타내는 단위이다. 1초에 틱이 몇 번 발생하는지를 나타내는데, 1초에 클록틱이 한 번이면 1Hz, 1000번이면 1KHz이다. 버스도 틱에 맞추어 데이터를 전송하기에 메인보드의 성능을 클록(Clock)으로 표시한다. 
  • 시스템 버스는 메모리와 주변장치를 연결하는 버스로 전면 버스(Front-side Bus)라고 한다. CPU 내부 버스는 CPU 내부에 있는 장치를 연결하는 버스로 후면 버스(Back-side Bus)라고 한다. CPU는 CPU 내부 버스의 속도로 작동하고 메모리는 시스템 버스의 속도로 작동하기 때문에 두 버스의 속도 차이로 인해 작업이 지연되는 문제가 있다.(문제는 다음에 나온다.)

 

'운영체제 > 기초' 카테고리의 다른 글

6. CPU와 메모리(2)  (0) 2020.10.14
5. CPU와 메모리(1)  (0) 2020.10.06
3. 운영체제의 구조  (0) 2020.10.05
2. 운영체제의 역사  (0) 2020.09.28
1. 운영체제  (0) 2020.09.28

 

   커널과 인터페이스  

 

 커널(Kernel)프로세스, 메모리, 저장장치 관리와 같은 운영체제의 핵심적인 기능을 담당한다. 자동차에 비유하면 자동차 성능을 좌우하는 엔진과 같다. 

 

 그리고 자동차가 움직이는 데에 엔진은 물론이고 사람이 조작할 수 있는 핸들과 브레이크가 필요하다. 또한, 현재 시속, 기어 상태 등을 알려주는 계기판이 있어야 하는데 이를 인터페이스라고 한다. 운영체제에서는 커널에 사용자의 명령을 전달하고 실행 결과를 사용자에게 알려주는 역할을 한다. 

 

 

 

 정리하자면 사용자와 응용 프로그램에 인접하여 커널에 명령을 전달하고 실행 결과를 사용자와 응용 프로그램에게 돌려주는 인터페이스와 운영체제의 핵심 기능을 모아놓은 커널이 운영체제를 담당한다.

 

 또한, 같은 커널을 사용하더라도 다른 인터페이스를 사용하면 다른 운영체제처럼 보인다. 유닉스의 사용자 인터페이스를 셸이라고 하는데 C셸, T셸, 배시셸 등 다양하다. 이처럼 유닉스는 명령어 기반이기 때문에 일반인이 사용하기 불편하다. 하지만, 편리한 인터페이스를 자랑하는 MAC OS X도 유닉스 계얼의 커널을 이용해 만든 것이다. 이처럼 사용자 입장에서는 커널보다 인터페이스가 먼저 보이기 때문에 좋은 커널보다는 좋은 인터페이스의 컴퓨터를 사용하려는 경향이 있다. 


 

   시스템 호출과 디바이스 드라이버  

 

 시스템 호출(System Call)은 커널이 자기 자신을 보호하기 위해 만든 인터페이스이다. 커널은 사용자나 응용 프로그램으로부터 컴퓨터 자원을 보호하기 위해 저원에 직접 접근하는 것을 차단한다. 따라서, 자원을 이용하려면 시스템 호출이라는 인터페이스를 이용하여 접근해야 한다. 

 

  그렇다면 직접 접근하는 것과 시스템 호출을 통한 접근은 무엇이 다를까? 어떤 응용 프로그램은 dog를 다른 응용 프로그램은 cat을 하드디스크에 저장한다고 가정해보면

 

  •  직접 접근: 두 응용 프로그램이 자기 마음에 드는 위치에 데이터를 저장할 것이다. 하지만, 다른 사람의 데이터를 지우고 덮어쓸 수 있고 반대로 내 데이터가 다른 사람에 의해 지워질 수 있다.
  • 시스템 호출을 통한 접근: 커널이 제공하는 write() 함수를 사용하여 데이터를 저장해달라고 요청하면 응용 프로그램은 데이터가 하드디스크의 어느 위치에 어떤 방식으로 저장되는지 알 수 없다. 데이터를 찾고 싶다면 read() 함수로 시스템 호출을 통해 접근한다. 이 때에는 커널이 데이터를 저장하는 것에 전적으로 책임지기 때문에 컴퓨터 자원을 관리하기 수월하다. 

 

 응용 프로그램과 커널의 인터페이스가 시스템 호출이라면 커널과 하드웨어의 인터페이스는 드라이버(Driver)가 담당한다. 컴퓨터 하드웨어는 종류가 다양한데 운영체제가 많은 하드웨어를 사용할 수 있는 환경을 제공하려면 각 하드웨어에 맞는 프로그램을 직접 개발해야 한다. 하지만, 현실적으로 커널이 각각의 인터페이스를 다 개발하기에는 어려울 뿐더러 각 하드웨어의 특성은 하드웨어 제작자가 가장 잘 알기에 하드웨어 제작자가 관련 소프트웨어를 만드는 것이 더 유리하다.

 

 따라서, 커널은 입출력의 기본적인 부분만 제작하고 하드웨어의 특성을 반영한 소프트웨어를 하드웨어 제작자에게 받아 커널이 실행될 때 함께 실행되도록 한다. 이 때, 이 소프트웨어를 디바이스 드라이버라고 부른다.  

 

 

 정리하면 시스템 호출은 커널이 제공하는 시스템 자원의 사용과 관련된 함수이고 응용 프로그램이 하드웨어 자원에 접근하거나 운영체제가 제공하는 서비스를 이용할 때에는 시스템 호출을 통해 이용해야 한다. 드라이브는 커널이 제공하는 드라이버도 있고 하드웨어 제작자가 제공하는 드라이버도 존재한다. 따라서, 하드웨어는 커널과 직접 연결되기도 하고 제작자가 제공하는 드라이버를 통해 연결되기도 한다. 

 


 

   커널의 구성  

 

 운영체제의 핵심 기능인 커널은 다음과 같은 역할을 한다. 

 

 

 또한, 커널은 위의 기능들을 어떻게 구현하는지에 따라 단일형, 계층형, 마이크로 구조형 커널로 구분된다.

 

 단일형 구조 커널은 초창기의 운영체제 구조로 커널의 핵심 기능을 구현하는 모듈들이 구분 없이 하나로 구성되어 있는 것이다. 초기 운영체제는 기능을 구현하는데에 초점을 두었기 때문에 모듈을 분리하여 구현할 만한 여력이 없었다. 비유하자면 함수를 거의 사용하지 않고 main()에 모든 기능을 구현한 형태와 같다. 

 

 모듈 간 통신 비용이 준다는 장점이 있지만 모듈이 하나로 묶여 있기에 버그나 오류를 처리하기 어렵고 기능상의 작은 결함이 시스템 전체로 확산될 수 있다. 

 

 

 계층형 구조 커널은 비슷한 기능을 가진 모듈을 묶어서 하나의 계층을 만들고 계층 간의 통신을 통해 운영체제를 구현하는 방식이다. 단일형 구조 커널보다는 버그나 오류를 쉽게 처리할 수 있고 해당 계층만 수정하면 되기에 디버깅이 쉽다. 오늘날의 운영체제는 대부분 이 구조로 되어 있다.

 

 

마이크로 구조 커널은 프로세스 관리, 메모리 관리, 프로세스 간 통신 관리 등 가장 기본적인 기능만 제공한다. 다른 커널에 비해 운영체제의 많은 부분이 사용자 영역에 구현되어 있다. 마이크로 구조에서 각 모듈은 독립적으로 작동하기 때문에 하나의 모듈이 실패하더라도 전체 운영체제가 멈추지 않는다. 또한, 다른 컴퓨터에 이식하기 쉽고 가벼워 CPU 용량이 작은 시스템에도 적용이 가능하다. 

 


 

   가상머신  

 

 C 언어는 어셈블리어 같은 저급 언어의 기능을 가져 시스템 프로그래밍 언어로 적합하고 대표적인 운영체제는 유닉스이다. 하지만, C 언어는 다른 커널을 가진 운영체제와 호환성이 떨어진다. 여기서 호환성이 떨어진다는 것은 한 쪽에서 만든 소스코드가 다른 운영체제에서는 작동하지 않는다는 것을 말한다. 예를 들어, 유닉스에서 C언어로 만든 코드가 윈도우 운영체제에서 작동하지 않는 것이 그 예에 해당한다. 

 

 이러한 호환성 문제를 해결한 언어가 자바(Java)인데 자바로 프로그래밍을 하면 대부분의 운영체제에서 작동한다. 자바가 작동하는 원리는 운영체제 위에 가상머신(Virtual Machine)을 만들고 그 위에서 응용 프로그램이 작동하는 것이다. 즉, 가상머신은 서로 다른 운영체제에서 하나의 응용 프로그램으로도 작동할 수 있도록 만든 시스템이다.

 

 

 가상머신을 설치하면 응용 프로그램이 모두 동일한 환경에서 작동하는 것처럼 보인다. 따라서, 개발자가 하나의 코드만 만들면 여러 운영체제에서 똑같이 실행할 수 있다. 자바에서 가상머신을 만들어 배포한 것을 자바 가상머신(Java Virtual Machine, JVM)이라고 한다. 

 

 만약 하나의 컴퓨터에 두 가지 운영체제를 설치하기가 부담스러울 경우 가상머신을 설치하면 해결된다. 가상머신을 사용하면 호환성이 높아지지만 응용 프로그램이 가상머신을 통해서만 작동하기 때문에 느리다는 단점이 있다. 

 

'운영체제 > 기초' 카테고리의 다른 글

6. CPU와 메모리(2)  (0) 2020.10.14
5. CPU와 메모리(1)  (0) 2020.10.06
4. 하드웨어의 기본 구성  (0) 2020.10.05
2. 운영체제의 역사  (0) 2020.09.28
1. 운영체제  (0) 2020.09.28