15.프로세스와 하드웨어

컴퓨터의 구조

 

*메모리 : 메모리는 0 또는 1의 열을 저장할 수 있는 장치다. 보통 8비트를 1바이트로 통합하므로 바이트 열을 저장한다고 할 수 있다. 바이트열은 프로그래머가 작성한 코드가 최종적으로 변환된 형태에 해당한다. 

 

*cpu : cpu는 내부에 레지스터 라는 작은 장치가 있다. cpu는 메모리부터 레지스터에 데이터를 복사하여 연산을 수행후, 다시 메모리에 되돌려서 메모리의 값을 바꾼다. 

 

*hdd : hdd 나 ssd는 메모리처럼 바이트 열을 저장하는 장치지만, 메모리와 비교해서 조금 다르다.

1.일정한 바이트 단위로만 데이터를 주고 받을 수 있다.

2.일반적으로 용량이 메모리 보다 크다. 

3.속도가 메모리 보다 느리다.

4.전원이 꺼져도 데이터는 보관 된다.

 

*키보드,마우스 : 키보드나 마우스에는 바이트 열을 기억하는 기능이 없다. 하지만 대신 cpu와 바이트열을 주고 받을 수 있다. 키보드는 이를 이용해서 사용자가 누른 키에 맞는 바이트열을 cpu에게 보내고 cpu는 키보드가 눌러졌음을 인지하고, 디스플레이에 바이트열을 보내서 화면에 표시한다.

 

 

가상cpu와 가상 메모리

cpu의 경우에는 매우 짧은 시간 단위, 예를 들면 0.001초 주기로 실행하는 프로세스를 전환한다.

메모리경우 커널은 각 프로세스에 0번지부터 시작하는 가상의 메모리 주소공간을 제공해준다. 그러면 각 프로세스가 사용하는 가상의 주소는 실제 접근이 일어날 때 커널에 의해서 실제 메모리 주소로 변환된다. 이때 프로세스가 사용하는 주소를 논리주소, 실제 주소를 물리주소라고 한다. 프로세스의 주소 공간은 4kb, 8kb 크기의 페이지 단위로 관리된다. 물리 주소와 논리 주소의 매핑도 페이지 단위로 관리된다. 

 

*논리주소가 물리주소로 매핑되지 않는 페이지도 있다. 물리적 주소와 매핑되지 않는 논리적 주소의 경우, 필요할 순간에 물리적 주소를 할당받거나 의도적으로 접근 금지 페이지로 남겨둔 경우도 있다. (리눅스에서 접근이 금지된 페이지에 프로세스가 접근하면, 커널은 프로세스에 시그널  sigsegv를 보낸다. )

참고로 리눅스에서 null 포인터를 참조하면 반드시 세그먼테이션 폴트가 발생한다. 이때 null 포인터에 대한 접근을 확실하게 검출하기 위해서 리눅스 커널이 의도적으로 논리 주소의 처음 몇 페이지에 물리적 주소를 할당하지 않은 채로 접근을 금지하고 있다. 

 

가상메모리 메커니즘 응용

 

1.페이징

2.메모리 맴 파일

3.공유 메모리

 

*페이징: 페이징은 디스크를 물리 메모리 대신에 사용하는 메커니즘이다. 물리 메모리가 부족하면 커널은 사용하지 않는 페이지를 선정하여 스토리지에 기록하고, 논리 주소와의 매핑을 해제해 버린다.

 

*메모리 맵 파일 : 메모리 맵 파일은 파일을 메모리에 매핑하여 메모리를 읽으면 파일을 읽은 것이 되고,  메모리에 쓰면 파일에 쓰는 것이 되게 하는 메커니즘이다. 엑세스가 있는 시점에서 메모리에 읽어들이고, 그 메모리를 논리 주소에 매핑한다. 그리고 프로세스가 사용 완료를 통지하면 논리 주소와 물리 주소의 매핑을 해제 하고 메모리의 내용을 파일에 쓴다. (mmap 시스템콜)

 

*공유메모리 : 이름 그대로 특정 범위의 물리 메모리를 여러 프로세스에서 공유하는 메커니즘.

 

 

주소공간의 구조

*텍스트영역 : 기계어 코드가 배치된다.

*데이터 영역 : 전역변수나 함수내 정적 변수중 초기값이 있는것.

*bss 영역 :  전역변수나 함수내 정적 변수중 초기값이 없는것이 보관.

*힙영역 : malloc() 이 관리하는 영역. 실행시에 확대 또는 축소됨.

*스택영역: 함수 호출에 따라 데이터가 쌓이는 곳. 함수의 인자나 지역변수등이 보관.

 

 

주소공간들여다 보기

<프로세스 파일 시스템>을 사용하면 특정 프로세스의 주소 공간이 어떻게 구성되어 있는지 알 수 있다. 예를 들어 프로세스 id가 n인 프로세스의 메모리 사용구조를 보고 싶을때는 cat/proc/ n / maps 를 실행하면 된다. 

 

 

프로그램이 완성될때 까지

1.전처리 : #include 등을 처리해서 순수한 c 코드로 변환한다. gcc -E 옵션을 붙여서 실행하면 전치리만 수행한 결과를 표준출력으로 출력한다.

 

2.컴파일 : c언어 소스 코드를 어셈블리어코드로 변환한다. gcc를 -S 옵션을 붙여 실행하면 컴파일 까지 수행한 결과를 출력한다.

 

3.어셈블 : 어셈블리어로 된 코드를 기계어를 포함한 오브젝트 파일로 변환한다. 

 

4.링크 : 오브젝트파일로 부터 실행파일 또는 라이브러리 (*.a - 정적, *.so - 동적) 를 만든다. 

 

 

 

#homework.

1. 표준 입력의 마지막 몇줄만을 출력하는 tail 명령어를 작성하라. 출력할 라인 수는 실행 인자로 받아 옵션처리하도록 한다. 

 

 

 

 

 

 

참고:모두를 위한 리눅스 프로그래밍