임베디드 개발 영역

임베디드 개발 영역은 크게 3개로 나뉜다

  • Application Level 임베디드 sw 개발자
  • Middleware Level 임베디드 sw 개발자
  • Low level 임베디드 sw 개발자

임베디드에서 사용되는 운영체제

  • 리눅스
  • RTOS
  • 자체적 운영체제 (firmware)

Applicaiton level 개발자

application이란?

운영체제 안에서 동작하는 프로그램

 

application 개발이란?

  • 각각 운영체제에 맞는 앱을 개발
    • 리눅스 운영체제
      • 리눅스 App 개발
    • RTOS 운영체제
      • RTOS 운영체제 App 개발
  • GUI 개발
  • 검증 SW 개발
  • 주로 C / C++ / C#을 주로 사용

 

Middleware level 개발자

Middleware란?

application과 운영체제의 중간 다리 역할을 한다

  • 운영체제의 신호를 App이 가져할 수 있는 API
  • App level에서 운영체제에 신호를 전달하는 API

middleware 개발이란?

  • application level 개발자를 위한 library / API 개발
  • 해당 문서 작성
  • 성능 최적화
  • 시스템 통합 및 테스트
  • 주로 사용되는 언어
    • Android
      • JAVA / C / C++
    • RTOS
      • C

Low level 개발자

Low level 개발이란?

  • Firmware 개발
    • H/W를 제어하는, 작은 운영체제 직접 개발
    • 주언어: C, Assembly
  • Device Driver 개발
    • 커널 내부에서 동작되는 프로그램으로 hw를 제어하는 프로그램 개발
    • 주언어: C

 

'Computer Science > 임베디드' 카테고리의 다른 글

Memory Mapped IO  (0) 2024.12.13
유선 통신 기초  (0) 2024.08.19

고정길이 정수를 사용하는 이유

int는 표준으로 4byte라고 명시되어있지 않다

=> int는 4byte가 아닐 수도 있다!

시스템마다 다른 사이즈를 사용할 수 있다.

어떤 시스템에서도 똑같은 크기를 갖게할 수 있도록 고정길이 정수를 사용한다.

 

여담으로 int는 원래 cpu가 가장 효율적으로 처리할 수 있는 크기를 의미하도록 설계되었다. 32bit 운영체제에서 가장 효율적으로 처리할 수 있는 크기는 4 byte 여서, 32 bit 운영체제에서는 int는 4 byte로 설계되었다.

지금 컴퓨터는 대부분 64 bit 운영체제를 사용하지만 대부분 int 크기가 8 byte가 아닌  4 byte로 사용한다.

그 이유는 

  •  호환성: 이전의 C/C++ 코드의 대부분이 32 bit 환경에서 작성되었고, int 는 4 byte로 대부분 익숙하기 때문
  • 성능: 과거에는 CPU 아키텍처가 데이터 모델과 더 밀접하게 연결되어 있어서 int가 CPU 레지스터 크기와 거의 동일했지만, 현대에는 컴퓨터 아키텍처와 데이터 모델이 분리되어 반드시 동일할 필요가 없어짐
  • 메모리 사용량: 메모리 사용량이 증가하면 캐시성능에 영향을 미칠 수 있으므로 불필요한 크기 증가를 피할 수 있음

stdin.h

#include <stdint.h>

IBM 문서

 

<stdint.h>

<stdint.h> 포함 파일은 너비를 지정하고 해당 매크로 세트를 정의하는 정수 유형 세트를 선언합니다. 또한 다른 표준 포함 파일에 정의된 유형에 대응하는 정수 유형의 한계를 지정하는 매크로도

www.ibm.com

 

고정길이 정수타입

  • int8_t : char
  • int16_t : short
  • int32_t : int
  • int64_t : long long

 

  • uint8_t : unsigned char
  • uint16_t : unsigned short
  • uint32_t : unsigned int
  • uint65_t : unsigned long long

unsigned는 부호없는 정수타입을 나타내는 키워드이다. - 부호를 포함하는 데이터타입에 비해서 약 2배의 양의 범위를 표현할 수 있게 해준다. 

그 외

  • 최소 너비 정수 유형
    • int_least8_t ...
  • 가장 빠른 최소너비 정수 유형 = 해당 크기 이상의 값을 저장할 수 있으면서 해당 플랫폼에서 가장 빠르게 동작
    • int_fast8_t ...
  • 가장 큰 너비 정수 유형
    • intmax_t, uintmax_t

 

문자열은 포인터로 처리된다.

c언어에서는 문자열 마지막에는 항상 '\0'  널 문자가 있기 때문에 배열 선언시 + 1칸 더해줘야 한다.

