CS

1장. 컴퓨터 시스템 (Computer Systems A Programmer's Perspective)

Jerry_K 2024. 9. 8. 23:31

🔖1.1 정보는 비트와 컨텍스트로 이루어진다. 

소스 프로그램은 0 또는 1로 표시되는 비트들의 연속으로, 바이트라는 8비트 단위로 구성된다. 각 바이트는 프로그램의 텍스트 문자로 나타낸다. 그리고 대부분의 컴퓨터 시스템에서는 텍스트 문자를 아스키(ASCII) 표준을 사용하여 표시한다. 오로지 아스키 문자들로만 이뤄진 파일들을 텍스트 파일이라고 부른다. (이외의 다른 모든 파일들을(실행파일 등) 바이너리 파일이라고 한다.) 

+ 최근에는 아스키 대신 유니코드가 많이 사용되고 있다.

 

모든 시스템 내부의 정보 (데이터,네트워크,프로그램 등)는 비트들로 표시되는데, 서로 다른 객체들을 구분하는 유일한 방법은 이들을 바라보는 컨텍스트에 의해서이다. 여기에서 컨텍스트는 저장매체, 프로세서의 구조, 파일 형식, 네트워크 프로토콜 등 다양한 요소들에 의해 결정되는 상활적 조건이라고만 생각하자.

 

 

🔖1.2 프로그램은 다른 프로그램에 의해 다른 형태로 번역된다.

hello.c를 시스템에서 실행시키려면, 각 C 문장들은 다른 프로그램들에 의해 저급 기계어 인스트럭션(명령어)들로 번역되어야한다. 

컴파일 시스템전처리 + 컴파일러 + 어셈블러 + 링커 를 합친 것이다.

 

해당 그림이 4단계의 컴파일 시스템이다. 

 

vim 에디터를 통해서 각 컴파일 시스템 단계를 수행해보고 살펴보자. 

(맥북으로 작성 / 윈도우 경우 따로 설치해야함) 

 

가장 먼저 vim 에디터를 실행시켜 .c 파일은 만들어보자. 

vi hello.c

 

그리고 간단하게 c언어 코드를 작성해주었다. 

#include <stdio.h>
int main(){
	printf(“hello,jerry”);
	return 0;
}

 

* 아래 과정으로부터 만들어진 파일들을 vim으로 꼭 확인해보자 ! 

 

1. 전처리 단계 :

gcc -E hello.c -o hello.i

 

전처리기는 .c 파일을 .i 전처리 파일로 만들어준다. 

전처리 과정에서 주석 제거, 매크로 확장(#define), 파일 포함(#include에 따른), 조건부 컴파일(#ifdef) 등의 작업을 수행한다. 

컴파일러가 이해할수 있는 형식으로 코드를 변환하여 실제 컴파일이 원활하게 진행될 수 있도록한다.

 

2. 컴파일 단계 :  

gcc -S hello.c -o hello.s

 

컴파일러는 .i 파일을 .s로 번역하여 어셈블리어 프로그램으로 저장된다. 해당 프로그램에서는 저수준 기계어 명령어텍스트 형태로 나타내고 있다. 

 

3. 어셈블리 단계

gcc -c hello.c -o hello.o

어셈블러가 hello.s를 기계어 인스트럭션(명령어)로 번역하고, 목적프로그램의 형태로 묶어서 목적파일에 그 결과를 저장한다. 

해당 파일을 텍스트 편집기로 열어보면 쓰레기 같은 데이터가 보인다. 

(해당 파일은 사람에게 보여주기 위한게 아니라 기계를 위해 기계어로 번역된 파일이다.)

 

hexdump -C hello.o

 

만일  기계어로 된 바이너리 파일을 보려면 해당 코드를 입력하면 된다. 

 

4. 링크 단계 :

gcc hello.o -o hello

printf는 이미 컴파일된 별도의 목적파일인 pritnf.o에 들어있으며, 이 파일은 hello.o 파일과 어떠한 형태로든 결합되어야한다. 

그리고 통합작업을 링커 프로그램 (ld)에서 수행한다. 

통합된 hello 파일은 메모리에 적재되어 시스템에 의해 실행된다. 

 

모듈화, 재사용성, 컴파일 시간 단축, 메모리 관리, 링크 시점에 결정되는 의존성, 디버깅 용이성 등의 이유로 목적 파일은 분리한다. 

결국에는 시간 복잡도와 공간 복잡도 측면 !

 

gcc -o hello hello.c

사실 위의 코드로 바로 컴파일 시스템을 처리 가능하다. 

 

 

🔖1.3 컴파일 시스템이 어떻게 동작하는지 이해하는 것은 중요하다.

- 프로그램 성능 최적화 

- 링크 에러 이해하기 

- 보안 약점 피하기

 

위와 같은 이유로 개발자로서 어떻게 컴파일 시스템이 동작하는지를 이해하는 것은 중요하다. 

 

 

🔖1.4 프로세스는 메모리에 저장된 인스트럭션을 읽고 해석한다. 

쉘은 명령어 라인을 입력 받아 그 명령어을 실행한다. 만일 명령어 라인이 내장 쉘 명령어가 아니면 쉘은 실행파일의 이름으로 판단하고 그 파일을 로딩해서 실행해 준다. 

 

전형적인 시스템의 하드웨어 구성

컴퓨터 시스템의 구성요소는 크게 CPU, 버스, 메모리, 입출력장치라고 할 수 있다. 간략하게 해당 시스템에 대해 알아보자.

 

🔗 버스 (Buses)

버스는 컴포넌트들 간에 바이트 정보를 주고 받는 통로이다. 일반적으로 워드 단위로 데이터를 전송하도록 설계된다. 

여기에서 말하는 워드는 CPU가 한번에 처리할 수 있는 단위이다.  요즘 대부분 컴퓨터는 32비트 또는 64비트의 워드 크기를 갖는다. 

 

🔗 입출력 장치

입출력 장치는 입출력 버스와 컨트롤러나 어댑터를 통해 연결된다. 입출력 장치로 시스템과 외부세계와의 연결을 할 수 있다. 

말은 거창하게 썼는데 우리가 흔히 생각하는 키보드,마우스이다 . 

 

🔗 메인 메모리 

메인 메모리는 프로세서가 프로그램을 실행하는 동안 데이터와 프로그램을 모두 저장하는 임시 저장장치이다. 

물리적으로 메인 메모리는 DRAM 칩들로 구성되어 있다.  (캐시는 일반적으로 SRAM을 사용)

 

🔗  프로세서 (CPU)

CPU는 메인 메모리에 저장된 인스트럭션들을 해독하는 엔진이다. 

프로세서 구성에는 레지스터,ALU, 제어장치가 있다. 

 

프로세서는 시스템에 전원이 공급되는 순간부터 전원이 끊어질 때까지

PC(프로그램 카운터)가 가리키는 곳의 인스트럭션을 반복적으로 실행하고,

PC 값이 다른 인스터럭션의 위치를 가리키도록 업데이트 한다. 

(프로세서는 자신의 ISA로 정의되는 매우 단순한 실행 모델을 따라 작동하는 것처럼 보인다.)

 

 

1. 저장(Store) : 레지스터에서 메인 메모리로 한 바이트 또는 워드를이전 값에 덮어쓰는 방식으로 복사

int a = 10; // 10이 메모리에 저장

 

2. 적재(Load) : 메인 메모리에서 레지스터에 한 바이트 또는 워드를 이전 값에 덮어쓰는 방식으로 복사

int a = 10;
int b = a;  // a의 값이 메모리에서 로드되어 b에 할당

 

3. 작업(Operate) : 두 레지터스 값을 ALU로 복사하고 두 개의 워드로 수식연산을 수행한 뒤, 결과를 덮어쓰는 방식으로 레지스터에 저장

int a = 10;
int b = 20;
int sum = a + b;  // 덧셈 작업

 

4. 점프(Jump) : 인스트럭션(명령어) 자신으로부터 한개의 워드를 추출하고, 이것을 PC에 덮어쓰는 방식으로 복사 

int i = 0;
label:  // jump target
    if (i < 5) {
        printf("%d\n", i);
        i++;
        goto label;  // label로 점프
    }

 

기본적으로 명령어를 순차적으로 실행하지만, 단순히 순차 실행으로만 이뤄지지 않는다.

점프를 통해 유연하게 구성 할 수 있다. 

 

(C언어에서 goto 명령이 jmp 명령을 그대로 표현한 것이라 한다. 예전에는 goto 명령을 쓰지말라고 하였지만, 요즘 트렌드는 에러 처리를 위해 goto를 쓰라고도 한다. 프로세서가 많이 발전했기 때문에 goto 명령으로 인한 성능 저하가 없고 오히려 코드 이해와 에러에 잘 대처할 수 있다고 한다. )

 

🔧 hello 프로그램 실행

쉘 프로그램은 사용자가 명령을 입력하기를 기다린다. 

".\hello"를 입력하면 쉘 프로그램은 각각의 문자를 레지스터(선)에 읽어 위의 그림처럼 메모리(후)에 저장한다.

 

 

 

키보드에 엔터키를 누르면 쉘은 명령 입력을 끝마쳤다는 것을 알게 된다.

쉘은 파일 내의 코드와 데이터를 복사하는(읽어들이는) 인스트럭션을 실행하여 DMA 기법을 사용해 실행파일 hello를 디스크에서 메인 메모리로 로딩한다. 장치와 메모리 간의 데이터 전송은 CPU가 직접 데이터를 복사하지 않는 DMA  구조를 사용하는게 효율적이다.

 

 

   

hello 목적파일의 코드와 데이터가 메모리에 적재된 후, 프로세서는 hello 프로그램의 main 루틴의 기계어 인스트럭션(명령어)를 실행한다. 해당 인스트럭션(명령어)들은 메모리로부터 레지스터 파일을 복사하고, 거기로부터 디스플레이 장치로 전송하여 화면에 글자들이 표시된다. 

 

 

 

🔖1.5 캐시가 중요하다. 

캐시 메모리 저장장치는 프로세서-메모리 간 격차에 대응하기 위함이다. 

 

L1 캐시는 대략 수천 바이트 데이터를 저장하고,

L2 캐시는 수백 킬로 바이트에서 메가 바이트 용량을 가지며 프로세서와 전용 버스를 통해 연결된다.

보통 L1,L2 캐시는 SRAM(Static Random Access Memory)를 사용한다.

 

보통 캐시에서 프로세서가 단기간에 필요로 할 가능성 높은 정보를 임시 저장한다

(자주 접근하거나 앞으로 접근 할 가능성 높은 데이터 저장)

 

이때 캐시 시스템은 지역성을 활용하여 매우 크고 빠른 메모리 효과를 얻는다.

여기에서 지역성은 어떤 특정한 메모리 주소를 참조한 후 그 근처의 주소들을 다시 참조할 확률이 높다.

 

 

지역성에는 시간적 지역성공간적 지역성이 있는데, 코드 예시를 봐보자.

for (int i = 0; i < 1000; i++) {
    sum += array[i];
}

 

시간적 지역성은 최근에 접근한 데이터나 명령어는 가까운 시점에 다시 접근될 가능성이 높은 원리이다.

→ 해당 코드에서 시간 지역성으로 i와 sum이 캐시에 저장 될 가능성이 높다. 

 

공간지역성은 특정 메모리 주소에 접근했을 때,

그 주소 근처에 있는 데이터나 명령어로 접근할 가능성이 높다는 원리이다. 

해당 코드에서 공간 지역성으로 array[i+1], array[i+2] 등이 캐시에 저장 될 가능성이 높다. 

 

 

 

🔖1.6 저장장치들은 계층구조를 이룬다.

 

프로세서에는 작고 빠른 저장장치를, 메인 메모리에서는 크고 느린 저장장치를 끼워 넣는 개념이 일반적인 아이디어이다.

모든 컴퓨터 시스템의 저장장치들은 메모리 계층구조로 구성되어 있다. 해당 구조의 주요 아이디어는 한 레벨의 저장장치

가 다음 하위레벨 저장장치의 캐시 역할을 한다는 것이다. 

 

일부 분산 파일시스템을 가지는 네트워크 시스템에서 로컬 디스크는 캐시 역할을 수행한다.

처음 요청된 데이터는 네트워크를  통해 서버에서 클라이언트로 전송이되고,

클라이언트는 자주 사용하는 데이터를 로컬 디스크에 캐시해 둔다.

 

이렇게 할 경우 저장된 데이터는 네트워크 트래픽을 줄이고,

다음번 동일한 데이터가 필요할 때 더 빠르게 접근 할 수 있다.

(캐시를 명사가아닌 동사의 느낌으로 받아보자)

 

💡레지스터는 일반적으로 플립플롭을 이용한 SRAM 구조로 구성되어있다. 
플립플롭은 1비트의 데이터를 안정적으로 저장 (전원이 공급되는 동안 안정적으로 값 유지) 할 수 있고,
64비트 레지스터는 64개의 플립플롭으로 구성된다. 



💡SRAM은 데이터 유지를 위해 지속적으로 전력이 필요하지만 데이터를 안정적으로 저장하고,
DRAM은 시간이 지나면 전하가 소멸되어 주기적으로 데이터를 리프레시 해야한다. 

 

 

🔖1.7 운영체제는 하드웨어를 관리한다.

 

응용 프로그램은 키보드나 디스플레이, 디스크나 메인 메모리를 직접 액세스하지 않는다. 응용프로그램이 하드웨어를 제어하려면 언제나 운영체제를 통해서 해야한다. 

 

[운영체제의 두 가지 주요 목적]

1. 제멋대로 동작하는 응용프로그램들이 하드웨어를 잘못 사용하는 것을 막기 위해

2. 응용프로그램들이 단순하고 균일한 매커니즘을 사용하여 복잡한 저수준 하드웨어 장치들을 조작할 수 있도록 하기위해

 

위의 Figure 1.11에서와 같이 운영체제파일을 입출력장치의 추상화,

가상메모리를 메인 메모리와 디스크 입출력장치의 추상화,

프로세스를 프로세서, 메인메모리, 입출력장치의 추상화한다.

여기서 추상화라는 개념이 잘 잡히지 않았다.

 

🔎 파일: 입출력장치 추상화
운영체제가 다양한 저장장치(HDD,SSD 등)을 하나의 파일 시스템으로 통합하고,
디스크의 원시 데이터를 파일이라는 형태로 추상화한다. 
파일은 이름을 통해 관리되고, 사용자는 데이터를 저장,읽기,수정,삭제를 할 수 있다. 

🔎가상메모리: 메인 메모리와 디스크 입출력장치 추상화
가상 메모리는 각 프로세스에 물리 메모리와는 독립적인 가상 주소 공간을 제공한다.
프로세스는 실제 물리 메모리 주소를 몰라도, 자신만의 가상 메모리 주소를 사용한다.
운영체제는 가상 주소와 물리적 메모리 주소 간의 매핑을 관리한다.

🔎프로세스: 프로세서, 메인메모리, 입출력장치  추상화
사용자가 프로그램을 실행하면, 운영체제는 메모리에 프로그램의 코드를 로드하고,
필요한 자원을 할당하여 하나의 프로세스를 만든다. 
사용자가 하드웨어에 직접 접근하지 않고도 프로그램을 실행하고 자원을 관리할 수 있게 한다. 

 

 

✨ 프로세스

 

새로운 프로그램을 실행하라는 명령은 받으면,  시스템 콜을 호출하여 운영체제로 제어권을 넘겨준다.

운영체제는 쉘의 기존의 컨텍스트를 저장하고 새로운 프로세스의 컨텍스트를 생성한 뒤 제어권을 새로운 프로세스로 넘겨준다. 종료되면 기존 컨텍스트를 복구시키고 다시 제어권을 쉘에게 넘겨준다. 

 

 

다수의 프로세스들동일한 시스템에서 동시에 실행될 수 있으며,  각 프로세스는 하드웨어를 배타적으로 사용하는 것 처럼 느낀다.

 

운영체제는 Context switching (문맥 전환)의 방법으로 이러한 교차실행을 수행한다.

현재 프로세스에서 다른 프로세스로 제어를 옮기려고 할 때,

현재 프로세스의 컨텍스트를 저장하고 새 프로세스의 컨텍스트를 복원시키는 Context switching을 실행한다. 

 

Context switching 같은 경우 운영체제의 커널에 의해 관리된다.

커널은 운영체제 코드의 일부분으로 메모리에 상주하고,

응용프로그램이 운영체제에 어떤 작업을 요청하면,

컴퓨터는 시스템 콜을 실행해서 커널에 제어를 넘겨준다.   

(운영체제로 제어권을 넘겨준다와 같은 말 ! )

 

커널은 별도의 프로세스가 아니고, 프로세스를 정의하는 코드와 자료구조의 집합이다.

 

💡 스터디를 하면서 나온 얘기가있다 . 그렇다면 OS는 프로세스인가 ? 
결론만 말하자면 OS는 프로세스라 볼 수 없다. 프로세스는 OS 자체에서 정의,생성,관리하는 것이다. 
해당 내용에 대해 약간의 의견이 갈리긴 하지만, 너무 깊게 생각하지는 않아도 될 것 같다. 
그 자체의 운영체제의 의의를 아는게 더 중요한 것 같다. 

 

[운영체제도 프로세스인가 ? - reddit]   https://www.reddit.com/r/computerscience/comments/19c9sco/is_an_operating_system_a_process_itself/

 

 

✨ 쓰레드

쓰레드프로세스 내에서 실행되는 하나의 작업 단위 또는 CPU 수행의 기본 단위이다.

각각의 쓰레드는 프로세스의 컨텍스트에서 실행된다.

프로세스는 실제로 쓰레드라고 하는 다수의 실행 유닛으로 구성되어 있다. 

 

운영체제는 쓰레드가 실행될 때

각각의 쓰레드에게 스택 공간과 프로그램 카운터를 할당하고,

힙 공간, 코드 영역, 데이터 역역을 공유한다. 

 

독립적인 실행 환경을 제공한다면, 쓰레드는 프로세스 내에서 병렬로 여러 작업을 처리할 수 있도록한다.

(다수의 프로세스들에서보다 데이터의 공유가 쉽고, 쓰레드가 프로세스보다 더 효율적)

 

 

 

✨ 가상메모리

가상 메모리는 실제 물리적 메모리(RAM)의 한계를 극복하고, 더 많은 메모리를 사용하게 해준다.

실제 물리 메모리보다 더 큰 메모리 공간을 사용하도록 하며

프로그램이 자신의 전체 메모리를 독립적으로 사용할 수 있도록 해준다. 

(독립적인 공간을 사용하여 메모리 충돌 방지)

 

가상메모리는 물리 메모리를 페이지 단위로 나누어 관리하고,

필요한 페이지는 실제 메모리에 적재되고, 나머지는 디스크에 저장된다.

(자주 사용하는 데이터만 실제 메모리에 유지)

 

만일 프로그램이 메모리에 없는 데이터를 접근하려 하면,

운영체제는 페이지 폴트라는 예외를 처리해서 필요한 데이터를 디스크에서 불러와 메모리에 적재한다. 

 

많은 프로그램을 동시에 실행하면 실제 물리 메모리가 부족할 수 있는데,

이때 가상 메모리를 통해 운영체제가 각 프로그램이 자신만의 충분한 메모리 공간을 가진 것처럼 보이게 한다. 

 

 

[User Space]

🔗 프로그램 코드와 데이터:

운영체제는 프로세스가 실행될 때 각 프로세스마다 고유한 가상 메모리 공간을 제공하는데,

일반적으로 가상 메모리 공간은 모든 프로세스가 동일한 시작 주소를 가지는 형태로 설정된다.

코드와 데이터 영역은 실행 가능 목적파일로부터 초기화된다. 

 

🔗 힙: 

힙은 동적으로 메모리를 할당하는 영역이다. 

프로세스가 실행되면서 C 표준함수 malloc이나 free를 호출하여

런타임에 동적으로 크기가 늘었다 줄었다 한다. 

사용자가 직접 관리 해야만 하는 메모리 영역으로 주의가 필요하다.

 

🔗 공유 라이브러리:

주소공간의 중간 부근에 C 표준 라이브러리나 수학 라이브러리와 같은

공유 라이브러리의 코드와 데이터를 저장하는 영역이 있다.

→  코드 재사용성을 높이고, 메모리 효율성을 개선

 

🔗 스택 :

스택은 함수 호출 시 지역 변수나, 함수 인자 등을 저장하는 메모리 영역이다. 

호출에 따라 메모리가 늘어나고, 반환되면 메모리가 해제되는 방식으로 크기가 동적으로 변화된다. 

(보통 운영체제에 의해 스택 최대 크기 제한)

 

[Kernel Space]

 

🔗 커널 가상 메모 :

운영체제의 커널이 사용하는 메모리 공간으로, 프로세스의 주소 공간과는 별도로 관리된다.

주소 맨윗 부분은 커널을 위해 예약 되어있다. 

 

응용프로그램들은 이 영역의 내용을 읽거나 쓰는 것이 금지되어 있으며,

커널 코드 내에 정의된 함수를 직접 호출하는 것도 금지이다. 

 

커널 공간은 운영체제가 시스템 호출이나 인터럽트를 처리하는데 필요한 코드와 데이터를 저장한다. 

각 프로세스는 커널에 접근할 수 있는 가상 주소 공간을 가진다.

(가상 주소는 물리적 메모리 주소와 매핑되어 운영체제가 효율적으로 메모리를 관리한다.)

 

 

✨ 파일

디렉토리, Kernel, 프로그램 등 모든 것이 다 파일이다.

 

디스크, 키보드, 디스플레이 등 모든 입출력장치는 파일로 모델링한다.

그렇기 때문에 모든 I/O 디바이스는 파일로 표현할 수 있다.

(리눅스에서 /dev 디렉토리에서 장치 파일 찾을 수 있음)

 

파일은 그저 연속된 바이트들이다.

시스템의 모든 입출력은 유닉스 I/O 시스템콜을 이용하여 파일을 읽고 쓴다.

이러한 기술은 다양한 입출력장치들의 통일된 관점을 제공한다.

(예를 들어 SSD에서 HDD로 디스크 기술이 바뀌어도 동일한 프로그램을 사용할 수 있다.)

 

 

 

 

🔖1.8 시스템은 네트워크를 사용하여 다른 시스템과 통신한다. 

개별 시스템의 관점에서, 네트워크는 또 다른 입출력장치로 볼 수 있다. 

데이터가 메인 메모리에서 로컬디스크 드라이브로 이동하듯, 

데이터가 네트워크를 통해 다른 컴퓨터로 이동하는 것이 통신이다.

 

인터넷과 같은 글로벌 네트워크의 출현으로 다른 컴퓨터로

정보를 복사하는 것이 가장 중요한 컴퓨터의 응용이 되었다.

ex) 이메일, 메신저, 웹 페이지, FTP, Telnet 등 

 

 

 

Telnet 프로토콜로 네트워크의 예시를 봐보자 

1. "hello" 스트링을 Telnet 클라이언트에 입력하고 enter 키를 누른다.
2. 클라이언트 프로그램은 스트링을 Telnet 서버로 보낸다.
3. Telnet 서버가 네트워크에서 스트링을 받은 후에, 원격 쉘 프로그램에 전달한다.
4. 원격 쉘은 hello 프로그램을 실행하고 출력을 다시 Telnet 서버로 전달한다.
5. Telnet 서버는 hello world 스트링은 클라이언트에게 전달한다.
6. 클라이언트 프로그램은 출력 스트링을 자신의 로컬 터미널에 표시한다. 

 

 


 

➕ TCP / UDP 개념 

위에 FTP 개념이 나왔으니, 관련 부분에 대해 좀만 자세하게 확인해보자.

(TCP 위에는 HTTP, FTP, Telnet, SSH 등이 있다.)

 

 

TCP(Transmission Control Protocol) 와 UDP(User Datagram Protocol)은

데이터 전송을 위한 두가지 기본적인 프로토콜이다.

 

[TCP]

TCP 같은 경우 신뢰성 높은 데이터를 전송한다.

(데이터의 순서 보장, 흐름 제어 등은 데이터 전송의 안전을 보장)

TCP 헤더에는 소스 및 대상 포트 번호, 순서 및 승인 번호, 플래그, 윈도우 크기 등과 같은 필수 정보가 있다. 

 

TCP 연결 지향 프로토콜로 데이터를 보내기 전

송신자와 수신자 간에 연결을 설정해야한다. 

 

- Telnet 프로토콜

사용자가 네트워크를 통해 원격 컴퓨터에서 접속하여 작업을 수행할 수 있다.

모든 작업과 응답이 텍스트 형태로 이뤄진다. 

Telnet 클라이언트가 Telnet 서버에 연결된다. 

→ 보안상 문제로 현대에는 사용이 줄고 SSH로 대체되고 있다. 

 

- FTP

파일 전송을 위해 설계된 표준 네트워크 프로토콜이다. 

웹 서버에 파일을 업로드하거나 서버에서 파일을 다운로드하는 데 널리 사용된다. 

FTP는 클라이언트-서버 모델을 기반으로 사용자 인증을 통해 보안을 제공한다. 

→ 데이터가 암호화되지 않아 보안 측면에 문제가 있어서, FTPS 같은 프로토콜 대안 

 

- SMTP

단순 이메일을 보내고 전달하는 데 사용되는 인터넷 표준 프로토콜이다.

 

- HTTP

웹 서버와 클라이언트 간에 웹 페이지와 다른 리소스를 주고받기 위해 사용된다.

클라이언트가 요청을 보내면 서버는 이에 대한 응답을 보낸다.

 

 

[UDP]

UDP 같은 경우 빠르고 가벼운 전송을 목적으로 한다. 

송신자와 수신자 간에 미리 연결을 설정하지 않고 바로 데이터를 전송한다. 

또한 UDP 헤더는 TCP 해더보다 훨씬 간단해서 네트워크 부하가 줄어든다.

 


 

🔖1.9 중요한 주제들

 

✨ 동시성과 병렬성

동시성(Concurrency) : 

논리적 개념으로 동시에 실행되는 것 같이 보인다. 

싱글 코어에서 멀티 쓰레드를 동작 시키는 방식이다.

 

병렬성 (Parallelism) :

물리적인 개념으로 식제로 동시에 여러 작업이 처리된다.

멀티코어에서 멀티 쓰레드를 동작시키는 방식이다. 

 

[쓰레드 수준 동시성]

쓰레드를 이용하면 한 개의 프로세스 내에서 실행되는 다수의 제어흐름을 가질 수도 있다.

멀티코어 프로세서는여러 개의 CPU를 하나의 집적화된 칩에 내장하고 있다.

 

어떤 시스템이 여러 개의 프로세서를 가지고 하나의 운영체제 커널의 제어 하에 동작하는 경우를 멀티프로세서 시스템이라고 한다. 이 경우 두개 이상의 물리적 CPU가 장착되어있고, 각 CPU는 독립적으로 작동하며 서로 협력하여 작업을 병렬로 처리한다. 

 

멀티코어 프로세서는 하나의 CPU 칩에 여러개의 물리적 코어를 내장하는 방식이다. 하나의 CPU 칩 안에 여러 개의 독립적인 물리적코어가포함되어 있고 각 코어는  별도의 명령을 처리할 수 있다. 병렬 처리 성능이 향상시키고, 독립적으로 명

령을 처리하여 멀티태스킹 성능이 뛰어나다. 

 

하이퍼 쓰레딩 같은 경우 하나의 물리적 코어가 두개의 논리적 코어처럼 동작하여 동시에 두 개의 쓰레드를 처리할 수 있도록 도와준다.  물리적 코어 하나가  두 개의 쓰레드를 처리하지만 자원을 나눠 쓰기 떄문에 성능이 완벽이 두 배로 증가하지 않는다.  (멀티코어와 병행해서 사용하면 효과가 극대화된다.) CPU가 여러 개의 제어 흐름을 실행할 수 있도록 해준다.  

 

기존의 프로세서가 쓰레드들 간의 전환을 하는 데 약 2만클럭 사이클이 요구되는 반면,

하이퍼쓰레드 프로세서에서는 매 사이클마다 실행할 쓰레드를 결정한다. 

예를 들어 어느 쓰레드가 데이터를 캐시에 로딩하기 위해 대기해야 하면, 

CPU는 다른 쓰레드를 실행할 수 있다. 

 

(다 비슷해보이지만 다른 의의를 가지고 있다.)

 

해당 사진은 인텔 i7 프로세서 코어의 구성이다. 

이 프로세서 칩은 4개의 CPU 코어를 가지고 있다. 

 

 

멀티프로세싱의 이용 

1. 동시성을 시뮬레이션할 필요를 줄여준다. 

2. 멀티프로세싱으로 프로그램이 병렬로 효율적으로 실행할 수 있는 멀티쓰레드형태

 

 

[인스트럭션 수준 병렬성]

인스트럭션 수준 병렬성으로 최근의 프로세서들은 훨씬 낮은 수준에서의 추상화로,

여러 개의 인스트럭션을 한 번에 실행할 수 있다.

 

파이프라이닝에서 하나의 인스트럭션을 실행하기 위해 요구되는 일들을 

여러 단계로 나누고 프로세서 하드웨어가 일련의 단계로 구성되어 

이들 관계를 하나씩 각각 수행한다. 

이들 단계는 병렬도 동작하고 서로 다른 인스트럭션의 다른 부분을 이용하여 작동한다. 

 

하드웨어 수준의 병렬성 : 

하드웨어가 여러 명령어를 병렬로 처리할 수 있는 설계

ex ) 파이프라이닝, 슈퍼스칼라 

 

