시스템 프로그래밍이란? <-> ‘애플리케이션 프로그래밍과 반대되는 개념이다.

e.g. 리눅스 커널 프로그래밍 (linux kernel)

 

프로그래머들이 시스템을 공부하는 이유는 프로그래밍에 있어 더 나은 퍼포먼스를 이끌어 낼수있기 때문이다.

가령, 네트워크 공부 중 '직접 링크 네트워크'에서 워크스테이션의 하드웨어적 개념들과 여러 특성들을 이해한 후에 여러 애플리케이션 코드를 공부하게 되면, 그 하드웨어에 맞추어 코드 최적화가 가능하다. ‘더 나은 퍼포먼스라는 말은 이렇게 하드웨어와 코드 사이의 긴밀한 관계를 알고 그 사이간에 여러 최적화를 하는 것을 뜻한다.

 

그리고 여러 버그들 중 거의 프로그래밍 상에서 찾지 못하는 버그는 하드웨어적인 것이 많다.

여러 공동 작업자들과 최소한의 충돌로 좋은 프로젝트를 이끌어 낼수도있다.

(거의 시스템 지식은 필수적이다. 후에, 회사에서 중대한 임무를 맡느냐, 하위층을 맡느냐.)

 

왜 우리가 찾지 못하는 버그들은 하드웨어적인 문제들이 많을까?

마치 '를 이용하는 인간들이 그들의 상식 선에서 수들을 다루는 것 처럼 코드를 짜는 프로그래머들도 그들의 상식 선에서 코드들을 다루게된다. 기본적으로 그 코드들이 돌아가는 것은 하드웨어이기에 당연히 하드웨어적 접근을 해야하지만, 프로그래머들은 가끔 우리 인간의 상식대로만 작성하게되어 상식적으로 아무 문제가 없다라고 여기게된다. 하지만 하드웨어는 우리와 다르기 때문에 우리가 생각하지 못한 문제점들이 생겨나는 것이다.

 

이렇게 우리의 상식과 벗어나는 하드웨어적 문제들(Tricks)중 몇 가지를 살펴보자.


x^2 >= 0


수학적(일반적 상식)으로는 맞는 말이다. (물론 x 는 실수) 그러나 컴퓨터에선 어떨까?

Floats - 맞는 말이다.

Ints - 부분적으로 틀리게된다.

40000 * 40000 = 1600000000

50000 * 50000 = 오류

(Int형이 가진 크기를 넘어서서 Overflow가 일어나고, 결과는 음수 값이 나와 버릴것이다.)


(x + y) + z = x + (y + z)


Ints -
맞는 말이다.

Floats - 틀린 말이다.

(1e20 + -1e20) + 3.14 -> 3.14

1e20 + (-1e20 + 3.14) -> 0 (오류)

(Floats 형은 알다시피 전체 비트에서 지수 부분 + 소수점 표현부분들이 섞여있다. Float형이라고 하는 제한된 비트 내에서 또 각자 부분들의 제한된 영역(Finite memory)을 가진다는 것이다. 소수점 표현 부분은 이러한 제한 때문에 1.000000000000000000000314 같은 매우 큰 격차를 지닌 소수점 표현들이 공존할 수 없게 된다. 그래서 컴퓨터는 자동적으로 뒤에 작은 수들을 잘라버리는데, 이 때문에 작은 수치는 무시되게 된다.)

 

시스템 프로그래밍과 시스템의 전반적인 것을 다 배우고 나면 우리는 (일반 상식과는 다른) 하드웨어를 포함한 넓은 통찰력으로 프로그램을 짤 수 있게 되고, 시스템 프로그램, 시스템 분석 툴들도 작성할 수 있다.

 

수업에서 우리는 크게 ARM assembly Language 를 배울 것이다. 이런 언어들을 배우면서 언어가 가진 하드웨어적인 의미도 같이 공부해 본다.

 

 

왜 시스템 프로그래밍에서 ARM 을 배울까? ARM x86 에 비해 더 시스템적이며, 차후에 우리가 ARM 기반 시스템들에 대한 시스템 프로그램을 이용할 기회가 많을 것이다. PC 계를 제외한 여러 장치들에 거의 ARM 아키텍쳐를 사용하고있기 때문이다.

 