c언어에서 널문자로 문자열 끝임을 인지한다.

몰론 널 문자 없이 꽉꽉 채워 저장할 수는 있지만, 그 경우 문자열로서 가치는 사라진다.

char v[4] = "ABC" ; vs const char* v = "ABC";

배열을 하나 만들고 네글자를 삽입하는 방식  vs 포인터를 이용해 문자열 상수를 가리키는 방식이다

무슨 차이가 있을까?

const char* v = "ABC";

포인터로 문자열을 가리키는 것부터 알아보자!

"ABC"는 코드상에서 리터럴로 메모리에서 읽기 전용의 데이터 세그먼트 영역에 저장된다.

포인터는 리터럴의 주소를 가리키게 되고 따라서 수정이 불가능하다.

const는 해당 데이터가 변경되지 않음을 보장한다.

char v[4] = "ABC";

배열을 선언하고 초기화를 할 경우, 해당 리터럴이 배열이 저장되어 있는 메모리 공간에 복사가 되어 저장된다.

  • Static : 전역 변수일 경우
  • Stack : 지역 변수일 경우

어느 곳이든 둘다 수정이 가능하다.

 

따라서 char v[4] = "ABC" ; vs const char* v = "ABC" 는 포인터가 가리키고 있는 메모리 영역이 다르며,  그 차이에 따라서 수정 여부가 달라진다.

 

zip & tar

리눅스에서 많이 사용되는 압축 방법

  1. tar + xz
  2. zip
  3. tar + gz
  4. tar + bz2

압축률

zip < gz < bz2 < xz

압축률이 높을수록, 속도는 느리다

 

실습

구성

아래와 같이 환경 구성

work1, work2에 각각 1MB 파일을 생성한다

mkdir work1
cd work1
for ((i=0;i<1024;*1024/2;i++)) do echo A >> a.txt; done
du -sh a.txt

mkdir work2
cd work2
for ((i=0;i<1024*1024/2;i++)) do echo A >> b.txt; done
du -sh b.txt

'A'와 줄바꿈문자 '\n' 까지 더해서 2byte * 1024 * 1024  / 2번 반복해서 해당 파일들은 각각 1mb 가 된다.

압축 -  zip

work1 디렉토리로 이동한 뒤 a.txt를 압축해보자.

zip all1.zip a.txt

원본 파일이 남아있는 것을 확인할 수 있다.

 

all1.zip을 지우고 a.txt와 work2를 같이 압축해보자.

