나의 경우 임배디드 제품 개발 시 윈도우의 비주얼스튜디오에서 에뮬레이션을 통하여 1차 검증을 하고 그 소스를 리눅스로 보내 컴파일하여 최종 펌웨어를 완성한다.
이때, 일일이 소스를 하나 하나 복사하기 귀찮아 배치파일을 사용하여 복사하는데, 이역시 윈도우 탐색기나 콘솔창을 열어 실행하려면 귀찮아 진다.
나는 이 귀찮음을 줄이려고 프로젝트 파일에 src_to_lnx.bat 라는 이름의 배치파일을 만들어 놓고 비주얼 스튜디오에서 버튼을 누르면 실행되어 리눅스와 소스가 동기화 되도록 만들어 어 사용하고 있다.
아직까진 편하다고 생각하며 사용하고 있다.
이렇게 사용하면서도, 머리가 나뻐서인지 나이 먹었다고 자꾸 까먹기 시작하는건지 윈도우라도 포맷하고 다시 만들려면 기억이 잘 안나기 시작한다.
잊어 먹었을 때 보고 하기 위하여 프로젝트에 들은 배치파일을 비주얼 스튜디오에서 버튼으로 등록하는 법을 기록해 놓겠다.
------------------------------------------------------------------------------------------
먼저 배치파일을 준비한다.
여기서는 "src_to_lnx.bat" 파일로 한다.
Visual studio 의 TOOLS 메뉴에 External tools 메뉴가 나온다.
(Visual studio 2013 기준)
Add 버튼을 누룬 후 위처럼 입력한다.
이후 TOOLS 메뉴에 보면 SrcToLinux 라는 메뉴가 생겨있을 것이다.
이를 좀 더 편하게 하고자 한다면 단축버튼을 만드는 방법이 있다.
Toolbar에서 마우스 오른쪽 버튼을 눌러 Build 메뉴를 보이도록 활성화 한 후
나타난 Build Toolbar 리본 오른쪽에 Build Toolbar Options 라고 풍선 도움말이 뜨는 버튼을 누른다.
저기 빨간색으로 동그라미 친 버튼이다. 삼각형이 아래로 향한듯한..
그러면 아래와 같은 창이 뜬다.
Add command 버튼을 눌러 external tool 순번을 선택한 후 , 해당아이템을 선택하여 Modifiy Selection 을 선택하여 이름을 수정한다.
자 완성이다.!
PS: 블로그에서 클립보드 그림을 붙여넣기가 된다면 얼마나 좋겠다. 그림 넣을 때 마다 파일로 저장해야하니 불편하다. -_-;;
2017년 5월 6일 토요일
2017년 5월 5일 금요일
ARM Cortex 와 최적화 - 나눗셈
요즘은 내가 주로 사용하는 SOC (system on chip)의 코어가 cortex 계열이다.
Cortex 계열을 사용하다보면 느끼는 점은, 예전 ARM 920 시절보다
참 많이 빨라지고 좋아졌다 라는 느낌이다.
적어도 정렬문제 (Byte alignment)에서 해방되었다는 것만 해도 큰 축복 같았다.
(이 부분에 대해서는 다음에 한번 글을 써보기로 하겠다.)
현업에서 제품을 개발하는 분들은 공감하실수 있는 부분이지만,
사용자 분들의 요구치는 항상 하드웨어 발전 속도를 앞지르는 것 같다.
특히 내가 있는 영상 음성 보안 분야의 경우 SOC 선택의 폭이 좁아지면서
비슷한 하드웨어로 가격과 성능을 경쟁하는 상태가 되었다.
즉, 같은 하드웨어로 누가 더 많은 성능을 뽑아내어 보다 많은 소비자의 요구 기능을 뽑아내는지 경쟁이 시작된 것이다.
(솔직히.. 이것때문에 매우 힘들다. 일부 업체의 경우 안정성까지 희생해 가면서 성능을 뽑아내는데, 이런 제품을 본 소비자 분들은 저 회사 제품과 같은 성능에 안정성까지 요구하시게 된다. 물론 동일한 가격으로.. ㅜ_ㅜ)
가격이 고정되어 하드웨어를 손 볼 수 없다면 소프트웨어를 손봐서라도 고객의 요구를 만족시켜드려야 프로(?)라 할 수 있지 않겠는가... 라는 자조 섞인 말로 암시를 걸면서 개발자의 본분(?)인 삽질을 열심히 하고 있다.
성능을 조금이라도 더 뽑아낼 수 있는 부분이 있을까를 확인하기 위하여 시간을 핑계로 대강만 봐왔던 Cortex를 면밀하게 검토해 봤다.
헉.. 모든 Cortex 가 곱셈과 나눗셈이 CPU에 내장된 것이 아니었어?!
적어도 Cortex - M 시리즈는 몰라도 Cortex - A시리즈는 되있을 거라 생각했는데 그건 아니었다.
[Cortex 코어별 나눗셈 지원 여부]
<출처: ARM Community : https://community.arm.com>
헉.. Cortex A9 와 Cortex A8이 나눗셈 명령 (SDIV/UDIV)가 기본지원이 아니라고!?
불행히도 내가 쓰는 SOC의 상당수 코어가 Cortex A9와 Cortex A8이었다.
Cortex가 기본적으로 나눗셈 instruction 을 지원하여 왠만해서는 25클럭을 넘지 않으리라 생각하고 만든 부분들을 전면적으로 재 검토 하여야 했다.
C++에서 나눗셈의 제수가 상수인 부분이야 쉬프트 계열 명령으로 자동 대체 되겠지만,
아닌 부분들은 뺄셈으로 구현하는 수 밖에 없게 된다.
그렇다고 피제수가 제수보다 작아질때까지 무조건 루프돌며 빼는 끝을 기약하기 힘든 형태로 뺀다면 32비트 기준으로 최대 40억회 가량의 루프를 돌아야 한다.
클럭으로 따지면 400억 클럭정도까지 소요될 수도 있다는 뜻이다.
그래서 일반적으로 제수와 피제수의 크기차이를 확인하고, 이에따라 방법을 결정하는데 비트 위치에 맞추어 빼는 형식으로 루프 회수를 줄인다.
예를 들어 14112 를 11로 나누는 경우, 무조건 빼가며 루프를 돌 경우 1282번의 루프를 돌아야 한다.
하지만 비트 위치에 맞추어 빼는 형식으로 하면 아래와 같다.
계산은 2진수 기준으로 한다.
14112 는 2진수로 11011100100000 (2진수 14자리)
11은 2진수로 1011 (2진수 4자리)
(루프 1회 - 13번 비트부터 제수와 동일한 4자리 비교)
11011100100000 -> 101100100000 (피제수)
10110000000000 (제수)
10000000000 (몫) 피제수가 더 크므로 1
(루프 2회 - 12번 비트)
0101100100000 (피제수)
1011000000000 (제수)
10000000000 (몫) 제수가 더 크므로 0
(루프 3회 - 11번 비트)
0101100100000 -> 100000 (피제수)
101100000000 (제수)
10100000000 (몫) 피제수가 더 크므로 1
(루프 4회 - 10번 비트)
00000100000 (피제수)
10110000000 (제수)
10100000000 (몫) 제수가 더 크므로 0
왠만하면 코드 재 사용을 하기 위하여 어셈블리는 되도록 손을 대지 않으려 하지만, 상황은 나에게 계속 어셈블리를 할 수 밖에 없는 상황이 되어가는것 같다.
1999년 경 C / C++을 잘 몰라 순수 어셈블리로 32bit OS를 개발했던 적이 있었다.
GPU 도움을 받지 못해 UI표현 속도가 너무 느려, 어디까지나 MMU 제어와 GUI, 멀티미디어에 대한 스터디 차원의 일이었지만, 이 작업이 인정받아 서울의 한 OS개발 프로젝트를 진행하는 개발사에 취직한 적이 있었다.
이 때 월요일에 출근해서 토요일까지 감금당하다 시피 일을 하다보니, 젊은 나이에 쇼크가 컷던지 OS 개발 분야를 포기하고, 지금과 같이 영상 , 음성 보안이나 신호처리로 전향하게 된 것 같다.
이 분야는 여러가지 SOC를 사용해야 하다보니 효율성을 위하여 재 사용 가능한 코드 개발을 지향하게 되었고, 때문에 어셈블리 사용을 최대한 자재하게 되었다.
세상은 그런 게으름(?)을 용납하지 않으려는지 아키텍쳐간 최적화를 하라고 요구한다.
사용자의 요구를 훨 씬 뛰어넘는 CPU가 나오기 전까진 앞으로도 이 삽질을 계속 해야만 할것 같다...
PS: 블로그에서 표그리기 왜이리 힘들까요 ㅠ_ㅠ
Cortex 계열을 사용하다보면 느끼는 점은, 예전 ARM 920 시절보다
참 많이 빨라지고 좋아졌다 라는 느낌이다.
적어도 정렬문제 (Byte alignment)에서 해방되었다는 것만 해도 큰 축복 같았다.
(이 부분에 대해서는 다음에 한번 글을 써보기로 하겠다.)
현업에서 제품을 개발하는 분들은 공감하실수 있는 부분이지만,
사용자 분들의 요구치는 항상 하드웨어 발전 속도를 앞지르는 것 같다.
특히 내가 있는 영상 음성 보안 분야의 경우 SOC 선택의 폭이 좁아지면서
비슷한 하드웨어로 가격과 성능을 경쟁하는 상태가 되었다.
즉, 같은 하드웨어로 누가 더 많은 성능을 뽑아내어 보다 많은 소비자의 요구 기능을 뽑아내는지 경쟁이 시작된 것이다.
(솔직히.. 이것때문에 매우 힘들다. 일부 업체의 경우 안정성까지 희생해 가면서 성능을 뽑아내는데, 이런 제품을 본 소비자 분들은 저 회사 제품과 같은 성능에 안정성까지 요구하시게 된다. 물론 동일한 가격으로.. ㅜ_ㅜ)
가격이 고정되어 하드웨어를 손 볼 수 없다면 소프트웨어를 손봐서라도 고객의 요구를 만족시켜드려야 프로(?)라 할 수 있지 않겠는가... 라는 자조 섞인 말로 암시를 걸면서 개발자의 본분(?)인 삽질을 열심히 하고 있다.
성능을 조금이라도 더 뽑아낼 수 있는 부분이 있을까를 확인하기 위하여 시간을 핑계로 대강만 봐왔던 Cortex를 면밀하게 검토해 봤다.
헉.. 모든 Cortex 가 곱셈과 나눗셈이 CPU에 내장된 것이 아니었어?!
적어도 Cortex - M 시리즈는 몰라도 Cortex - A시리즈는 되있을 거라 생각했는데 그건 아니었다.
[Cortex 코어별 나눗셈 지원 여부]
Cortex | Thumb 모드 지원 | 일반모드지원 |
A57 | 지원 | 지원 |
A53 | 지원 | 지원 |
A15 | 지원 | 지원 |
A9 | 지원안함 | 지원안함 |
A8 | 지원안함 | 지원안함 |
A7 | 지원 | 지원 |
A5 | 지원안함 | 지원안함 |
R7 | 지원 | 지원 |
R5 | 지원 | 일부 지원 |
R4 | 지원 | 지원안함 |
M4 | 지원 | 지원안함 |
M3 | 지원 | 지원안함 |
M1 | 지원안함 | 지원안함 |
M0 | 지원안함 | 지원안함 |
M0+ | 지원안함 | 지원안함 |
헉.. Cortex A9 와 Cortex A8이 나눗셈 명령 (SDIV/UDIV)가 기본지원이 아니라고!?
불행히도 내가 쓰는 SOC의 상당수 코어가 Cortex A9와 Cortex A8이었다.
Cortex가 기본적으로 나눗셈 instruction 을 지원하여 왠만해서는 25클럭을 넘지 않으리라 생각하고 만든 부분들을 전면적으로 재 검토 하여야 했다.
C++에서 나눗셈의 제수가 상수인 부분이야 쉬프트 계열 명령으로 자동 대체 되겠지만,
아닌 부분들은 뺄셈으로 구현하는 수 밖에 없게 된다.
그렇다고 피제수가 제수보다 작아질때까지 무조건 루프돌며 빼는 끝을 기약하기 힘든 형태로 뺀다면 32비트 기준으로 최대 40억회 가량의 루프를 돌아야 한다.
클럭으로 따지면 400억 클럭정도까지 소요될 수도 있다는 뜻이다.
그래서 일반적으로 제수와 피제수의 크기차이를 확인하고, 이에따라 방법을 결정하는데 비트 위치에 맞추어 빼는 형식으로 루프 회수를 줄인다.
예를 들어 14112 를 11로 나누는 경우, 무조건 빼가며 루프를 돌 경우 1282번의 루프를 돌아야 한다.
하지만 비트 위치에 맞추어 빼는 형식으로 하면 아래와 같다.
계산은 2진수 기준으로 한다.
14112 는 2진수로 11011100100000 (2진수 14자리)
11은 2진수로 1011 (2진수 4자리)
(루프 1회 - 13번 비트부터 제수와 동일한 4자리 비교)
11011100100000 -> 101100100000 (피제수)
10110000000000 (제수)
10000000000 (몫) 피제수가 더 크므로 1
(루프 2회 - 12번 비트)
0101100100000 (피제수)
1011000000000 (제수)
10000000000 (몫) 제수가 더 크므로 0
0101100100000 -> 100000 (피제수)
101100000000 (제수)
10100000000 (몫) 피제수가 더 크므로 1
00000100000 (피제수)
10110000000 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 5회 - 9번 비트)
0000100000 (피제수)
1011000000 (제수)
10100000000 (몫) 제수가 더 크므로 0
0000100000 (피제수)
1011000000 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 6회 - 8번 비트)
000100000 (피제수)
101100000 (제수)
10100000000 (몫) 제수가 더 크므로 0
101100000 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 7회 - 7번 비트)
00100000 (피제수)
10110000 (제수)
10100000000 (몫) 제수가 더 크므로 0
10110000 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 8회 - 6번 비트)
0100000 (피제수)
1011000 (제수)
10100000000 (몫) 제수가 더 크므로 0
1011000 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 9회 - 5번 비트)
100000 (피제수)
101100 (제수)
10100000000 (몫) 제수가 더 크므로 0
101100 (제수)
10100000000 (몫) 제수가 더 크므로 0
(루프 10회 - 4번 비트)
100000 -> 1010 (피제수)
10110 (제수)
10100000010 (몫) 피제수가 더 크므로 1
10110 (제수)
10100000010 (몫) 피제수가 더 크므로 1
(루프 11회 - 3번 비트)
1010 (피제수)
1011 (제수)
10100000010 (몫) 제수가 더 크므로 0
1011 (제수)
10100000010 (몫) 제수가 더 크므로 0
위와 같이 11회의 루프에 연산을 마쳤다.
물론 사전에 bit 개수를 알아내는 작업이 필요하거나, 무조건 32 - 제수 비트수 만큼의 루프를 돌아야 하지만, 최대 32회의 루프면 충분하다.
그래도 CPU에서 직접 지원하는 명령보다 매우 느리다는 점은 여전하다.
Cortex도 A15급이상이 아닌 이상 되도록이면 상수가 아닌 변수간 나눗셈 연산은 피하거나 쉬프트로 대치하는것이 좋다는 것을 알게 되었다.
왠만하면 코드 재 사용을 하기 위하여 어셈블리는 되도록 손을 대지 않으려 하지만, 상황은 나에게 계속 어셈블리를 할 수 밖에 없는 상황이 되어가는것 같다.
1999년 경 C / C++을 잘 몰라 순수 어셈블리로 32bit OS를 개발했던 적이 있었다.
GPU 도움을 받지 못해 UI표현 속도가 너무 느려, 어디까지나 MMU 제어와 GUI, 멀티미디어에 대한 스터디 차원의 일이었지만, 이 작업이 인정받아 서울의 한 OS개발 프로젝트를 진행하는 개발사에 취직한 적이 있었다.
이 때 월요일에 출근해서 토요일까지 감금당하다 시피 일을 하다보니, 젊은 나이에 쇼크가 컷던지 OS 개발 분야를 포기하고, 지금과 같이 영상 , 음성 보안이나 신호처리로 전향하게 된 것 같다.
이 분야는 여러가지 SOC를 사용해야 하다보니 효율성을 위하여 재 사용 가능한 코드 개발을 지향하게 되었고, 때문에 어셈블리 사용을 최대한 자재하게 되었다.
세상은 그런 게으름(?)을 용납하지 않으려는지 아키텍쳐간 최적화를 하라고 요구한다.
사용자의 요구를 훨 씬 뛰어넘는 CPU가 나오기 전까진 앞으로도 이 삽질을 계속 해야만 할것 같다...
PS: 블로그에서 표그리기 왜이리 힘들까요 ㅠ_ㅠ
아키텍쳐의 특성과 어셈블리
2000년대 초 중반만큼 많은 CPU아키텍쳐들이 각축을 벌였던 때가 없었던 것 같다.
그 당시 PC쪽은 Windows 진영의 x86 아키텍쳐와 MAC 진영의 Power PC 아키텍쳐로 평정이 되다시피 했지만, 임베디드 시스템이 활성화 되면서 그 시장에서의 주도권을 잡고자 Power PC, x86, ARM, MIPS, AVR, DSP 등, 여러 아키텍쳐에서 상당히 공격적으로 마케팅을 하고 제품을 양산하기도 했다.
근래 들어 다시 정리가 되고 PC쪽에서는 MAC 진영 마저 돌아 서면서 x86으로 평정되는 분위기가 되어가고, 임베디드는 ARM으로 평정되어 가고 있고, 간간히 모바일이나 게임기에서 x86진영이 자리를 잡으려고 노력하는 편이다.
글쓰는 본인 역시 저 시기를 겪어와서 x86, ARM, Power PC , ARM, MIPS, DSP 를 겪어 보게 되었다. (AVR은 검토만 하다 끝났다. )
이런 계시글에서 나열한 모든 아키텍쳐를 다룬다는 것은 글 쓰는 이로서도 힘든 일이고 요즘처럼 x86과 ARM이 대세로 자리 잡은 시절에는 불필요한 옛날 이야기를 주절되는것과도 같을 수 있다고 생각되어 주를 x86과 ARM으로 이야기하고 , 간간히 비교할 만한 부분에서 다른 아키텍쳐를 언급하려 한다.
1. x86 아키텍쳐
1) 정의와 변천
intel이라는 기업을 많이 들어보았을 것이다.
TV 광고를 통하여, 또는 잡지등을 통하여 많이 접 할수 있는 세계적인 반도체 메이커다.
이 intel 에서 만들어 세계적인 회사로 자리 잡을 수 있도록 해준 아키텍쳐가 x86이다.
x86 아키텍쳐는 intel이 PC용으로 만든 CPU칩의 OP-CODE 체계로 칩 이름에 붙인 제품 명의 끝 숫자가 86으로 끝나기 때문에 x86이라 부른다.
필자가 게을러 다 표시는 못했지만, 이후 Pentium II , Pentium III , core , i 시리즈등이 나왔다.
2) 16bit
요즘같이 64bit로 바뀌어 가는 시대에 16bit에 대해 논한다는 것을 단순히 옛날 이야기로 치부하는 사람도 있을 것이다.
하지만 잘 보면 우리주위에는 16비트, 또는 8비트 CPU들이 아직도 많이 사용되고 있다.
왜 이런 구형방식의 CPU가 사용되는 것일까?
첫번째는 가격이다.
8bit 계열인 8051이나 PIC 계열 CPU는 2$ 보다 싼 제품이 많다.
물론 32bit인 Coretex M 계열 제품 중에서도 10$ 밑의 제품들이 있지만 대부분 flash 메모리를 별도 장착해야 하거나 별도의 부가비용을 필요로 하는 경우가 많다.
또한 1$라는 가격차이도 제품 가격에서는 무시할 수 없다.
보통 제품을 양산할 때 최소 500개에서 1만개 가량 생산한다.
여기서 1$의 차이는 한번 양산 시 500$에서 1만$의 차이라는 뜻이 된다.
두번째는 크기이다.
단순한 아키텍쳐일 수록 크기를 작게 만들 수 있다.
실제 8bit CPU들의 경우 가로 세로 7mm 이하의 제품들도 존재한다.
여기에 CPU외에 플레시메모리와 기타 장치까지 내장되어 있어 빠른 속도를 필요로 하지 않는 다면 적은 공간과 비용을 소요하는데에 요긴하게 사용된다.
세번째는 신뢰성이다.
단순할 수록 신뢰성이 강하다.
따라서 일부 제품의 경우 별도의 8bit나 16bit CPU를 사용하여 메인 CPU의 동작 여부를 감시시키기도 한다.
일부 관심있는 사람들은 인공위성이나 우주선에 8bit나 16비트 CPU를 쓰고, 32bit CPU를 쓴 것도 얼마 안된다는 것을 알 것이다.
신형 CPU들은 성능이 높고 집적해야 하는 로직이 많아져 공정을 낮출 수 밖에 없게 된다.
공정을 낮춤과 함께 사용하는 전압도 낮아지게 되기 때문에 같은 전압차의 노이즈에도 높은 공정이 낮은 공정의 칩보다 강해지게 된다.
그렇다면 16bit x86은 어디에 사용할까?
현재 필자가 주로 본 곳은 모터제어였다.
8086의 확장판인 80186/80188 을 주로 사용하며 이마저도 사실 ARM의 Coretex-M3에 밀리는 중이다.
그래도 80186계열은 아래와 같은 장점으로 아직도 적지 않은 사용자가 존재한다.
첫번째로 DOS와 호환이 된다.
즉 윈도우의 Visual Studio 로 DOS옵션에 맞춰 작성한 소프트웨어가 수정없이 그대로 실행이 가능하다는 뜻이다.
물론 플레시와 연동을 위하여 DOS를 애뮬레이션 시켜줄 수 있는 부트로더가 필요하다.
이미 시중에 나온 80186/80188 칩에는 DOS를 에뮬레이션 해주는 부트로더에 대한 자료가 있으므로 활용하면 된다.
두번째로 빠른 편이다.
40Mhz 스피드의 모델까지 존재하며, 같은 스피드라도 8051같은 8bit CPU보다 빠른 효율을 보인다.
이런 장점을 가진 186/188 계열 칩을 자신의 마음대로 주무르기 위해서는 위에서 설명한 것 처럼 DOS를 애뮬레이션 해주는 부트로더가 필요하다.
어플리케이션을 실행하기 위해서 필요한 DOS의 기능은 화면표시 , 실행파일 로딩, 실행보조기능이다.
DOS의 주요기능은 interrupt 0x21, 0x20번을 통해 실행된다.
PC에서는 화면표시를 비디오카드를 통하여 모니터로 했으나 임베디드 분야에서는 시리얼포트를 통하여 한다.
따라서 시리얼 포트를 통해서 프로그래머가 표시하고자 하는 문자열을 출력해야 한다.
따라서 INT 21h 의 9번이나 몇몇 화면출력 기능들을 애뮬레이션 해줘야 한다.
시리얼 포트를 통하여 글자를 출력하기 때문에 비디오카드처럼 그래픽을 프레임버퍼를 통하여 출력하는 기능을 구현하기란 매우 어렵다.
DOS의 실행파일은 COM 파일과 EXE파일 두가지가 있다.
COM은 쉽게 실행 코드 바이너리 이미지다. 최대 64Kbyte 까지 크기를 가질 수 있고
통째로 메모리에 로딩만하면 된다.
반면 EXE는 64Kbyte보다 큰 실행 파일을 만들 수 있지만 재배치 테이블을 분석해서 코드와 데이터들을 재 배치하고 내용을 일부 수정해 주어야 한다.
또한 C library는 기본적으로 부동소숫점 연산을 아무렇지도 안게 시도를 한다.
이에 대한 예외처리를 해줘야 한다.
이런 부트로더를 구현 할 때에 자주 사용되는 기능이 스텍관리다.
8086/8088보다 80186/80188에서 편해진 기능이 스텍 관리 명령들이 추가된 것이다.
enter , leave , pusha, popa 와 같은 명령들이 스텍관리를 편하게 해주고 실행코드 크기를 줄여준다.
필자는 같이 일하는 후임 연구원들에게 8051이나 PIC와 같은 8bit 마이컴을 다룰땐 한번정도 전체를 어셈블리로 만들어 보고 16bit CPU를 다룰때에도 어느정도 어셈블리로 함수몇개정도는 만들어 봐야 한다고 말하고 있다.
8086 - 8088 - 80186 - 80286으로 변천 해 오면서 양산성이나 보호모드 지원과 같은 기능도 추가 되었지만, opcode 여러 개를 써야 할 것을 하나로 만들어 코드를 좀 더 빠르게 실행하고자 하는 부분도 추가되었다.
이런 기능이 추가된 다는 것은 어셈블리로 코딩이 점점 편해진 다는 뜻으로 많은 어셈블리 사용자들이 매우 환영할 만한 일이었다.
당시 어셈블리의 사용 빈도는 높았다.
DOS 특성 상 주변기기 제어를 DOS 드라이버를 만들어 사용하는 것 보다 어플리케이션에서 직접 제어하는 것이 편했었고, 8bit 시절에 그랫듯 16bit도 메모리와 속도가 한계에 달하기 시작하면서 이를 조금이라도 해결하고자 어플리케이션에서도 어셈블리가 사용되기 시작했 었다.
12월 26일 발행
3) 32 bit
32비트 초/중기 시절에 와서도 어셈블리는 여전히 많이 쓰였다.
생각보다 DOS의 수명이 길었던 탓과 윈도우의 자원관리 성능 때문이었다.
32bit로 와서 DOS의 1MByte 메모리 영역제어의 한계를 넘어서고자 새로운 OS를 필요로 하였는데, 대표적인 것이 Windows 와 OS/2 였다.
하지만 초기에 나온 OS들이 그렇듯, 사용할만한 어플리케이션 부족이라는 문제와 가정에서의 컴퓨터 활용 용도에서 적지않은 비중을 차지하는 것이 게임이었는데, 새로 나온 GUI 기반 OS들은 그래픽카드 자원 사용을 어플리케이션과 타협하는 부분에 대한 배려가 매우 부족했다.
결국 게임 개발자들은 그래픽카드와 주변기기 성능을 최대한 끌어내기 위해 DOS를 계속 사용하였고 사용자들 역시 게임을 즐기기 위하여 DOS를 계속 사용했다.
게임 개발자들 역시 메모리 사용의 한계를 넘기 위하여 DOS에서 32비트 모드를 사용하고자 했는데 이때 사용한 것이 어셈블러와 Dos-extender 였다.
하지만 Dos-extender를 사용한 다 하더라도 메모리만 해결 될 뿐, 아직 문제는 많았다.
당시 PC 사용자들에게 제일 어려운 문제는 게임등을 실행할 때 자신의 비디오카드나 오디오카드등의 주변기기가 무었인지를 설정 해 주어야 한다는 것과 주변기기를 설치 할 때 장치간 서로 충돌하지 않도록 자원을 설정 해 주어야 한다는 것이었다.
즉, 개발자 뿐 아니라 PC사용자도 자신의 컴퓨터에 대해 잘 알아야 하는 시절이었던 것이다.
이를 해결하려면 32bit OS의 표준화와 게임등을 위하여 적절한 자원 몰아주기 기능의 지원, 주변 기기간 자원 자동 할당 기능이 필요했다.
이런 필요를 느끼던 시절이다 보니, OS독점이라는 빌게이츠의 자리를 차지하고자 32비트 OS를 개발해서 내놓으려는 사람들이 여럿 나타나기 시작했다.
필자도 그중 한 사람이었다.
당시 거의 모든 사람이 비슷한 시작선 상에 있었기 때문에 누가 먼저 OS와 사용자들이 사용하고싶어 하는 어플리케이션을 만드냐가 성공의 관건이었다.
다른 선례를 봐도 알겠지만, 사용자들은 OS의 안정성이나 완성도 보다 어플리케이션을 보고 OS를 택하게 되기 때문에 소수의 개발자가 OS를 만든다는 것은 자동차를 엔진만 완성한 다음 자기가 만들 차를 고객들이 사주길 바라는 것과 같았다.
그래도 그런 도전이 나쁘지 않았던 점은 그 시절 개발자들이 OS를 만들기 위해 노력하다 보니 CPU에 대한 이해도가 높은 편이었다는 것이다.
32bit OS의 초기에도 역시 난립하는 장치들에 대한 device driver가 부족하다는 것이 문제였는데, 특히 device driver를 읽어 실행(loading)하기 전인 booting 할 때 , OS를 저장장치에서 메모리로 읽어 들이는 bootloader라는 프로그램이나 OS 운용 초기 부분에는 여전히 16bit로 운영되는 BIOS에 의지해야 한다는 것이었다.
DOS가 존재하는 한 BIOS는 16bit로 유지되어야 할 필요가 있었다.
따라서 32bit 보호모드 상에서 16bit real mode 코드를 실행 할 수 있는 가상86모드 (virtual 86 mode)의 안정적인 운용이 중요 했다.
시스템 상에서 16bit 코드를 제거한다는 것은 DOS를 더 이상 사용하지 못하게 한다는 것이고, 그 뜻은 32bit OS상에서도 게임을 돌릴 수 있도록 자원운용을 할 수 있어야 한다는 뜻이었다.
Direct X가 7.0 이상으로 자리 잡아갈 때 까지 게임 제작자 들은 OS플렛폼으로 DOS를 선호하는 경향이 많았다.
16bit코드를 가상86모드로 사용한다는 것은 보안 밎 안정성에 적지 않은 추가 노력이 필요하다는 뜻이다.
가상86모드로 돌리는 코드는 OS의 커널이 아닌 검증되지 않은 외부 코드를 사용한 다는 뜻이기 때문이다.
또한 OS커널을 통하지 않은 I/O 에대한 접근을 허용해야 하기 때문에 이로 인한 안정성 저하를 막기 위하여 많은 노력이 필요했다.
Device driver 역시 하드웨어 제작사가 지원을 해주어야 가능하기 때문에 인지도 없는 OS의 안정성은 본의 아니게(?) 낮아질 수 밖에 없었다.
현재의 linux의 드라이버들은 많은 개발자들의 노력 덕에 이루어 진 것이라 할 수 있다.
32bit OS들이 자리를 잡아 가면서 어셈블리의 활용성은 점점 낮아지는 듯 했다.
하지만 인간의 눈높이는 지칠 줄 모르고 높아만 갔다.
8bit에 128Kbyte 메모리를 가지고 잘 사용하다가 16bit에 640kbyte 메모리를 사용하는 시대로 바뀌면서 10년 이상 유지 될 것만 같았지만 얼마 안되 더 높은 성능을 요구받았고 결국 32bit 시대에 와서도 사용자의 요구 사항은 시스템을 넘어 서고야 말았다.
특히 멀티미디어 시대가 오면서 연산량은 대폭 늘어 멀티미디어를 위한 MMX, SSE와 같은 SIMD계열 전용 명령을 추가하게 되었다.
결국 이마저 부족하여 Graphic card가 CPU와 같은 연산을 하게 되는 시대까지 맞이하게 된다.
이러한 멀티미디어 전용 명령이 나오면서 인라인 어셈블리 명령의 형식으로 어셈블리의 사용빈도가 높아지게 된다.
컴파일러의 성능도 많이 발전하여 어설프게 어셈블리로 만드는 것 보다 더 빨라지게 되었지만, 병렬연산을 수행하는 SIMD계열 명령을 순차/직선적인 연산을 위한 언어인 C/C++ 가 최적화하여 사용하기에는 한계가 있었다.
결국 제대로 된 성능을 내기 위해서는 인라인 어셈블리라는 형식으로 구현하는 경우가 많았다.
가속 관련된 부분은 나중에 따로 연재하기로 하겠다.
또한 32bit에서 와서 CPU의 속도 증가를 위한 여러가지 기술들이 들어가면서 , 이 기술들이 제 성능을 내기 위해서 개발자나 컴파일러 제작자가 해야 할 일이 생겼다.
최적화라고 부르는 것이 그것이다.
먼저 pipe line이 도입되면서 branch (점프 관련 명령들, 분기들..) 사용을 줄여야 했고,
Super scalers 가 도입되면서 명령들의 순서에 대해서도 신경을 써야 했다.
자세한 최적화 역시 나중에 따로 연재를 하면서 자세하게 다루기로 하겠다.
이렇듯, CPU를 개발하는 하드웨어 개발자 들도 시스템의 성능을 조금이라도 높이기 위하여 일부 제약이 생기더라도 새로운 기술을 도입하게 됨으로서 소프트웨어 개발자들은, 성능을 높이기 위해 최적화라는 과제가 생기게 되었다.
32bit에 와서 임베디드가 활성화 되기 시작하면서 여러가지 아키텍쳐들이 나왔다.
MIPS, ARM, DSP 등 여러 아키텍쳐가 나왔지만, 필자가 겪어본 것들 중에선 x86이 같은 클럭(Clock)대 비 성능이 x86계열이 좋았다.
Intel과 AMD에서 지속적이고 많은 투자를 해서 효율을 높여서가 아닐까 추측한다.
실제로 500Mhz 18$짜리 x86계열 CPU가 다른 10~20$하는 800Mhz 속도를 가진 ARM이나 MIPS보다 빠른 성능을 내는 경우를 보았다.
물론 ARM도 최근 들어 여러가지 기술들을 내장하면서 많이 빨라지긴 했지만, 추가하는 기술 중 적지 않은 수가 이미 x86에서 예전부터 도입해서 쓰는 기술이었다.
ARM이 성능보다 저전력을 목표로 만든 아키텍쳐이긴 하지만, 사용자가 임베디드, 특히 모바일쪽에서 요구하는 성능이 높아지다 보니, 점점 PC를 따라가게 되는 형태가 되어가는 것 같다.
실제로 요즘 임베디드 제품들이 저전력 구현이 코어 자체가 소모하는 전력 비율을 줄이는 것 보다 아무 일도 안하는 대기시에 CPU의 사용하지 않는 부분을 꺼서 소비를 낮추거나 아예 저전력 저성능 코어와 고성능 고전력 코어를 같이 내장해서 성능이 높게 필요하지 않을 때에는 저전력 코어로, 높은 성능을 필요할 때에는 높은 코어를 돌리는 방식도 사용하고 있다.
물론 이런 방식의 구현이 사용자가 특별한 일을 안 할 때 대기 시간에 대한 배터리 효율이 올라가긴 하지만, 열심히 무언가를 할 때에 대한 효율이 좋아지는 것이 아니라 가끔은 사용자를 기만하는 것이 아닌가 하는 생각이 들기도 한다.
32bit는 약 4GByte 의 주소영역을 지원하는 데, 주변기기등이 사용하는 공간을 제하고 나면 3Gbyte 남짓한 용량만큼 RAM을 장착 할 수 있다.
대형 어플리케이션이 많아지면서 3GByte 라는 메모리 공간으론 부족해 지게 된다.
특히 멀티미디어 분야가 심한데 HD 해상도 그림 한장의 크기가 약 8MByte 가량 차지하므로 3GByte 용량에 300장 가량밖에 못 들어가고 UHD 해상도에서는 그 수는 훨씬 줄어들게 된다.
부족한 주소공간을 확장하기 위하여 16bit에서 80286이 selector 를 사용하여 16MByte 영역을 16bit 주소공간에 연결하여 사용할 수 있도록 한 것과 같이 PAE라는 기능을 도입하여 page table director 에 64bit address 를 지정할 수 있도록 하여 64bit 영역의 어느 곳이라도 32bit 주소공간에 배치 할 수 있도록 하였다.
PAE를 사용해도 어플리케이션은 크게 신경 쓸 필요는 없으나 OS kernel과 device driver는 영향이 많았다.
PAE는 64bit로 가는 과도기적 과정에서 나온 기술이라 크게 신경을 쓸 필요는 없어보인다.
12월28일 발행
4) 64bit
다시 한번 과거 호환성이 발목을 잡는 때가 왔다.
Intel은 32bit에서 16bit가 발목을 잡혔으나, 64bit에서는 새롭게 시작을 하고 싶었다.
IA-64는 기존 x86계열과 호환을 배제하고 성능과 효율 만을 생각하며 새로 디자인 하였다.
기존 x86 32bit(IA-32) 호환을 위한 부분도 추가를 해 놓았지만 성능은 좋지 않았고, 오로지 새로운 64bit에만 집중 했다.
기존 32bit 어플리케이션들은 새로운 64bit CPU에서 형편없는 성능을 보여주었고, 아직 64bit 어플리케이션이 충분치 않은 상황은 사용자들을 혼란에 빠트렸다.
AMD는 이런 상황을 기회로 여기고 기존 IA-32를 확장한 AMD64를 내놓았다.
AMD64는 기존 IA-32를 호환하는 명령셑이기 때문에 별다른 성능 저하 없이 32bit와 64bit어플리케이션을 사용할 수 있게 되었다.
물론 기존 호환성을 구현하기 위해 더욱 복잡해지고 그만큼 64bit에 대한 성능적 배려가 줄어드는 단점은 있었으나 사용자들은 열광했다.
결국 Intel도 입장을 바꾸고 em64t 라는 AMD64 호환 아키텍쳐를 내놓게 되고 후에는 이름도 Intel64로 바꾸게 된다.
64 bit에선 SSE 기능이 기본으로 들어가 있기 때문에 XMM레지스터를 기본 레지스터로 대우하여 사용한다.
XMM레지스터는 함수 호출시 아규멘트를 넣는 용도로도 사용하게 된다.
64bit 모드에서 어셈블리를 사용하려는 사용자는 XMM레지스터 관리에도 신경을 써야 한다.
사실 필자는 64bit 계열에 대한 개발을 직접적으로 해본 경험이 없어 이 정도로 줄이고 ARM에 대해 설명하기로 하겠다.
2. ARM
1) 정의와 변천
초기엔 생소했지만 스마트폰이 많이 보급되면서 지금은 접하기 쉬운 아키텍쳐명이 ARM일 것이다.
ARM 아키텍쳐는 ARM이라는 회사가 직접 CPU를 만들어 파는 것이 아닌 IP(Intellecual property)를 파는 업체다.
즉 CPU의 소스코드를 파는 업체라는 뜻이다.
CPU의 소스코드라 하면 일부 분들은 당황 할 수 있을 것이다.
“CPU가 하드웨어인데 소프트웨어마냥 소스코드라니.. 회로도 말인가?”
초기 반도체를 설계할 때에는 로직도를 그려 제작하였다.
하지만 반도체 기술의 발전으로 로직 자체를 언어로서 표현하여 로직간의 관계를 프로그래밍 하여 테스트할 수 있는 프로그래밍 가능한 칩(CPLD,FPGA)에 넣어 시험해 볼 수 있게 되었다.
일부 제품은 반도체를 직접 ASIC으로 만들지 않고 CPLD나 FPGA 상태로 양산하기도 할 정도로 가격과 성능이 좋아 졌다.
이런 반도체 개발 방식의 변경은 각종 하드웨어 기능을 소스로서 거래를 가능하게 했고, 이렇게 CPU소스부분만 판매하는 업체 중 하나가 바로 ARM 인 것이다.
소프트웨어를 제작하는 사람이 사용할 라이브러리를 구매하여 사용하듯, ARM을 가지고 SOC(System on chip)을 만들고자 하는 사람은 구매하고자 하는 ARM의 버전을 결정하고 주변기기로 사용할 IP들도 같이 구매하여 서로 연결되고 관리하는 부분의 코드를 추가하여 완성하게 된다.
물론 코드만 완성하고 FPGA로 디버깅을 한다고 해서 끝나는 것은 아니다.
FPGA 와 ASIC간의 특성 차이도 있기 때문에 완성된 코드를 디자인 하우스에 맡겨 최적화 및 특성 조정을 한 후 반도체 제작 공정에 들어가게 된다.
ARM과 x86의 대표적인 차이라면 RISC와 CISC다.
RISC는 대부분의 연산을 레지스터 상에서 하기 때문에 한 클럭에 수행되는 명령이 적다.
대신 명령 하나하나가 단순해서 구현하는 시스템의 속도를 높이기가 수월하다.
하지만 intel이 x86의 성능 향상을 위하여 많은 투자를 하면서 이 경계가 모호해졌다.
펜티엄 이후부턴 내부적으로 CISC명령을 RISC수준의 명령으로 풀어 적절한 파이프라인에 할당하는 슈퍼스케일러스가 적용되었기 때문이다.
다른 연재에서도 다루겠지만 이런 구조는 x86을 보다 빠르게 만드는 장점이 되기도 했지만, x86와 같은 CISC특유의 강점인 쉬운 원자화구현을 더이상 하기 어렵게 만드는 원인이 되기도 하였다.
사실 요즘처럼 멀티코어가 기본으로 쓰이게 된 시절에는 기존 x86의 원자화는 의미가 없을태니 강점이 없어졌다고 할 수 없다.
ARM은 초기 저전력 고효율을 목표로 시작한 CPU다보니 MMU조차 없었다.
임베디드 환경에서는 가상메모리로 PC처럼 하드디스크로 메모리 스와핑을 할 일이 없을 것이라는 생각이었던 것 같다.
하지만 리눅스가 임베디드로서 본격적으로 사용되기 시작하고, 저가 CPU를 여러개 쓰는 것 보다 고성능 CPU하나에 멀티테스킹을 사용하는 것이 효율적으로 생각되면서 ARM은 MMU를 추가하게 된것 같다.
공정이 점점 낮아지면서 저 전력으로 보다 많은 로직을 내장 할 수 있게 되자 JAVA 가속기능과 MAC연산기능,SIMD명령등을 추가하게 된다.
여기서 잠깐 SIMD에 대해 언급하고자 한다.
간혹 SIMD 명령이 DSP와 비슷할 것이다라고 예상하는 분들도 계실 것이지만, 널리 쓰이는 DSP들은 SIMD 아키텍쳐가 아닌 병렬 명령실행이나 입력가 출력 메모리 영역의 분리를 통한 순차 연산등을 사용한다.
초기에는 DSP는 멀티미디어와 같은 배열 연산이나 부동소숫점 연산을 빠르게 할 수 있다는 장점을 가지고 있었지만, 범용 CPU들이 슈퍼스케일러스와 SIMD명령 내장으로 오히려 범용 CPU가 DSP보다 빠른 경우도 많다.
2) 개발 시 발생할 수 있는 문제
필자가 ARM으로 개발 할 때 제일 두려웠던건 오류(BUG)였다.
개발자가 자신이 만든 소프트웨어의 BUG를 두려워하는건 당연하지만, 여기서 말하는 것은 CPU의 BUG다.
ARM의 의도는 자신이 판매한 IP를 사용하는 CPU간에는 컴파일러가 서로 호환되도록 하는 것이었을탠데 실제로는 그러질 못했다.
CPU제작자들은 구입한 IP를 이식하는 과정에서 실수가 발생하는 경우가 많았기 떄문이다.
천하의 Intel도 Pentium 때처럼 눈에 띄는 연산 오류부터 눈에 잘 안보이는 작은 오류도 만드는데 이보다 작은 업체들의경우는 더욱 심각 할수 있을 것이다.
더우기 몇몇 업체들은 자신이 만들 제품의 목적에 맞춰 성능을 강화하려고 수정을 시도하는 경우도 있었다.
이러다 보니 ARM은 일반적으로 공개된 컴파일러를 사용하지 못하는 경우가 많았다.
임베디드 개발자들 사이에 널리 퍼진 이야기 중 하나가 “SOC를 개발할 때에는 컴파일러를 업체에서 준 것을 사용하라!” 라는 것이다.
리눅스는 PC에서 다른 아키텍쳐 용 결과물을 출력해 주는 컴파일러를 크로스컴파일러( Cross compiler) 또는 툴체인(Tool chain)이라 한다.
툴체인의 설정상태는 자신이 사용 할 수 있는 라이브러리등 여러 제약을 주기 때문에 잘 파악한 후 사용해야 한다.
데이터의 주소 정렬 위치에도 주의 해야 한다.
Cortex 시리즈로 넘어오고 난 이후 이 문제는 신경을 안 써도 되는 문제였으나, 지금도 여전히 많이 사용되는 ARM9시리즈의 경우 이 부분에 대하여 매우 민감하다.
ARM은 저전력을 목표로 했던 아키텍쳐 답게 초기에는 정렬이 되지 않은 주소 접근에 대한 배려기능이 없었다.
즉, 1byte를 읽을 때는 문제가 없으나 short(2byte)이나 int(4byte)크기로 읽거나 쓰기를 할 경우 주소는 제어하고자 하는 크기의 배수가 되어야 한다.
C언어를 통해 예를 들자면
i = 2;
라는 명령을 수행 한다면 i라는 변수가 char(1byte)타입의 변수일 때는 문제가 없으나 short 일 경우 주소는 2의 배수여야 하고 int일 경우 4의 배수여야 한다.
만약 이 규칙을 어긴다면 수행한 결과값이 잘 못 되거나 예상치 못한 오류가 발생하게 된다.
이런 제약이 일반적인 경우에는 struct align 설정만 잘 되어 있다면 컴파일러가 알아서 피해주지만 네트워크로 들어온 패킷을 분석하거나 하는 경우 곤란한 상황을 접하게 되는 경우가 많다.
네트워크로 들어오는 패킷은 변수크기에 맞춰 정렬되어 있지 않은 경우가 많기 떄문에 이런 값을 읽으려면 1byte 씩 읽어서 조립해서 써야 하는 경우가 발생한다.
Cortex 계열 CPU에서는 정렬되지 않은 주소에 대한 배려가 되어있어 정렬된 데이터를 제어할 때와 수행 성능에서 약간의 차이가 있을 뿐 실행되는 데에는 문제가 없다.
그래도 성능 향상을 조금이라도 더 시키고 싶다면 데이터 정렬은 반드시 하는게 좋다.
Endian 문제도 중요한 문제다.
초기 ARM 아키텍쳐를 사용하는 CPU나 SOC들은 네트워크데이터를 효율적으로 다루기 위하여 Big endian 계열이 많았다.
독립적으로 운영될 때에는 문제가 없었으나 PC와 같은 Little endian 계열 제품과 연동하거나 통신을 할 때에 많은 부하를 주게 되었다.
요즘은 PC와 연동을 중요하게 생각해서 그런지 Little endian으로 나오는 제품들이 많아졌다.
3. 마치면서…
이 글은 예전 어셈러브 블로그에 올렸던 글이다.
한때는 활발했던 활동이지만 모두의 생업에 바뻐 지금은 흔적만 남은듯 하다.
나름 열심히 살았던 시절 추억의 파편들을 정리해보려 한다.
ARM 어플리케이션 디버깅 준비
[컴파일]
디버깅 모드로 컴파일 하면 나중에 에러 발생 시 나온 이미지 파일로 에러가 난 위치를 찾을 수 있다는 장점이 있다.
gcc 기준으로 컴파일 옵션에서 -O2 대신 -g -O0 옵션으로 대체한다.
-g는 함수, 해당 코드가 소스상 어느라인인지를 알려주는 라인정보등을 추가한다는 뜻이고,
-O0는 최적화 정도를 말하는데 0이므로 최적화를 하지 않는다는 뜻이다.
최적화를 실행 할 경우, 지역변수 생성을 동적으로 하고, 일부 코드의 경우 자동으로 최적화된 코드로 대체하기 때문에 에러가 발생한 위치를 소스와 매칭시킬수 없다.
[실행 전]
ulimit -c 명령을 이용하여 에러가 발생했을때 core 파일이 생성 될 수 있도록 한다.
core 파일은 말 그대로 해당 프로세스의 CPU상태 (레지스터 등), 메모리 상태를 덤프하는 것을 뜻한다.
하드디스크나 NFS등으로 실행하여 저장 용량이 넉넉 할 경우 ulimit -c unlimit 로 하여도 되지만
RAM DISK 등이나 NAND 플래시와 같이 여유 저장공간이 적은곳에서 실행파일을 실행 할 경우
용량에 맞춰서 수치를 적어준다. ex) ulimit - c 1024
[실행 후]
에러가 발생하였을 경우 실행파일 있는 자리에 .core 라는 파일이 생긴다.
당연한 말이지만 저장 불가능한 cd 나 dvd 와 같은 곳애서 실행한다면 생기지 않을 것이다.
.core 파일이 생성되었으면 그 자리나 source 파일 있는 곳에다가 복사해 놓고 gdb로 불러 들인다.
gdb역시 해당 CPU(타겟 CPU)에 맞는것을 써야한다.
실행방법은
gdb 실행파일명 .core
이다.
어디서 멈춰섰는지를 찾고 싶으면 화면에서 where 를 치면 된다.
디버깅 모드로 컴파일 하면 나중에 에러 발생 시 나온 이미지 파일로 에러가 난 위치를 찾을 수 있다는 장점이 있다.
gcc 기준으로 컴파일 옵션에서 -O2 대신 -g -O0 옵션으로 대체한다.
-g는 함수, 해당 코드가 소스상 어느라인인지를 알려주는 라인정보등을 추가한다는 뜻이고,
-O0는 최적화 정도를 말하는데 0이므로 최적화를 하지 않는다는 뜻이다.
최적화를 실행 할 경우, 지역변수 생성을 동적으로 하고, 일부 코드의 경우 자동으로 최적화된 코드로 대체하기 때문에 에러가 발생한 위치를 소스와 매칭시킬수 없다.
[실행 전]
ulimit -c 명령을 이용하여 에러가 발생했을때 core 파일이 생성 될 수 있도록 한다.
core 파일은 말 그대로 해당 프로세스의 CPU상태 (레지스터 등), 메모리 상태를 덤프하는 것을 뜻한다.
하드디스크나 NFS등으로 실행하여 저장 용량이 넉넉 할 경우 ulimit -c unlimit 로 하여도 되지만
RAM DISK 등이나 NAND 플래시와 같이 여유 저장공간이 적은곳에서 실행파일을 실행 할 경우
용량에 맞춰서 수치를 적어준다. ex) ulimit - c 1024
[실행 후]
에러가 발생하였을 경우 실행파일 있는 자리에 .core 라는 파일이 생긴다.
당연한 말이지만 저장 불가능한 cd 나 dvd 와 같은 곳애서 실행한다면 생기지 않을 것이다.
.core 파일이 생성되었으면 그 자리나 source 파일 있는 곳에다가 복사해 놓고 gdb로 불러 들인다.
gdb역시 해당 CPU(타겟 CPU)에 맞는것을 써야한다.
실행방법은
gdb 실행파일명 .core
이다.
어디서 멈춰섰는지를 찾고 싶으면 화면에서 where 를 치면 된다.
시작하며
집에서 사용하던 개인 웹사이트가 하드디스크 노후로 날라가 버렸다.
다시 복구하여 운영하자니 귀찮다.
그래도 개인 사이트나 블로그는 필요한 것 같다.
누구에게 보이기 위한 것이 아니라,
점점 나이가 들면서 뭔가를 기억해 내기 힘들어 지는 부분을 보완하기 위하여
어디서나 쉽게 접근 가능한 외장 메모리가 필요하다.
적어도 구글 블로그라면 데이터가 날라갈 일은 없지 않을까 생각하며 시작해 본다.
다시 복구하여 운영하자니 귀찮다.
그래도 개인 사이트나 블로그는 필요한 것 같다.
누구에게 보이기 위한 것이 아니라,
점점 나이가 들면서 뭔가를 기억해 내기 힘들어 지는 부분을 보완하기 위하여
어디서나 쉽게 접근 가능한 외장 메모리가 필요하다.
적어도 구글 블로그라면 데이터가 날라갈 일은 없지 않을까 생각하며 시작해 본다.
피드 구독하기:
글 (Atom)