사전 Arm
표준

Arm

gabury1고친 사람 github-actions[bot]

Arm 은 칩이 어떤 명령을 알아듣는지 정해 둡니다. 스마트폰과 작은 전자 기기 대부분이 이 약속대로 만든 칩으로 돌아갑니다. Arm 은 그 약속을 설계해 다른 칩 회사에 빌려주는 회사의 이름이기도 합니다. 이 글은 약속 쪽을 중심으로 씁니다.

쉽고 빠른 이해

Arm 은 칩이 알아듣는 명령의 목록과 규칙입니다. 지금 팔리는 스마트폰의 칩이 거의 다 이 규칙대로 만들어졌습니다.

이런 공통 규칙이 없으면 칩 회사마다 명령이 달라집니다. 그러면 같은 프로그램을 칩마다 새로 번역해야 합니다.

돌아가는 순서는 셋입니다.

  1. Arm 이라는 회사가 명령 규칙과 그 규칙을 따르는 칩 설계도를 만듭니다
  2. 칩 회사가 사용료를 내고 그 설계를 가져가 자기 칩에 넣습니다
  3. 프로그램은 Arm 용으로 한 번 번역하면 이 규칙을 따르는 칩에서 돌아갑니다

잃는 것도 있습니다. Arm 용으로 번역한 프로그램은 노트북과 서버에 흔한 인텔 계열 칩에서 돌지 않습니다. 같은 서비스를 두 종류 칩에 올리려면 두 벌을 만들어야 합니다.

상세

Arm 은 CPU(Central Processing Unit, 중앙 처리 장치)가 실행할 명령을 정한 규격입니다. CPU 는 프로그램을 한 줄씩 읽어 계산하는 칩입니다. 덧셈 명령 하나를 어떤 비트로 적는지, 어느 값을 읽어 어디에 결과를 남기는지가 이 규격에 적혀 있습니다.

명령의 목록과 규칙을 통틀어 명령어 집합이라고 부릅니다. 영어로는 ISA(Instruction Set Architecture, 명령어 집합 구조)라고 씁니다. 「Arm 아키텍처」라고 하면 대개 이 명령어 집합을 가리킵니다.

명령어 집합이라는 약속

프로그램을 컴파일러로 번역하면 실행 파일에는 명령어 집합이 정한 명령만 담깁니다. 컴파일러는 사람이 쓴 코드를 칩이 읽는 기계어로 옮기는 프로그램입니다. 그래서 번역하기 전에 어느 명령어 집합용으로 만들지부터 정합니다.

명령어 집합이 다르면 같은 코드라도 번역 결과가 다릅니다. 흔한 예가 x86 입니다. x86 은 인텔 계열 칩이 따르는 명령어 집합입니다. 노트북과 서버에서 오래 주로 쓰였습니다.

x86 용으로 번역한 실행 파일은 Arm 칩에서 돌지 않습니다. 반대로 규격이 같으면 칩을 누가 만들었든 같은 실행 파일이 돌아갑니다. 서로 다른 회사가 만든 Arm 칩 두 개가 같은 프로그램을 돌리는 까닭입니다.

칩마다 덧붙인 선택 명령은 다를 수 있습니다. 벡터 계산을 한꺼번에 하는 SVE(Scalable Vector Extension, 가변 길이 벡터 확장) 명령이 그런 예입니다. 그런 명령을 쓴 프로그램은 그 기능을 갖춘 칩에서만 돌아갑니다.

명령을 작게 자르는 설계

Arm 은 RISC(Reduced Instruction Set Computer, 축소 명령어 집합 컴퓨터) 계열 명령어 집합입니다. RISC 는 명령 하나가 맡는 일을 작게 자르는 설계 방향입니다. 명령이 단순하면 그 명령을 해석하는 회로도 단순해집니다. 회로가 작으면 전기를 덜 씁니다.

x86 은 반대쪽인 CISC(Complex Instruction Set Computer, 복합 명령어 집합 컴퓨터)에서 출발했습니다. 명령 하나가 메모리를 읽고 계산하고 다시 쓰는 일까지 한꺼번에 하기도 합니다.