zip all2.zip ./*

 

압축 파일 내 work2 디렉토리가 비어있는 파일인 것을 확인할 수 있다

-r 옵션을 줘야 하부 디렉토리가 정상적으로 같이 압축된다

all2.zip을 지우고 

-r 옵션을 주고 a.txt와 work2를 같이 압축해보자.

zip -r all3.zip ./*

압축해제 - unzip

unzip [압축파일명] -d [압축풀 경로]

[압축풀 경로]에 해당 경로가 없으면 생성하고 압축해제한다.

unzip all3.zip -d ./newDirect

압축 -  gzip, xz

gzip과 xz는 리눅스의 단일 파일 압축 방식이다.

원본 파일이 압축 파일로 변경된다 (원본 파일이 사라진다)

압축 성능 xz > gzip

gzip a.txt
xz b.txt

압축해제 - gunzip, xz -d

gunzip a.txt.gz
xz -d b.txt.xz

파일아카이브 유틸리티 - tar 

파일을 하나로 합칠 수 있다.

파일을 하나로 합치는 것뿐 압축을 하는 것은 아니다.

원본이 남아있다.

tar -cvf ab.tar a.txt b.txt

tar -cvf [파일명.tar] [파일1] [파일2] ...

  • -c : 하나로 모아서 새파일 생성 (create)
  • -f : 파일 이름을 지정 (file)
  • -v : 진행 상황을 보여줌  (verbose)
  • 원본이 남음
tar -xvf ab.tar -C ./ab/

tar -xvf [파일명.tar] -C [압축 풀 디렉토리 경로]

  • -x : tar 해제 (extract)
  • -C : 경로 지정 (change directory)
  • tar는 자동으로 디렉토리를 만들어주는 기능이 없어서 해당 디렉토리를 만들어야 함
  • 원본이 남음

핵심 - tar + xz

tar -Jcvf [파일명.tar.xz] [파일1] [파일2] ...

  • -J : XZ 압축 방식을 사용하도록 지정하는 옵션
tar -Jcvf ab.tar.xz a.txt b.txt

tar -Jxvf [파일명.tar.xz] -C [디렉토리]

tar -Jxcf ab.tar.xz -C ./ab

'Linux > tips' 카테고리의 다른 글

아카이브 변경하기  (0) 2024.12.11
Vim 설치, 단축키, vimrc  (0) 2024.12.10
bash shell script - 명령어 정리  (0) 2024.12.10

환경 변수란?

사용자, process, 리눅스 자체 등 다같이 사용하는 변수

다양한 운영체제에서 적용된다.

 

printenv  명령어를 사용하여 환경변수를 확인할 수 있다

printenv

echo 명령어를 통해서 하나씩 출력할 수 있다

export [변수]=[값]

원하는 변수를 생성할 수 있다

재부팅하면 사라진다

환경 변수로 등록하면 어디서든 사용 가능하다!

  • .sh 내부에서도 사용 가능
export HYndrome=good
printenv | grep HYndrome

환경 변수 영구적으로 등록하기

방법 1. 셀 초기화 파일에 등록하기

bash shell이 시작하자마자 시작되는 bash shell script 파일 편집

vi ~/.bashrc

export 명령어 등록

reboot 한다음에 남아 있는지 확인

방법 2. /etc/environment에 추가 (시스템 전체)

/etc/environment에 환경변수를 직접 등록할 수 있다.

해당 사용자뿐만 아니라 시스템 전체에 해당 환경 변수를 등록하는 것이다.

etc/environment 파일을 수정한 후에는 로그아웃하고 다시 로그인해야 변경사항이 적용된다

sudo vi /etc/environment

변수 등록

세션 로그아웃

exit

등록 확인

'Linux > 개념' 카테고리의 다른 글

Bash shell script 문법  (0) 2024.12.12
리눅스 사용자 및 권한 관리  (0) 2024.12.11
포트포워딩  (0) 2024.12.11
리눅스 파일 시스템 및 구조  (0) 2024.12.10
우분투(Ubuntu)와 리눅스(Linux), 운영체제, 쉘  (0) 2024.12.10

기본

모든 쉘 스크립트 확장자는 .sh

(권장 사항) 파일의 맨 위에는 #! /bin/bash를 적어준다.

  • 이 문서는 bash 쉘 스크립트임을 알린다
    • #! /bin/bash : bash 쉘
    • #! /bin/sh: dash 쉘

bash vs dash

bash와 dash의 대부분의 명령어는 같다

user는 기본적으로 bash를 사용하지만 우분투 시스템에서는 기본적으로 dash를 사용한다.

 

bash

더 많은 기능 지원 (ex history)

기본 설정으로 POSIX 비호환 확장 기능을 제공하나 --posix 옵션을 사용하여 POSIX 모드 사용 가능

기능이 많아서 느릴 수 있음

dash

빠른 실행과 최소한의 리소스 사용을 목표로 함

POSIX 표준을 엄격히 준수

경량이라서 빠름

 

 

실행방법

  • .source [파일이름.sh]
    • 가장 많이 사용되는 방법
  • .  [파일이름.sh]
    • 실행권한이 필요한 경우가 있음

문법

변수

변수이름=값 (띄워쓰기 금지)

모든 값들은 문자열로 취급 (숫자로 취급 x)

$(( )) 를 붙이면 이 안에서 산술 연산으로 처리 됨

실행 결과

shell 명령어의 실행 결과를 변수에 저장할 수 있다

실행 결과

주석

한줄 주석 # 입력하고 그 후에 작성

조건문

if 문은 띄워쓰기를 조심해야 함

fi 로 조건문 종료를 해줘야 함

독보적인 비교 연산자

  • -lt : less than
  • -gt : greater than
  • -eq : equal (수 비교)
  • -ne : not equal
  • -ge : greater or equal
  • -le : less or equal

파일 비교도 가능하다

  • -x : 파일이 존재하고, 권한이 (+x)일 때
  • -f : 파일이 존재하고, Regular 파일일 때
  • etc...

printf

c언어의 printf와 비슷하다

있다는 것 정도만 알아주자

배열

배열을 만들 때는 ( )  사용

배열 값을 출력할 때는 { } 괄호 필수

반복문

for 문법

함수

함수 호출할 때, 함수 이름만 입력하면 됨

 

'Linux > 개념' 카테고리의 다른 글

환경변수  (0) 2024.12.12
리눅스 사용자 및 권한 관리  (0) 2024.12.11
포트포워딩  (0) 2024.12.11
리눅스 파일 시스템 및 구조  (0) 2024.12.10
우분투(Ubuntu)와 리눅스(Linux), 운영체제, 쉘  (0) 2024.12.10

<stdlib.h>에 있는 system() 을 사용해서 shell scipt를 실행가능하다!

#include <stdlib.h>

int main()
{
    system("echo HI");
    system("ls");

    return 0;
}

실행결과

 

예시: 쉘 제작해보기

작성해본 코드

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_HISTORY_SIZE 100
#define MAX_COMMAND_LEN 100

// Define command
typedef struct {
    int id;
    char command[MAX_COMMAND_LEN];
} Log;

int cnt = 0;
Log command_log[MAX_HISTORY_SIZE];

void RecordCommand(char* input)
{
    cnt++;
    strcpy(command_log[cnt].command, input);
    command_log[cnt].id = cnt;
}

int main()
{
    while (1)
    {
        char input[100];
        // input command
        printf("SSAFY>_ ");
        fgets(input, sizeof(input), stdin);

        // remove "\n"
        input[strcspn(input, "\n")] = 0;

        // input !_number
        if (input[0] == '!' && atoi(input + 1) >= 0 && atoi(input + 1) < cnt)
        {
            int command_no = atoi(input + 1);
            // printf("%s\n", command_log[command_no].command);
            strcpy(input, command_log[command_no].command);
        }

        // commands
        if (strcmp(input, "date") == 0)
        {
            system("date");
            RecordCommand(input);
            continue;
        }
        if (strcmp(input, "uptime") == 0)
        {
            system("uptime");
            RecordCommand(input);
            continue;
        }
        if (strcmp(input, "ls") == 0)
        {
            system("ls -al");
            RecordCommand(input);
            continue;
        }
        if (strcmp(input, "log") == 0)
        {
            system("dmesg");
            RecordCommand(input);
            continue;
        }
        if (strcmp(input, "exit") == 0)
        {
            break;
        }
        if (strcmp(input, "hclear") == 0)
        {
            cnt = 0;
            continue;
        }

        if (strcmp(input, "history") == 0)
        {
            for (int i = 1; i <= cnt; i++)
            {
                printf("%d %s\n", command_log[i].id, command_log[i].command);
            }
            RecordCommand(input);
            continue;
        }
        else
        {
            printf("ERROR\n");
        }
    }

    return 0;
}

기본문법

Target 타켓

목표 파일 이름, 빌드하려는 최종 결과물

1개 이상의 타겟이 있어야 한다

comment를 실행하며 comment  앞은 반드시 Tab 들여쓰기를 해야한다 (그림에서 노란색으로 표시된 곳)

HI:
    echo "HI"
HELLO:
    echo "HELLO"

 

 

Dependency 의존성

Target을 생성하기 위한 파일 목록

의존성 Target을 먼저 수행하게 됨

 

아래의 경우 HI 를 실행하면 의존성에 HELLO가 있어서

HELLO를 우선 실행하게 된다

HI: HELLO
    echo "HI"
HELLO:
    echo "HELLO"

Variable 변수

앞에 $를 붙이고 소괄호 () or 중괄호 {} 를 붙여서 사용한다.

가독성을 위하여 script 최상단에 작성

MSG1 = "HI"
MSG2 = "HELLO"

HI: HELLO
    echo ${MSG1}
HELLO:
    echo $(MSG2)

Comment 주석

# 을 사용하여 주석을 표시한다.

한줄 주석만 지원한다.

 

특수변수 (자동변수)

  • $@ : Target 이름
  • $^ : Dependecy 목록 전체
  • $< : Dependecy 목록 중 첫 번째
  • ...

연산자

다양한 연산자를 갖는다

  • +=
  • := (simple equl)
    • script 순서대로 현재 기준에서 값
  • = (reculsive equl)
    • 최종 변수 결과 값

단계별 Makefile 제작

함수 2개가 있는 makefile을 단계별로 제작해보자

common.h

#include <stdio.h>

func1.h

void func1();

func1.c

  1 #include "common.h"
  2
  3 void func1()
  4 {
  5     printf("Func1\n");
  6 }

func2.h

void func2();

func2.c

#include "common.h"

void func2()
{
    printf("Func2\n");
}

main.c

#include "common.h"
#include "func1.h"
#include "func2.h"

int main()
{
    printf("main start!\n");
    func1();
    func2();
    printf("main end!\n");

    return 0;
}

 

Makefile 1단계

파일 구성을 바탕으로 dependencies를 작성한다

result: main.o func1.o func2.o
    gcc main.o func1.o func2.o -o result

main.o: main.c common.h func1.h func2.h
    gcc -c main.c

func1.o: func1.c common.h func1.h
    gcc -c func1.c

func2.o: func2.c common.h func2.h
    gcc -c func2.c

clean:
    rm -r ./*.o result

Makefile 2단계 - 변수 추가

  • CC: compiler가 바뀔 때 변경
  • OBJS: 목적 파일 목록
CC = gcc
OBJS = main.o func1.o func2.o

result: $(OBJS)
    $(CC) -o result $(OBJS)

main.o: main.c common.h func1.h func2.h
    $(CC) -c main.c

func1.o: func1.c common.h func1.h
    $(CC) -c func1.c

func2.o: func2.c common.h func2.h
    $(CC) -c func2.c

clean:
    rm -r $(OBJS) result

 

Makfile 3단계 - 특수 변수 추가

  • $@ : Target을 나타냄
  • $^ : 의존성 타겟들을 나타냄
  • $< : 의존 타겟 중 첫번째
CC = gcc
OBJS = main.o func1.o func2.o

result: $(OBJS)
    $(CC) -o $@ $^

main.o: main.c common.h func1.h func2.h
    $(CC) -c $<

func1.o: func1.c common.h func1.h
    $(CC) -c $<

func2.o: func2.c common.h func2.h
    $(CC) -c $<

clean:
    rm -r $(OBJS) result

 

Makefile 4단계 - 컴파일러 옵션 변수 추가

컴파일 옵션 지정 $(CFLAG)

  •  -g : 디버깅 (Trace) 가능하도록 설정
  • -Wall : Warning이 뜨면 Error 처럼 멈추도록 함
  • -O2 : 최적화 2단계 옵션
CC = gcc
CFLAG = -g -Wall -O2
OBJS = main.o func1.o func2.o

result: $(OBJS)
    $(CC) $(CFLAG) -o $@ $^

main.o: main.c common.h func1.h func2.h
    $(CC) $(CFLAG) -c $<

func1.o: func1.c common.h func1.h
    $(CC) $(CFLAG) -c $<

func2.o: func2.c common.h func2.h
    $(CC) $(CFLAG) -c $<

clean:
    rm -r $(OBJS) result

 

Makefile 5단계 - wildcard, 확장자 치환 적용

wildcard 함수

  • 지정된 패턴에 해당하는 파일 목록 갖고오기
  • *.c : 현재 디렉토리 내 모든 .c 파일 가져오기

확장자 치환 사용

  • 변수에 할당된 파일 목록에서 확장자 치환
  • 변수명:[pattern]=[replacement]
  • SRCS의 .c를 .o 변경해서 OBJS에 저장

CC = gcc
CFLAG = -g -Wall -O2
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)

result: $(OBJS)
    $(CC) $(CFLAG) -o $@ $^

main.o: main.c common.h func1.h func2.h
    $(CC) $(CFLAG) -c $<

func1.o: func1.c common.h func1.h
    $(CC) $(CFLAG) -c $<

func2.o: func2.c common.h func2.h
    $(CC) $(CFLAG) -c $<

clean:
    rm -r $(OBJS) result

 

Makefile 6단계 - makedepend 유틸리티 및 SUFFIXES 적용

makedepend

입력한 .c 파일을 분석해서 의존성 헤더파일을 등록해주는 make 도우미 유틸리티

 

makedepend 설치

sudo apt install xutils-dev -y

makedepend 실행

makedepend main.c func1.c func2.c -Y

Makefile 하단에 의존성 목록이 자동으로 추가된 것을 확인할 수 있다.

makedepend 적용

makedepend Target 추가

SUFFIXES

  • 파일 확장자와 관련된 규칙을 지정할 때 사용
  • .c파일을 .o 파일로 컴파일하는 것을 뜻함 (default)
  • suffixes 뜻은 접미사로 확장자를 뜻함

.c .o

  • .c 파일을 .o 파일로 변환할 때 실행할 명령을 정의

CC = gcc
CFLAG = -g -Wall -O2
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
SUFFIXES = .c .o

result: $(OBJS)
    $(CC) $(CFLAG) -o $@ $^

.c .o:
    $(CC) $(CFLAG) -c $<

clean:
    rm -r $(OBJS) result

depend:
    makedepend $(OBJS) -Y

 

실행

make depend
make

 

Makefile 7단계 - 파일명 매크로 추가

all

  • Target 파일이 여러개일 때 사용

.c .o: 는 default 값이라서 생략 가능

CC = gcc
CFLAG = -g -Wall -O2
SRCS = $(wildcard *.c)
OBJS = $(SRCS:.c=.o)
SUFFIXES = .c .o
TARGET = result

all: $(OBJS)
    $(CC) $(CFLAG) -o $(TARGET) $^

clean:
    rm -r $(OBJS) $(TARGET)

depend:
    makedepend $(OBJS) -Y

 

+ Recent posts