물론 Assembly Language는 이용할 날이 많진 않을것이다. 여러 exe 형태의 파일들의 원 소스를 파악하기 위해 디어셈블, 디컴파일을 하는데 이 과정에서 나오는 코드가 어셈블리 형태로 구성되어있어서, 이를 이해하는 것 외에는 크게 직접적으로 사용할 일이 없을것이다.

 

(ARM(Advanced RISC Machine) 아키텍처는 임베디드 기기에 많이 사용되는 32-bit RISC 프로세스이다. 저전력을 사용하도록 설계하여 ARM CPU는 모바일 시장에서 뚜렷한 강세를 보인다.)

      + RISC ( <-> CISC )

: RISC(reduced instruction set computer)
CPU 명령어의 개수를 줄여 하드웨어 구조를 좀 더 간단하게 만드는 방식으로, 마이크로프로세서를 설계하는 방법 가운데 하나이며, SPARC, MIPS 등의 아키텍처에서 사용된다.


전통적인 'CISC CPU'에는 프로그래밍을 돕기 위한 많은 수의 명령어과 주소 모드가 존재했다. 그러나 그중에서 실제로 쓰이는 명령어는 몇 개 되지 않는다는 사실을 바탕으로, 적은 수의 명령어만으로 명령어 집합을 구성한 것이 RISC이다. 그래서, RISC CISC보다 구조가 더 단순하다. 복잡한 연산도 적은 수의 명령어들을 조합하는 방식으로 수행이 가능하다.


그리고 CISC 형식의 CPU ROM에 소프트웨어적으로 적재된 내부 명령어들을 하드웨어적으로 구성하여 제어기(장치 드라이버 = 소프트웨어 드라이버 : 높은 수준의 컴퓨터 프로그램들이 컴퓨터 하드웨어 장치와 상호 작용하기 위해 만들어진 하나의 컴퓨터 프로그램)가 제거된 부분에 '프로세서 레지스터 뱅크' '캐시'를 둔다. 이렇게 함으로써 CPU, 상대적으로 느린 메인 메모리에 접근하는 횟수를 줄여주어 파이프라이닝 등 시스템 수행속도가 전체적으로 향상된다.)


> 특징


* 고정 길이의 명령어를 사용하여 더욱 빠르게 해석할 수 있다.

* 모든 연산은 하나의 클럭으로 실행되므로 파이프라인을 기다리게 하지 않는다.

­* 레지스터 사이의 연산만 실행하며, 메모리 접근은 세이브(save), 로드(load) 등 명령어 몇 개로 제한된다. 이렇게 함으로써 회로가 단순해지고, 불필요한 메모리 접근을 줄일 수 있다.

* 마이크로코드 논리를 사용하지 않아 높은 클럭을 유지할 수 있다.

* 많은 수의 레지스터를 사용하여 메모리 접근을 줄인다.

* 지연 실행 기법을 사용하여 파이프라인의 위험을 피한다.


CISC
에서는 하드웨어가 '스택'을 지원하지만, RISC에는 없다. 스택 제어(데이터의 PUSH, POP이 발생할 때 레지스터의 퇴피, 서브루틴에 점프했을 때의 리턴 주소의 보존, 복귀)의 처리는 단순한 명령을 조합하여 소프트웨어로 구현된다. 명령어의 순서로 인해 발생할 수 있는 파이프라인의 위험은 컴파일하는 동안 최적화되어 사라진다.


명령어의 길이를 고정하면 파이프라인 처리의 고속화를 꾀할 수 있지만, 컴파일러의 최적화 과정이 복잡해지기 쉽다.


RISC
는 대부분의 현대 프로세서 디자인에 채택되고 있고, 또 비교적 전력 소모가 적기 때문에 임베디드 프로세서에도 채택되고 있다.


요즘에는, 펜티엄과 같은 CISC CPU도 내부적으로는 복잡한 명령들을 단순한 명령들로 나누어 파이프라인에서 처리하기 때문에, 실제 작동 원리는 RISC와 같다.