Arm 의 계산 명령은 레지스터에 있는 값만 다룹니다. 레지스터는 CPU 안에 있는 아주 작은 저장 칸입니다. 메모리에 있는 값을 더하려면 먼저 그 값을 레지스터로 읽어 와야 합니다.

메모리를 건드리는 명령은 둘로 몰아 두었습니다. 메모리에서 읽어 오는 로드와 메모리에 내려 쓰는 스토어입니다. 이런 짜임을 로드-스토어 구조라고 부릅니다.

Arm 에는 32비트 판과 64비트 판이 있습니다. 여기서 비트 수는 레지스터 하나가 담는 값의 폭입니다. 명령 하나의 길이와는 다른 이야기입니다.

아래는 64비트 Arm 의 명령 다섯 줄입니다. x0·x1 같은 이름이 64비트 레지스터의 이름입니다. mov 는 레지스터에 숫자를 넣습니다. add 는 레지스터 둘을 더합니다.

str 은 메모리에 내려 쓰는 스토어 명령입니다. ldr 은 메모리에서 읽어 오는 로드 명령입니다. [x3] 은 x3 에 든 주소가 가리키는 메모리 칸을 뜻합니다.

asm
mov  x0, #7        // x0 = 7
mov  x1, #5        // x1 = 5
add  x2, x0, x1    // x2 = 12
str  x2, [x3]      // x3 주소에 12
ldr  x4, [x3]      // x4 = 12

덧셈 add 는 메모리를 전혀 모릅니다. 메모리와 값을 주고받는 일은 str 과 ldr 만 합니다.

64비트 Arm 의 명령은 모두 32비트 길이입니다. 레지스터가 64비트여도 명령은 늘 4바이트씩입니다. 길이가 고정이라 칩은 다음 명령이 어디서 시작하는지 바로 압니다. 그래서 명령 여러 개를 한꺼번에 읽어 미리 해석해 두기 쉽습니다.

회사 이름과 규격 이름

Arm 은 이 규격을 설계하는 영국 회사의 이름이기도 합니다. 이름은 원래 Advanced RISC Machines 의 머리글자였습니다. 지금 회사는 머리글자가 아닌 이름으로 Arm 이라고 씁니다. 옛 표기인 ARM 도 같은 것을 가리킵니다.

이 회사는 칩을 직접 만들어 팔지 않습니다. 대신 설계를 팝니다. 무엇을 파는지 보려면 코어부터 알아야 합니다.

코어는 칩 안에서 명령을 읽고 실행하는 계산 단위입니다. 칩 하나에 코어가 여럿 들어가기도 합니다. Arm 은 명령어 집합과 함께 그 규격을 따르는 코어 설계도를 만듭니다. 그 설계를 다른 회사에 빌려주고 사용료를 받습니다.

설계를 빌려주는 두 방식

칩 회사가 Arm 의 설계를 가져가는 방식은 크게 둘입니다. 어느 쪽이든 칩이 팔리는 만큼 사용료를 내는 계약이 대개 따라붙습니다.

첫째는 코어 라이선스입니다. Arm 이 다 그려 둔 코어 설계도를 받아 자기 칩에 넣습니다. Cortex 라는 이름이 붙은 코어들이 이렇게 팔립니다.

둘째는 아키텍처 라이선스입니다. 설계도 없이 명령어 집합을 따를 권리만 받습니다. 코어는 칩 회사가 직접 설계합니다. 품이 많이 드는 대신 성능과 전력을 자기 제품에 맞게 다듬을 수 있습니다. 애플의 M 계열 칩이 이렇게 만든 Arm 칩입니다.

어느 쪽이든 칩 회사는 코어 둘레에 메모리 제어기, 그래픽 처리기, 통신 부품을 붙여 칩 하나로 묶습니다. 이렇게 컴퓨터 부품 여럿을 한 칩에 담은 것을 SoC(System on Chip, 시스템 온 칩)라고 부릅니다. 그 칩을 찍어 내는 일은 반도체 공장이 맡습니다.

