1-1 정보는 비트와 컨텍스트로 이루어진다.
프로그램은 프로그래머가 에디터로 작성한 소스 파일(0,1로 표시되는 비트의 연속 바이트라는 8비트 단위이다)로 생명을 시작하며 텍스트 파일로 저장된다. 소스 프로그램에서 각 바이트는 프로그램의 텍스트 문자를 나타낸다. 대부분의 컴퓨터 시스템은 텍스트 파일을 아스키(ASCII)표준을 사용하여 표시한다.(각 문자를 바이트 길이의 특정 정수값으로 나타냄) 연속된 바이트로 파일에 저장되는데 이것을 텍스트 파일이라 부른다. 다른 모든 파일들은 바이너리 파일이다 * 모든 시스템 내부의 정보, 디스크파일, 메모리상의 프로그램, 데이터는 비트들로 표시된다. 서로 다른 객체들을 구분하는 유일한 방법은 이들을 구분하는 유일한 방법은 컨텍스트에 의해서다. ex) 동일한 일련의 바이트가 컨텍스트에 따라 정수, 부동소수, 문자열을 의미할수있다.
소스 파일(hello) - 바이트(8비트)단위를 사용하는 0,1로 구성된 파일
텍스트파일(hello.c)- 아스키코드로만 이루어진 파일(연속된 바이트)
컨텍스트 - 코드의 배경이 되는 조건, 환경
1-2 프로그램은 다른 프로그램에 의해 다른 형태로 변역된다.
텍스트 파일을 시스템에서 실행시키려면, 각 C문장들은 다른 프로그램에 의해 인스트럭션으로 변역되어야한다. 그리고 인스트럭션들이 합쳐져서 바이너리 디스크 파일로 저장된다(목적 프로그램). C언어에서의 컴파일러 드라이버는 소스파일을 읽어 실행파일로 번역한다. 번역이 될때 4개의 단계를 거치게된다. 이것을 컴파일 시스템(전처리기, 컴파일러, 어셈블러, 링커을 합친 것)이라 부른다
컴파일 시스템(4개의 단계 C의 Hello world코드를 기준으로)
1) 전처리 단계 - 전처리기는 #뒤의 문자(첫줄의 include<stdio.h>) 디렉티브에 따라 hello.c를 hello.i.로 끝나는 새로운 프로그램 생성
2) 컴파일 단계 - 컴파일러는 텍스트 파일 hello.i를 텍스트 파일인 hello.s로 번역한다 (이때 어셈블리어로 바뀐다.)
3) 어셈블리 단계 - 어셈블러가 hello.s를 기계어 인스트럭션으로 변역한다. 이들은 재배치가능 목적프로그램의 형태로 묶어서 hello.o라는 목적 파일에 그 결과를 저장한다.(이 파일은 17바이트를 포함하는 바이너리 파일이다.) 쓰레기같은 데이터 처럼 보일수있다.
4) 링크 단계: 링커 프로그램이 hello.o파일과 별도의 목적파일(ex)printf.o) 의 통합 작업을 수행한다. 그 결과인 hello파일은 실행파일로 메모리에 적재되어 시스템에 의해 실행된다.
hello 프로그램 -> hello.c(아스키코드) -> hello.i(텍스트 파일) -> hello.s(어셈블리어) -> hello.o(인스트럭션) -> hello 실행파일
인스트럭션 - 컴퓨터가 알아들을수있는 기계어로 이루어진 명령
1-3 컴파일 시스템이 어떻게 동작하는지 이해하는 것이 중요하다.
C프로그램 작성 시 올바른 판단을 하기 위해서는 기계어 수준의 코드에 대한 기본적인 이해를 할 필요가 있다.
ex) switch문은 if-else문을 연속해서 사용하는 것보다 언제나 더 효율적일까?, 함수 호출 시 발생하는 오버헤드는 얼마나 되는가? while루프는 for루프 보다 더 효율적일까? 포인터 참조가 배열 인덱스보다 더 효율적일까? 등 C프로그램의 성능을 조절하는 방법을 배우게된다.
보다 더 효율적으로 실행할수있는지! 보안약점(security hole) 또한 버퍼 오버플로우의 취약성이 인터넷과 네트워크상의 보안 약점의 주요원인이였다. 이는 프로그램 스택에 데이터와 제어 정보가 저장되는 방식 때문에 생겨난다.
컴퓨터 시스템의 더 깊은 이해 -> 오류와 취약점 해결 능력 상승
1.4 프로세서는 메모리에 저장된 인스트럭션을 읽고 해석한다.
디스크에 저장된 실행가능 목적파일은 유닉스 시스템에서 실행하기위해 쉘(명령어 해석기)이라는 응용프로그램을 이용한다.
1.4.1 시스템의 하드웨어 조직
버스(Buses) - 시스템 내를 관통하는 전기적 배선으로 컴포넌트들 간에 바이트 정보들을 전송하는 역할
입출력장치 - 키보드, 마우스, 출력용 디스플레이, 데이터와 프로그램의 저장을 위한 디스크 드라이브 등 정보를 주고받도록 하는 역할을 한다.
메인 메모리 - 프로세서가 프로그램을 실행하는 동안 데이터와 프로그램을 모두 저장하는 임시 저장장치다.
프로세서(CPU) - 메인 메모리에 저장된 인스트럭션들을 해독(실행)하는 엔진이다.
1.4.2 hello 프로그램의 실행 예시
1. 쉘 프로그램이 자신의 인스트럭션을 실행하면서 우리가 명령하기를 기다린다.
2. 우리가 /:hello를 입력하고 엔터를 치면 각각의 문자를 레지스터에 읽어들인 후 메모리에 저장한다.
3. 그 이후 실행파일 hello를 디스크에서 메인 메모리로 로딩하는 명령을 내리고 직접 메모리 접근을 이용해 데이터로 들어가있는 "hello, world\n" 문자열을 프로세서(cpu)를 거치지 않고 디스크에서 메인 메모리로 직접 이동한다.
4. 일단 hello 목적 파일의 코드와 데이터가 메모리에 적재된 후 프로세서는 hello 프로그램의 인스트럭션을 실행해 문자열을 메모리로부터 레지스터 파일로 복사하고 디스플레이 장치로 전송하여 화면에 문자열이 표시된다.
레지스터와 메모리의 주된 차이 - 메모리는 cpu(프로세서)가 처리에 필요한 데이터 즉 명령과 데이터를 둘다 가지고 있지만 레지스터는 현재 처리하고있는 데이터를 가지고 있다. -> cpu가 메모리에 직접 접근해서 데이터를 가져오는 것 보다 레지스터에 더 빠르게 엑세스가 가능하다. -> 대부분의 현대 프로세서는 메인 메모리에서 레지스터로 데이터를 옮겨와 데이터를 처리한 후 그 내용을 다시 레지스터에서 메인 메모리로 저장하는 로드-스토어 설계를 사용하고 있다
1.5 캐시가 중요하다.
1.4.2의 예시에서 알게되는 것은 시스템이 정보를 한곳에서 다른곳으로 이동시키는 일에 많은 시간을 할애하는 것 이다. 프로그래머의 관점에서 보면 이러한 여러 복사과정들이 프로그램의 실제 작업을 느리게 하는 오버헤드다. -> 시스템 설계자들의 주요목적은 이러한 복사과정들을 가능한 빠르게 동작하도록 하는것이다. -> 메인 메모리를 더 빠르게 동작하도록 만드는 것보다 프로세서를 더 빨리 동작하도록 만드는 것이 더 쉽고 비용이 적게든다 -> 기술이 발전함에 따라 프로세서와 메모리간의 격차를 대응하기 위해 나온것이 캐시이다. 캐시는 프로세서가 단기간에 사용할 가능성이 높은 정보를 임시 저장한다. -> 캐시 메모리를 이해해 응용하는 프로그래머는 캐시를 활용하여 자신의 프로그램 성능을 개선할 수 있다.
1.6 저장장치들은 계층 구조를 이룬다.
모든 컴퓨터 시스템의 저장 장치들은 메모리 계층 구조로 구성되어 있다.
ex) 캐시메모리(작고 빠른 저장장치) -> 메인 메모리 -> 로컬 디스크
여기서 중요한것은 상위 메모리가 하위 메모리의 캐시역할을 한다는 것이다.
1.7 운영체제는 하드웨어를 관리한다.
1.4.2 예시에서 쉘프로그램이 hello 프로그램을 로드하고 실행했을때와 hello 프로그램이 메시지를 출력할 때 프로그램이 직접 입출력 장치에 엑세스하지않았다. 오히려 OS(운영체제 Operation System)가 제공하는 서비스를 사용한다. 운영체제는 하드웨어와 소프트웨어 사이에 위치한 계층이다. 응용프로그램이 하드웨어를 제어하려면 언제나 운영체제를 통해 해야한다.
운영체제의 기능
1. 프로세서, 기억장치, 입출력장치, 파일 및 정보 등의 자원을 관리합니다.
2. 자원을 효율적으로 관리하기 위해 자원의 스케줄링 기능을 제공합니다.
3. 사용자와 시스템간의 편리한 인터페이스를 제공합니다.
4. 시스템의 각종 하드웨어와 네트워크를 관리, 제어합니다.
5. 데이터를 관리하고, 데이터 및 자원의 공유 기능을 제공합니다.
6. 시스템의 오류를 검사하고 복구합니다.
7. 자원 보호 기능을 제공합니다.
8. 입 출력에 대한 보조 기능을 제공합니다.
9. 가상 계산기 능력을 제공합니다.
ex) WIndows 98, Windows10, UNIX, LINUX, MS-DOS
1.7.1 프로세스
프로세스는 운영체제가 프로그램을 실행하기 위해 필요한 가장 작은 단위의 쓰레드, 메모리, 소스코드들의 집합이며 프로그램 동작 자체를 의미한다. 운영체제는 프로세스를 작업의 단위로 보고 자원들을 프로세스들에 적절히 분배하는 역할을 한다.
1.7.2 쓰레드(Thread)
프로세스는 쓰레드라고 하는 다수의 실행유닛으로 구성되어 있다. 각각의 쓰레드(프로세스 내부의 작업의 흐름, 단위이다)는 해당 프로세스의 컨텍스트에서 실행되며 동일한 코드와 전역데이터를 공유한다. -> 쓰레드는 프로세스보다 더 빠르고 효율적이다.
1.7.3 가상 메모리(Virtual Memory)
가상 메모리라는 개념이 도입되기 전에는 프로세스가 실행되는 코드의 데이터 전부를 메모리에 로드해야했고 메모리 용량보다 큰 프로그램을 실행시킬 수 없었다. 따라서 물리적 메모리 크기의 한계를 극복하기 위해서 나온 것이 가상 메모리이다. 프로세스를 실행할때 실행에 필요한 일부만 메모리에 로드하고 나머지는 디스크에 두는 것으로 메모리의 작은양의 주소 공간으로도 충분히 프로세스를 실행 시킬 수 있고 더 많은 프로그램을 동시에 돌릴 수 있다.
1.7.4 파일
파일은 그저 연속된 바이트로 입출력 장치는 모두 파일로 모델링된다.
1.8 시스템은 네트워크를 사용하여 다른 시스템과 통신한다.
시스템이 메인 메모리에서 네트워크 어댑터로 바이트를 복사할때 데이터는 로컬 디스크 드라이브 대신 네트워크를 통해 다른 컴퓨터로 이동한다. 마찬가지로 시스템은 다른 컴퓨터로부터 받은 데이터를 읽어서 메인메모리에 저장할 수 있다. * 인터넷의 등장으로 하나의 컴퓨터에서 다른 컴퓨터로 정보를 복사하는 것이 컴퓨터의 응용에서 중요해졌다.ex) 클라이언트와 서버간의 데이터 교환
1.9 중요한 주제들
시스템은 하드웨어와 시스템 소프트 웨어가 서로 연결 된 것을 애기하고 컴퓨터 시스템에 대한 세부 지식을 통해 보다 빠르고 안정적인 프로그램을 작성할 수 있는지 깨닫게 될 것이다.
1.9.1 Amdahl의 법칙
어떤 시스템의 한 부분의 성능을 개선할 때, 전체 시스템 성능에 대한 효과는 그 부분이 얼마나 중요한가와 이 부분이 얼마나 빨라졌는가에 관계된다
ex) 전체 시간의 60%를 소모하는 시스템의 일부분이 속도가 3배 빨라짐. 전체 속도향상은 1.67배밖에 향상 안 됨.(a = 60% = 0.6, k=3) 1/[0.4+0.6/0.3] = 1.67
3x) 시스템의 60%를 시간이 거의 걸리지 않는 지점까지 속도를 올릴 수 있다면, 총 속도 개선율은 여전히 1/0.4 = 2.5밖에 걸리지 않음
* 전체 시스템을 상당히 빠르게 하기 위해서는 전체 시스템의 매우 큰 부분의 성능을 개선해야 한다.
1.9.2 동시성과 병렬성
동시성 | 병렬성 |
동시에 실행된 것 처럼 보이는 거 | 실제로 동시에 작업을 처리하는거 |
싱글 코어에서 멀티쓰레드를 동작시키는 방식 | 멀티 코어에서 멀티쓰레드를 동작시키는 방식 |
한번에 많은 것을 처리 | 한번에 많은 일을 처리 |
논리적인 개념 | 물리적인 개념 |
그림 처럼 싱글 코어는 실제 여러개를 동시에 실행 시키는 것처럼 보이기 위해 번갈아가며 작업을 수행하지만(Context switch) 멀티 코어는 실제로 여러개를 동시에 실행한다.
1.9.3 컴퓨터 시스템에서 추상화의 중요성
'컴퓨터 시스템' 카테고리의 다른 글
메모리 구조 (0) | 2023.05.12 |
---|---|
프로그램의 기계수준 표현, 링커 (0) | 2023.05.12 |
동적 메모리 할당(Malloc과 Free, calloc, realloc) (1) | 2023.05.07 |
Red-Black 트리 (0) | 2023.05.07 |
Ubuntu 리눅스 명령어 정리 (0) | 2023.05.04 |