-> ARM
계열 - 최신 팜 파일럿 PDA 시리즈. 게임보이 어드밴스, 닌텐도DS과 같은 닌텐도사의 소형 게임기 하드웨어. 한국 게임파크사의 GP32 하드웨어, 스마트폰, 태블릿pc.


      + 장치 드라이버 Device Driver
 

장치 드라이버(문화어: 장치구동기, 장치구동프로그람)디바이스 드라이버, 장치 제어기 또는 소프트웨어 드라이버라고 한다. 높은 수준의 컴퓨터 프로그램들이 컴퓨터 하드웨어 장치와 상호 작용하기 위해 만들어진 하나의 컴퓨터 프로그램이다.

드라이버는 흔히 컴퓨터 버스, 또는 하드웨어와 이어진 통신 하위 시스템을 통해(e.g. I/O버스 - 네트워크 어댑터) 장치와 통신한다. 요청하는 프로그램이 드라이버의 명령어들을 불러내면, 드라이버는 장치에 명령어들을 제공한다. 장치가 드라이버에게 데이터를 되돌려 주면, 드라이버는 원래 요청한 프로그램의 명령어들을 불러낸다.


드라이버는 하드웨어에 의존하며 특정한 운영 체제를 따른다.

이러한 드라이버는 비동기 시간에 의존하는 하드웨어 인터페이스에 필요한 인터럽트를 다룰 수 있다.

장치 드라이버는 흔히 장치 칩의 레지스터에 접근하여 하드웨어를 제어하며 하드웨어와 주변 기기를 사용하는 프로그램의 중간 다리 역할을 한다.

      + 레지스터 register

[
하드웨어 레지스터 ]

 

컴퓨팅에서, 하드웨어 레지스터는 다른 종류의 '하드웨어 입출력 (I/O)'를 위한 저장공간이다.

하드웨어 레지스터는 주요 주변장치의 내부에 포함되어 있고, '메모리맵 I/O' '포트맵 I/O'의 의미에 따라서 컴퓨터의 '중앙 처리 장치(CPU)'를 나타낸다. 특히 초기화 동안에, "환경설정"과 주기능의 시작을 포함하는 하드웨어 레지스터의 일반적인 사용은,


1. "
버퍼 저장공간" (예시로 그래픽 카드를 위한 비디오 메모리),

2. 하드웨어 장치에 발생되는 주요한 사건 같은 "상황 보고"가 있다.


읽어진 하드웨어 레지스터는 프로세서에 의한, "읽기" "저장" 명령과 접근한 그것의 메모리나 포트주소를 포함한다. 하드웨어 레지스터는 워드로 주소화되지만, 레지스터를 읽거나 쓰는데, 가끔씩 워드의 몇 비트만 사용한다.


[ 스트로브 레지스터 ]

 

스트로브 레지스터는 데이터를 저장하지않는 하드웨어 레지스터이지만 접근할 때 동작을 트리거링 수단처럼 사용된다. 신호의 수단이다.


전자 제품에서 각각의 주변 기기들을 제어하기 위해 설계된 펌웨어 또한 장치 드라이버라고 한다. 장치 드라이버의 실제 예는 소스가 공개된 '리눅스 커널 소스'에서 /driver 디렉터리 밑에 있는 것을 참조하여 볼 수 있다.


 

      + '리눅스 커널' Linux kernel 이란?

리눅스 커널(Linux kernel)은 유닉스 계열 운영 체제의 '커널'이다. 'GNU 일반 공중 사용 허가서' 버전 2 (GPLv2) 아래에서 공개되었으며 전 세계적으로 배포자들이 개발한 리눅스는 가장 두드러진 '자유 소프트웨어 / 오픈 소스'의 본보기들 가운데 하나이다.

 

리눅스 커널은 1991년에 리누스 토르발스에 의해 생긴 말이다. 일찍이 '미닉스' 커뮤니티가 리눅스 커널에 코드와 개념을 제공하였다. 그 당시 'GNU 프로젝트' '자유 소프트웨어' 운영 체제에 필요한 요소를 많이 만들어 냈지만 자체 커널 'GNU 허드'는 완전하지 않았고 이용성이 없었다. 'BSD' 운영 체제는 법적 문제로부터 헤어나오지 못했다. 이는 초기 버전의 제한된 기능에도 불구하고 리눅스가 새로운 운영체제를 사용하기 위한 프로젝트로부터 코드를 채용한 개발자들과 사용자들을 빠른 속도로 모았다는 것을 말해 준다. 오늘날 리눅스 커널은 수많은 프로그래머로부터 기여를 받고 있다.


 

      + '유닉스 계열' Unix-like 이란?