flowchart TD
    A["Arm · 명령어 집합과 코어를 설계한다"]
    A --> L1["코어 라이선스 · 코어 설계도를 받는다"]
    A --> L2["아키텍처 라이선스 · 명령어 집합만 받는다"]
    L1 --> C["칩 회사 · 받은 코어로 SoC 를 묶는다"]
    L2 --> D["칩 회사 · 코어를 직접 설계해 SoC 를 묶는다"]
    C --> F["반도체 공장 · 칩을 찍어 낸다"]
    D --> F

두 갈래는 코어를 누가 그리느냐에서만 갈립니다. 명령어 집합은 양쪽이 같습니다. 그래서 어느 길로 나온 칩이든 같은 Arm 용 프로그램을 돌립니다.

32비트 Arm 과 64비트 Arm

Arm 명령어 집합은 판을 거듭하며 자랐습니다. 큰 갈림은 Armv8 이라는 판입니다. 이 판에서 64비트 레지스터로 도는 방식이 새로 들어왔습니다.

그때부터 Arm 은 실행 상태를 둘로 가릅니다. 실행 상태는 칩이 지금 어느 규칙으로 도는지를 말합니다. 상태가 바뀌면 레지스터의 폭과 명령을 읽는 규칙이 함께 바뀝니다. 레지스터가 64비트인 상태를 AArch64, 그 전부터 있던 32비트 상태를 AArch32라고 부릅니다.

32비트 쪽에는 Thumb 이라는 명령 모음이 있습니다. 자주 쓰는 명령은 16비트로 짧게 적습니다. 나머지는 32비트 명령으로 섞어 씁니다. 프로그램이 차지하는 메모리가 줄어서 저장 공간이 작은 칩에서 많이 씁니다.

두 상태를 나란히 놓으면 이렇습니다.

AArch32 AArch64
레지스터 폭 32비트 64비트
범용 레지스터 r0~r15 16개 x0~x30 31개
명령 길이 32비트 · Thumb 는 16비트와 32비트를 섞는다 32비트 고정

스마트폰과 서버의 Arm 은 이제 대부분 64비트로 돌아갑니다. 마이크로컨트롤러는 센서나 가전에 들어가 정해진 일 하나를 맡는 작은 칩입니다. 마이크로컨트롤러용 Arm 은 지금도 32비트입니다. 명령도 Thumb 만 씁니다.

Arm 의 세 프로파일

같은 Arm 이라도 맡는 일에 따라 세 갈래로 나뉩니다. 이 갈래를 프로파일이라고 부릅니다. 갈래마다 Cortex 코어 이름에 붙는 글자가 다릅니다.

프로파일 코어 이름 맞춘 일 들어가는 기기
A · Application Cortex-A 운영체제를 올려 여러 프로그램을 돌린다 스마트폰 · 태블릿 · 서버
R · Real-time Cortex-R 정해진 시간 안에 반드시 응답한다 저장 장치 컨트롤러 · 자동차 제동 장치
M · Microcontroller Cortex-M 작고 전기를 적게 쓴다 센서 · 가전 · 웨어러블

A 프로파일 칩에는 MMU(Memory Management Unit, 메모리 관리 장치)가 들어 있습니다. MMU 는 프로그램마다 따로 보이는 주소를 실제 메모리 주소로 바꿔 주는 장치입니다. 이 장치가 있어야 리눅스 같은 운영체제가 프로그램끼리 메모리를 갈라 줄 수 있습니다.

M 프로파일 칩에는 MMU 가 없습니다. 대신 MPU(Memory Protection Unit, 메모리 보호 장치)를 선택으로 둡니다. MPU 는 주소를 바꿔 주지 않습니다. 메모리 구역마다 읽기·쓰기 권한만 겁니다.