소프트웨어 수준의 병렬성 : 

컴파일러가 명령어 간의 독립성을 분석하고,

서로 간섭하지 않는 명령어를 재배열하여 병렬로 실행

 

[싱글 인스트럭션, 다중 데이터 병렬성 (SIMD)]

많은 최신 프로세서들은

한 개의 인스터럭션이 병렬로 다수의 연산을 수행할 수 있는 특수 하드웨어를 가지고 있다.

(하나의 명령어로 여러 데이터를 동시에 처리)

 

이들 SIMD 인스트럭션들은 영상,소리,동영상 데이터 처리를 위한 응용프로그램의 속도를 개선하기 위해 제공된다.

 

ex) 


A = [1, 2, 3, 4]
B = [5, 6, 7, 8]

일반 프로세서 
A[0] + B[0] = 1 + 5 = 6
A[1] + B[1] = 2 + 6 = 8
A[2] + B[2] = 3 + 7 = 10
A[3] + B[3] = 4 + 8 = 12


SIMD 방식 
한번의 연산으로 [6, 8, 10, 12] 

 

 

병렬처리로 연산 속도가 매우 빠르고,

다량의 데이터를 반복적으로 처리해야 할 때 프로세서의 성능 극대화,

데이터를 통시에 처리하여 전력 소모가 적다 . 

 

반면 SIMD는 각 데이터가 독립적이어야 효과적이고,

모든 연산에서 SIMD를 적용할 수 없다.