유닉스 계열(Unix-like) 운영 체제는 유닉스 시스템과 비슷한 구조를 가진 운영 체제를 말한다. *nix라고 부르기도 한다.


유닉스 계열이라고 해서 '단일 유닉스 규격'을 따르거나 관련 인증을 받을 필요는 없다.

이 용어는 벨 연구소의 유닉스가 고안하여 기능을 에뮬레이트하도록 설계된 자유 소프트웨어 / 오픈 소스 운영 체제와 그리고 라이선스된 유닉스 소스 코드를 기반으로 하는 버전들을 포함하기도 한다. 이 용어에 대한 표준이 공식적으로 정의된 바는 없으며, 어떠한 운영 체제가 유닉스 계통이냐 아니냐에 대해 일부 의견차가 있을 수 있다.


유닉스를 본래 제작한 사람들 가운데 한 명인 '데니스 리치' 'GNU/리눅스'와 같은 유닉스 계열 시스템이 de facto(in fact : 표준이나 법에 관련이 없고, 보편적이지 않은 단지 사실적인)유닉스 시스템이라고 의견을 냈다. 에릭 레이먼드와 랍 랭글리(Rob Langley)는 유닉스 계열 시스템에 세 가지 종류가 있다고 언급하였다.


*
일반 유닉스:
대부분의 상표 유닉스 시스템은 이 분류로 들어간다.
*
상표 유닉스: 오픈 그룹이 단일 유닉스 규격을 충족하기 위해 결정한 시스템이다.
*
기능 유닉스: 유닉스 규격에 거의 상응하는 방식으로 동작하는 유닉스 계열 시스템이다.


      + GNU

      + 리눅스

      + '단일 유닉스 규격' Single UNIX Specification : SUS ?

 

단일 유닉스 규격(Single UNIX Specification : SUS)은 컴퓨터의 운영체제가 유닉스란 이름을 사용하기 위해 지켜야 하는 표준 규격의 총칭이다. SUS IEEE와 오픈 그룹(The Open Group)의 표준화 작업 결과물에 바탕을 두고 있으며, 오스틴 그룹이 개발 및 유지 관리를 담당하고 있다. 단일 유닉스 규격 이전에 존재하였던 'POSIX' ISO/IEC JTC1에서의 작업은 종료되어, 오스틴 그룹이 유지 관리 작업에 관여하고 있다.



>역사


1980
년대 중반, 단일 유닉스 규격은 여러 유닉스 계열 운영 체제의 인터페이스를 표준화하기 위해 시작된 한 프로젝트에 그 바탕을 두고 있다. 업체마다 다른 운영체제 사이의 소프트웨어 이식에 들이는 비용을 되도록 줄여 달라는 여러 운영체제를 운영하던 기업들의 요청으로 인해 표준화 프로젝트가 시작되었다. 표준화의 바탕이 되는 운영 체제로 유닉스가 선택이 되었으며, 이는 유닉스는 특정 회사 제품에 종속되지 않는 중립형 운영 체제로 받아들여졌기 때문이다. 이 표준화 프로젝트의 결과로 만들어진 것이 IEEE 1003 (ISO/IEC 9945 로 등록되었다) 다른 말로 'POSIX'이다.


1990
년대 초에 POSIX와는 별도로, 이른바 UNIX 전쟁의 결과로 몇 군데의 회사들이 COSE(Common Open Source Environment) 협정을 결성하여, Common API Specification 또는 Spec 1170이라 불리는 사양을 내놓은 바 있다. 이 사양은 무료로 입수할 수 있었기에 IEEE에게 접근 비용을 부담해야 하는 POSIX보다 널리 일반화되었다.


1998
, Austin Group이라 불리는 공동의 워킹 그룹이 이 사양들의 통합을 시작하여, 그 결과로 Single UNIX Specification version 3(단일 유닉스 규격 제3)이 탄생하였다.