MMU 가 없으면 리눅스처럼 프로그램마다 메모리를 따로 보여 주는 운영체제를 올리기 어렵습니다. 그래서 운영체제 없이 프로그램 하나만 돌립니다.

아니면 실시간 운영체제를 올립니다. 실시간 운영체제는 일을 정해진 시간 안에 끝내도록 짠 작은 운영체제입니다. Zephyr 가 그런 예입니다.

M 프로파일 칩은 장치가 보내는 인터럽트에 답하는 일을 많이 합니다. 인터럽트는 장치가 CPU 에게 「지금 처리해 달라」고 보내는 신호입니다. 버튼이 눌리거나 센서 값이 들어오면 이 신호가 옵니다.

인터럽트마다 실행할 함수의 주소는 벡터 테이블이라는 표에 적어 둡니다. 이 표는 메모리 앞쪽에 놓입니다. 인터럽트가 오면 칩은 표에서 그 칸을 찾아 적힌 함수로 건너뜁니다.

칩이 켜지면 맨 먼저 도는 코드가 있습니다. 이 코드를 담은 파일을 시작 파일이라고 부릅니다. 벡터 테이블도 시작 파일 안에 적혀 있습니다.

Arm 은 이 시작 파일의 틀과 공통 함수 모음을 함께 내놓습니다. 이 모음을 CMSIS(Common Microcontroller Software Interface Standard)라고 부릅니다. 칩 회사는 그 틀에 자기 장치의 인터럽트를 채워 넣습니다.

실행 권한의 네 레벨

한 칩 위에서 운영체제와 응용 프로그램이 같은 권한을 가지면 곤란합니다. 응용 프로그램 하나가 다른 프로그램의 메모리를 읽거나 장치를 마음대로 건드릴 수 있기 때문입니다.

그래서 64비트 Arm 은 실행 권한을 네 레벨로 나눕니다. 높은 레벨일수록 할 수 있는 일이 많습니다.

낮은 레벨의 코드가 권한 밖의 일을 하려면 높은 레벨에 넘겨야 합니다. 실행 흐름이 이렇게 높은 레벨로 넘어가는 일을 예외라고 부릅니다. 오류만 가리키는 말이 아닙니다. 파일을 읽어 달라는 부탁도, 장치가 보낸 인터럽트도 이 길로 넘어갑니다.

예외가 오르내리는 레벨이라 이 넷을 예외 레벨(Exception Level)이라고 부릅니다. Exception Level 의 머리글자를 따서 낮은 쪽부터 EL0 · EL1 · EL2 · EL3 이라는 이름을 붙입니다.

EL0 에서는 응용 프로그램이 돌아갑니다. EL1 에는 운영체제의 핵심부인 커널이 있습니다.

EL2 에는 하이퍼바이저가 있습니다. 하이퍼바이저는 한 칩 위에 운영체제 여럿을 나란히 올려 주는 프로그램입니다. 서버 한 대를 여러 가상 머신으로 나눌 때 이것을 씁니다.

EL3 에는 보안 펌웨어가 있습니다. 전원이 들어온 뒤 가장 먼저 도는 코드입니다.

flowchart TD
    E3["EL3 · 보안 펌웨어"]
    E2["EL2 · 하이퍼바이저"]
    E1["EL1 · 운영체제 커널"]
    E0["EL0 · 응용 프로그램"]
    E3 -->|"쓸 수 있는 범위를 내준다"| E2
    E2 -->|"쓸 수 있는 범위를 내준다"| E1
    E1 -->|"쓸 수 있는 범위를 내준다"| E0
    E0 -->|"부탁 · 예외"| E1

아래 레벨은 위 레벨이 내준 범위 안에서만 움직입니다. 응용 프로그램이 파일을 읽으려면 커널에게 부탁해야 합니다. 부탁하는 순간 예외가 일어나 실행 흐름이 EL1 에 미리 정해 둔 코드로 옮겨 갑니다.

서버와 개발 노트북의 Arm