> 상세내용


단일 유닉스 규격에서 규정하는 운영체제와 사용자 및 소프트웨어 사이의 인터페이스는 다음의 4 가지로 분류된다.


* Base Definitions :
표준 규격을 기술하는 데 사용되고 있는 정의와 규약 등의 목록과, 이에 따르는 운영체제가 반드시 제공해야 할 C 언어의 헤더 파일 목록
* Shell and Utilities :
유틸리티(명령)의 목록 및 셸(sh)의 내역
* System Interfaces :
제공되어야 하는 시스템 호출 및 C 라이브러리의 목록
* Rationale :
이상의 표준에 대한 해설


이 표준에 의한 사용자 명령 줄 인터페이스와 스크립트 인터페이스는 초기 '콘 셸'에 바탕을 둔 '본 셸'의 확장판인 'POSIX '이다. 이 밖에 사용자 레벨의 프로그램 또는 서비스, 유틸리티로는 awk, echo, ed 등 수백여개의 목록이 포함되어 있다. 프로그램 레벨에서 필요로 하는 서비스로는 입출력(파일, 터미널, 네트워크) 등이 있다.


표준에는 테스트 프로그램 모음인 PCTS(Posix Certification Test Suite)가 포함되어 있다. PCTS NIST에서 오픈 소스로 공개되어 있다.


이 사양을 만족하기 위해 AT&T의 유닉스 소스 코드를 사용하지 않아도 된다는 점에 주의해야 한다. (실제의 예로, IBMz/OS (OS/390)은 소스코드는 완전히 독자적으로 만들어졌으나, 'UNIX'란 이름을 사용하도록 허용받고 있다.)



> 인증마크


이 표준을 만족하는 운영 체제에 사용할 수 있는 마크가 2가지가 존재한다.

UNIX 98 - SUS Version 2 를 만족하는 운영체제에 붙일 수 있는 마크
UNIX 03 - SUS Version 3
를 만족하는 운영체제에 붙일 수 있는 마크

이 밖에도 예전에 존재하였던 UNIX 93 UNIX 95가 있다.



      + '커널' kernel 이란?

컴퓨터 과학에서의 커널(kernel)은 운영 체제의 핵심 부분으로서, 운영 체제의 다른 부분 및 응용 프로그램 수행에 필요한 여러 가지 서비스를 제공한다.



> 커널의 역할


커널은 운영 체제의 핵심 부분이므로, 커널의 역할 역시 운영 체제의 핵심 역할이라 할 수 있다.


*
보안

커널은 컴퓨터 하드웨어와 프로세스의 보안을 책임진다.


*
자원 관리

한정된 시스템 자원을 효율적으로 관리하여 프로그램의 실행을 원활하게 한다.

특히 프로세스에 처리기를 할당하는 것을 스케줄링이라 한다.


*
추상화

같은 종류의 부품에 대해 다양한 하드웨어를 설계할 수 있기 때문에 하드웨어에 직접 접근하는 것은 문제를 매우 복잡하게 만들 수 있다. 일반적으로 커널은 운영 체제의 복잡한 내부를 감추고 깔끔하고 일관성 있는 인터페이스를 하드웨어에 제공하기 위해 몇 가지 하드웨어 추상화(같은 종류의 장비에 대한 공통 명령어의 집합)들로 구현된다. 이 하드웨어 추상화는 프로그래머가 여러 장비에서 작동하는 프로그램을 개발하는 것을 돕는다. 하드웨어 추상화 계층(HAL)은 제조사의 장비 규격에 대한 특정한 명령어를 제공하는 소프트웨어 드라이버에 의지한다.



> 초창기의 커널


초창기의 컴퓨터에서 운영 체제 커널은 필수적인 것이 아니었다. 초기의 프로그램은 하드웨어 추상화나 운영 체제의 지원을 받지 않고도 컴퓨터만으로 불러들인 다음 실행될 수 있었으며, 이것은 초창기 컴퓨터들의 일반적인 운영 방식이었다. 다른 프로그램을 실행하기 위해서는 컴퓨터의 전원을 껐다가 켬으로써 다시 입력자료를 읽어들여야 하는 방식이었다. 이러한 과정이 반복되면서 사람들은 '로더' '디버거' 같은 작은 프로그램들이 상주해 있는 것이, 다른 프로그램으로 교체하거나 새로운 프로그램을 개발하는 데 유리하다는 사실을 알게 되었다. 이와 같은 로더, 디버거들이 초기 운영 체제 커널의 기초가 되었다