백엔드 개발자는 대개 개발 노트북과 서버에서 Arm 을 만납니다. 애플의 M 계열 맥이 Arm 칩으로 돌아갑니다. AWS(Amazon Web Services) 의 Graviton 처럼 클라우드 업체들도 Arm 칩을 단 서버를 빌려줍니다.

64비트 Arm 은 이름이 둘이라 헷갈리기 쉽습니다. 리눅스 커널은 aarch64 라 부릅니다. macOS 와 컨테이너 이미지는 arm64 라 부릅니다. 둘은 같은 것입니다. x86 의 64비트 판도 x86_64 와 amd64 로 이름이 갈립니다.

어디서 보나 64비트 Arm 64비트 x86
리눅스의 uname -m aarch64 x86_64
macOS 의 uname -m arm64 x86_64
컨테이너 이미지의 플랫폼 표기 linux/arm64 linux/amd64

컨테이너 이미지도 명령어 집합마다 따로 만듭니다. 이미지 안의 실행 파일이 한 명령어 집합용으로 번역돼 있기 때문입니다. Arm 맥에서 따로 정하지 않고 만든 이미지는 linux/arm64 용이 됩니다.

그 이미지를 x86 서버에서 띄우면 exec format error 라는 오류를 내고 바로 멈춥니다. 커널이 실행 파일의 명령어 집합을 알아보지 못했다는 뜻입니다. 다른 명령어 집합을 흉내 내는 에뮬레이터로 돌릴 수는 있습니다. 대신 명령마다 번역을 거쳐 느려집니다.

그래서 두 명령어 집합용 이미지를 함께 만들어 한 이름 아래 묶어 둡니다. 이렇게 묶은 이미지를 멀티 아키텍처 이미지라고 부릅니다. 받는 쪽은 자기 칩에 맞는 것을 골라 받습니다.

지금 쓰는 컴퓨터와 다른 명령어 집합용 실행 파일을 번역하는 일은 크로스 컴파일이라고 합니다. x86 노트북에서 Arm 서버용 프로그램을 만들 때 이 방식을 씁니다.

바이트 순서는 걱정이 덜합니다. 여러 바이트짜리 값을 메모리에 담는 순서를 엔디언이라 합니다.

Arm 은 두 순서를 다 고를 수 있게 되어 있습니다. 그래도 쓰이는 시스템은 거의 전부 리틀엔디언입니다. 리틀엔디언은 작은 쪽 바이트를 앞에 두는 순서입니다. x86 도 리틀엔디언이라 옮길 때 이 부분은 손볼 일이 없습니다.

관련 항목

Arm 이 속하는 상위 분류

명령어 집합 · 컴퓨터 구조 · CPU · RISC · CISC · 마이크로아키텍처

Arm 과 같은 역할을 두고 겨루는 명령어 집합

x86 · x86-64 · RISC-V · MIPS · PowerPC · SPARC

Arm 이 나뉘는 실행 상태와 프로파일

AArch64 · AArch32 · Thumb · Cortex-A · Cortex-R · Cortex-M

Arm 명령이 다루는 하드웨어 요소

레지스터 · 로드-스토어 구조 · 메모리 · 엔디언 · 코어 · MMU · MPU

Arm 이 나누는 실행 권한과 예외 처리

예외 레벨 · 예외 · 인터럽트 · 벡터 테이블 · 시스템 콜 · 커널 · 하이퍼바이저 · 펌웨어

Arm 용 실행 파일과 이미지를 만드는 도구

컴파일러 · 크로스 컴파일 · 툴체인 · GCC · LLVM · Arm Compiler · CMSIS · 멀티 아키텍처 이미지 · 컨테이너 이미지

Arm 칩 위에서 도는 소프트웨어

운영체제 · Linux · Android · Zephyr · 실시간 운영체제 · 베어메탈

Arm 칩이 들어가는 기기와 분야

임베디드 · 마이크로컨트롤러 · SoC · 스마트폰 · 라즈베리 파이 · 사물인터넷

다른 이름: ARM · Arm 아키텍처 · Arm architecture