> 커널의 종류


*
단일형 커널(monolithic kernel : 모노리딕 커널) -
커널의 다양한 서비스 및 높은 수준의 하드웨어 추상화를 하나의 덩어리(주소 공간)로 묶은 것이다. 운영 체제 개발자 입장에서 유지 보수가 일반적으로 더 어려우나 성능이 좋다.


*
마이크로커널(microkernel) - 하드웨어 추상화에 대한 간결한 작은 집합을 제공하고 더 많은 기능은 서버라고 불리는 응용 소프트웨어를 통해 제공한다.


*
혼합형 커널(hybrid kernel) -
성능 향상을 위해 추가적인 코드를 커널 공간에 넣은 점을 제외하면 많은 부분은 순수 마이크로커널과 비슷하다. 수정 마이크로커널이라고도 한다.


*
나노커널(nanokernel) - 실질적으로 모든 서비스를 책임진다.


*
엑소커널(exokernel) - 낮은 수준의 하드웨어 접근을 위한 최소한의 추상화를 제공한다. 전형적으로 엑소커널 시스템에서는 커널이 아닌 라이브러리가 단일형 커널 수준의 추상을 제공한다.

 

 



      + CISC ( <-> RISC )


: CISC (Complex Instruction Set Computer)는 복잡한 명령어 집합를 갖는 CPU 아키텍처이다.

명령어가 복잡하기 때문에 명령어를 해석하는 데 시간이 오래 걸리며, 명령어 해석에 필요한 회로도 복잡하다. 보통 풍부한 어드레싱 기능을 갖추고 있어 명령의 직교성이 좋으며, 어느 어드레싱 모드에서도 임의의 연산을 수행할 수 있다.


연산에 대해서는 레지스터와 레지스터 연산, 레지스터와 메모리 연산, 메모리와 메모리 연산을 모두 갖추고 있는 것이 보통이다. 피연산자(operand) 2개에서 3개까지 지정할 수 있는 경우가 많다. , 메모리 1의 내용과 메모리 2의 내용의 논리곱을 취해 메모리 3에 넣는 일을 하나의 명령어로 수행할 수 있다.


C
언어의 연산자는 CISC PDP-11이 갖추고 있던 명령에서 나온 것이다. 인덱스 어드레싱시의 오프셋도 명령의 데이터의 길이에 맞추어 바뀌는 것이 많다. , 길이가 다른 데이터 사이의 연산에서도 연산 전에 자동적으로 부호 확장 등이 수행되기 때문에, 데이터 길이를 가지런히 하는 명령어가 필요 없는 경우가 많다. 하나의 명령어를 수행하는 처리가 복잡하기 때문에, 마이크로 프로그램 방식을 채택하는 경우가 많다.


주로 메인프레임이나 X86 호환 프로세서, 모토로라사의 MC 680x0 (68K)계열 프로세서에서 이 방식을 사용하고 있다. 펜티엄 시리즈와 같은 CISC CPU도 내부적으로는 복잡한 명령들을 다시 단순한 명령들로 나누어 명령어 파이프라인에서 처리하기 때문에, 실제 내부 작동원리는 RISC와 같다고 할 수 있다.


-> x86



이렇게 우리가 시스템 프로그래밍 수업으로 무엇을 얻으려는지, 시스템 수업을 위해서 우리는 ARM을 공부할것이고, ARM의 특성, 각 용어들의 정확한 정의들을 알아보았다. 그럼 이제는 ARM 수업에 앞서 환경을 구축해 놓아야한다.

다음 강의에는 'ARM 수업'을 위한 환경, 정확히 말하자면 ARM (SoC) 개발을 위한 개발환경(Develop Environment)을 구축하는 법을 알아보도록 하자.

 

 

 

 

 

 

Posted by 하늘_