티스토리 뷰

RTOS의 정의

리얼타임은 한마디로 “빠르다”라고 표현할 수 있다.

리얼타임에 대해서는 많은 부분이 오해로 남아있다.

 일반적으로는 어떤 사상이 발생한 것을 받아 즉시 응답하는 것을 리얼타임이라고 말하기도 한다.

이것은 즉시 응답이라는 것과 우리가 오해하고 있는 등시성(우리는 리얼타임이라 하면 동일시간에 발생하는 것으로 오해하는 경우가 비일비재하다.)과는 약간은 다른 표현이 된다. 쉽게 말하자면, 리얼타임은 등시성보다 완만한 의미를 가진 단어이다.

"진정한 의미의 리얼타임이란 허용시간내에 응답하는 것을 의미한다."

 리얼타임이라 하면 소프트 리얼타임과 하드 리얼타임의 두 종류로 분류할 수 있다.(굳이 나누자면)소프트 리얼타임 시스템에서는 각 태스크는 시스템에 의해 가능한 빨리 실행되지만, 어떤 정해진 시간내에 종료할 필요는 없다.

 하드 리얼타임 시스템에서는 태스크가 정확하게 실행될 뿐 아니라, 정확한 시간에서 실행되어야 한다.리얼타임 애플리케이션은 넓은 영역에 걸쳐 있다. 리얼타임 시스템을 필요로 하는 애플리케이션은 대부분이 망구축 시스템이다. 간략한 예를 들자면, 화학플랜트와 같은 프로세서 제어, 자동차의 엔진제어, OA, 컴퓨터 주변기기,로봇,항공기의 제트엔진 제어, 그리고, 심지어는 세탁기와 같은 가전제품에도 해당한다.

 이러한 것들에서는 사용되는 컴퓨터(사실은 컴퓨터의 개념은 아니지만)는 시스템에 이미 구축되어 있어서 사용자에게는 별반 인식없이 넘어가는 것들이다.

 일반적으로, 리얼타임 소프트웨어는 그렇지 않은 경우보다 설계가 훨씬 어렵다. 그러나, 리얼타임 커널을 사용하는 것으로, 대상을 커널에 의해 관리하는 복수의 태스크로 분할할 수 있으므로, 설계 프로세스를 보다 단순화할 수 있다.

(사실은 어려운 일임에는 틀림없다.)


Linux가 RTOS가 아닌 이유

 리눅스는 일반적인 목적으로 설계된 운영체제이기 때문에 real time 운영체제가 아니며, real time 운영체제의 입장에서 보면 많은 문제를 지니고 있다.

 우선 리눅스는 다른 UNIX와 마찬가지로 time-sharing scheduling을 한다. 즉 하나의 프로세스를 실행하고 있는데, 이 프로세스가 정해진 time slice를 모두 채우거나, blocking이 되어서 어쩔 수 없이 중단되어야 하는 경우, system call을 하는 경우, paging 메커니즘을 통해서 page을 하드 디스크에서 읽어와야 하는 경우 등을 제외하고 이 프로세스를 중단하지 않는다. 따라서 지금 실행하고 있는 프로세스보다 높은 priority를 갖는 프로세스가 나타나더라도 현재 프로세스가 time slice를 채우거나 스스로 CPU를 놓아 주기만을 기다려야 한다. 또한 priority가 더 높은 프로세스가 있다고 하더라도 더 낮은 priority를 갖는 프로세스에게도 가끔씩 CPU를 할당해주어야 한다. 높은 priority를 갖는 프로세스라도 정해진 time slice를 채우고 나면 CPU를 놓아주어야 한다. 리눅스는 지금 실행할 수 있는 있는 프로세스 중 가장 중요한 프로세스를 실행하고 있다는 보장을 하지 못한다.

 기존 리눅스의 프로세스 관리에 보면 프로세스를 일반 프로세스와 real time 프로세스로 분류하여 real time 프로세스에게 더 높은 priority를 주고 이들에게 별도의 scheduling 정책을 사용하는 것을 볼 수 있다. 그러나 이는 단지 더 높은 권한을 부여하여 scheduling을 할 때 real time 프로세스가 다른 프로세스보다 먼저 수행되게 하는 것이지, preemption을 비롯한 일반적인 real time scheduling 요건을 충족하지 못한다.

 리눅스는 커널 모드에서 선점(preempt)할 수 없다. 어떤 프로세스가 시스템 콜을 불러 커널 모드에 진입한 경우 이 프로세스를 중지시킬 수 없다. 또한 커널은 필요한 경우 인터럽트가 발생하는 것을 막아버리기도 한다. 이 경우 인터럽트가 발생하더라도 그 수행이 지연될 수 밖에 없다.

 리눅스는 가상 메모리를 사용한다. 리눅스는 demand paging 기법을 사용하고 swapping을 하기 때문에, 프로세스 수행 도중 페이지를 디스크에서 메모리로 읽어들이는 일이 종종 발생하여 프로세스의 수행 시간을 예상할 수 없게 된다.. 가상 메모리는 프로세스가 관리해야 하는 자료구조가 많아지게 하기 때문에 task switching이 일어나는 경우 처리해야 하는 일이 많아지게 되어 성능 저하를 가져오게 된다. (가상 메모리를 사용하지 않는 리눅스 프로젝트도 있다)

 리눅스에서 서로 동기화가 필요한 부분에서는 성능을 위하여 자원을 오랜시간 동안 점유한다. 실제 자원을 필요한 순간에 쓰고 반납하게 되면 자원을 얻고 반납하는 횟수가 늘어나기 때문에 성능이 떨어지게 된다. 그래서 이 횟수를 줄이고 자원을 오랫동안 독점하게 되는데 이를 coarse grained synchronization이라고 한다. 또한 priority가 낮은 프로세스가 점유하는 자원을 priority가 높은 프로세스가 기다려야 하는 priority inversion 문제도 발생한다.


Real Time Linux >> RT-Linux

 RT Linux는 리눅스에 Hard Real Time 기능을 부여하기 위하여 시작된 프로젝트이다. 리눅스가 Real Time 기능을 갖추려면 실제로 커널부터 다시 설계되어야 하겠지만, 이는 커널을 구성하는데 많은 시간을 필요로 하며, 리눅스 커널이 업그레이드 될 때마다 이를 신속히 반영하기 어렵고, 버그가 생길 가능성이 더 커지게 된다. 이에 RT Linux 프로젝트는 커널을 다시 설계하지 않고 리눅스 소스 변경을 최소화하는 방향으로 진행되었다. 리눅스 아래에 RT Linux 커널을 올리고 기존 리눅스 커널을 RT Linux 커널에서 돌아가는 하나의 프로세스로 만들어서 Real Time task와 기존의 리눅스 커널이 공존하는 형태를 만들었다. RT Linux 커널은 real time 프로세스를 생성하고 이들을 스케쥴링하며, 인터럽트가 발생하였을 때 이를 처리하며 리눅스와의 통신을 담당하는 역할을 한다. 기존 리눅스 커널은 그대로 일반 리눅스 프로세스를 관리하고, 자신의 인터럽트를 처리한다. RT Linux의 구현 특징을 살펴보면 다음과 같다.


- Linux OS Kernel Task : RT Linux는 기존 리눅스 커널을 RT Linux 상에서 돌아가는 하나의 task로 생각한다. 여기에는 가장 낮은 priority가 부여되므로, 실행할 수 있는 RT task가 하나도 없을 때 실행된다.

- Scheduling : RT Linux는 현재 두가지 scheduling 방법을 제공한다. 하나는 priority-based preemptive scheduler로서 모든 task에 priority를 부여하고, 실행할 수 있는 task 중에서 가장 priority가 높은 task를 수행하며, 이보다 더 높은 priority를 갖는 task가 등장하면 이를 곧바로 수행하는 것이다. 다른 스케쥴러는 earliest deadline first (EDF) 알고리즘을 사용하며, priority가 아니라 deadline을 기준으로 scheduling을 한다. 이들 외에 사용자가 별도의 scheduler를 커널 모듈로 만들어서 이를 대신 사용할 수도 있다.

- Kernel Preemption : 기존 리눅스 커널은 선점되지 않는다는 가정 하에 만들어져 있다. 하지만 Real Time에서는 커널 작업 중이라도 이를 중단할 수 있어야 하는데, 현실적으로 커널을 재진입 가능하게(reentrant) 수정하는 것은 커널을 다시 설계해야 하는 문제가 있다. RT Linux는 이 문제를 RT task와 RT 인터럽트가 기존 리눅스 커널에서 제공하는 시스템 콜을 사용하지 못하게 함으로써 해결한다. 즉 RT 인터럽트는 기존 리눅스 커널과 무관하기 때문에 기존 리눅스 커널을 선점하더라도 문제가 되지 않는다. 하지만 RT Linux 커널 그 자체는 선점할 수 없도록 설계되어 있다. 이 커널은 아주 작고 빠르게 설계되어서 큰 시간 지연을 발생하진 않는다.

- Interrupt Blocking : RT Linux에서 인터럽트의 제어권은 RT Linux 커널에 있다. 기존 리눅스 커널은 인터럽트에 의해 중단되고 싶지 않은 경우, 하드웨어 인터럽트를 막을 수 있다. 이렇게 인터럽트를 막아버리면(cli) 그 사이 발생한 인터럽트는 인터럽트가 허용될 때까지(sti) 기다리고 있어야 하고(pending), 이는 인터럽트 반응 속도를 느리게 하는 요소가 된다. 이에 RT Linux는 기존 리눅스 커널이 인터럽트를 막는 것을 소프트웨어적으로 에뮬레이션을 하고, 실제로 인터럽트를 막을 수 없도록 한다. 리눅스 커널이 인터럽트를 막으면 별도의 플래그에 이를 표시해 두었다가, 실제로 인터럽트가 발생하면 이 플래그를 참조하여 인터럽트 가능 상태이면 인터럽트를 전달하고, 인터럽트 금지 상태이면 인터럽트가 발생했다는 표시를 하고, 인터럽트를 허용할 때까지 전달하지 않는 것이다. 이렇게 하여 기존 리눅스 커널의 입장에서는 자신이 인터럽트되지 않는 효과를 주고, 실제로 real time 인터럽트를 바로 처리할 수 있도록 한다.

- Interrupt 처리 : RT Linux에서 인터럽트는 기존 리눅스 커널의 관리하에 있는 인터럽트와, RT Linux 커널의 관리하에 있는 RT 인터럽트 두가지로 나뉜다. RT Linux는 RT 인터럽트를 위하여 request_RTirq(), free_RTirq() 함수를 제공한다. (기존 인터럽트는 request_irq(), free_irq() 함수를 사용한다) 인터럽트가 발생했을 때 real time 인터럽트 핸들러가 등록되어 있다면 이를 불러주고, real time 인터럽트 핸들러가 없거나, 인터럽트를 기존 리눅스 커널과 공유하는 경우 이를 리눅스 커널에 전달한다.

- Resource : RT 커널은 리눅스에 메모리나 세마포어같은 자원을 요청하지 않는다. RT task는 동적으로 메모리를 요청할 수 없고, 리눅스 커널과 자료구조를 공유하면서 동기화를 해야하는 경우도 없다.

- Real Time FIFO : RT Linux를 이용하여 실제로 프로그램을 짤 때 프로그램을 RT task와 일반 리눅스 프로세스로 나누어서 하도록 한다. RT task에서는 할 수 있는 일이 극히 제한되기 때문에, 실제 RT 기능이 필요한 부분은 RT task에서 구현하고, 그렇지 않은 부분은 기존의 리눅스 프로세스로 만드는 것이다. 이를 위해 RT Linux는 이 두 프로세스가 서로 데이터를 교환할 수 있는 기능을 제공하는데, 이것이 Real Time FIFO이다. 이는 커널 주소공간에 공유 메모리를 이용하여 구현되며 (절대 page out되지 않는 영역이다), 이에 대한 접근은 세마포어같은 것을 통하지 않고 atomic하게 이루어진다. RT task가 여기에 접근할 때 blocking을 하지 않고, 이 영역의 크기가 고정되어 있기 때문에 버퍼가 넘치거나(overflow) 부족하는(underflow) 현상이 발생할 수 있다.

- Real Time Task의 특징 : RT task는 리눅스 커널 task이다. 즉 커널 권한을 가지고 수행되므로 자유롭게 하드웨어에 접근할 수 있다. 또한 코드와 데이터 영역 모두 메모리에 존재하며 page out되지 않는다. 이는 페이지를 읽어들이면서 발생하게 되는 지연을 막기 위함이다. RT task들은 커널 영역을 함께 공유한다. 메모리 보호를 위해 task들이 별도의 주소 공간을 갖도록 할 수 있지만, 이렇게 하면 task switching을 할 때마다 page directory가 바뀌게 되며, TLB도 갱신해야 하기 때문에 성능이 떨어지게 되고, 시스템 콜을 할 때마다 protection level을 바꾸면서 약간의 오버헤드가 생기게 된다. RT Linux는 성능을 위하여 모든 RT task들이 같은 커널 영역을 공유한다. RT task는 리눅스 커널 모듈로 만들 수 있다. 이는 동적으로 RT task를 실행할 수 있게 한다. 여기서 모듈을 로드하는 역할은 기존의 리눅스에서 한다. RT task에서는 리눅스에서 제공하는 시스템 콜을 사용할 수 없으며 커널에 있는 다른 루틴들을 직접 부르거나 커널에 있는 자료구조들을 사용할 수 없다. // RT-Task들은 프로그램 전부 메모리에 올라가며 커널영역의 메모리 공간에 저장이 된다. 


RT-LINUX의 실재

 RT-Linux의 목표는 다음의 공존할 수 없는 두 가지를 적절히 혼합 하는데에 있다.

하드 리얼타임 서비스인 Predictable, Fast, Low Latency(호출 시간이 길지 않은), Simple Scheduler 등과 스탠더드한 Posix 규약의 모든 서비스인 GUI, TCP/IP, NFS, compilers, Web-servers 등이 같은 Operating System에서 효율적으로 존재하는 것이다.

 RT-Linux application은 일반적으로 아래와 같이 구성되어진다.

 리눅스 프로세스와 리얼타임 태스크는 특별한 Fifo와 공유메모리에 의해서 상호간에 통신을 주고 받는다.자, 이제 Bill Crum에 의해서 개발된(486/33) 신호발생기 어플리케이션에 대한 예를 보자.두 개의 주기적인 RT-task(주기 @800 μs)가 있다. 각각의 태스크는 사각파, 삼각파, 정현파등을 발생시킬 수 있다. TCL/TK로 된 사용자 프로그램은 파형을 선택하는데 사용되는 푸쉬 버튼을 디스플레이한다. 명령들은 fifo에 의해서 리얼타임 태스크에게 전달되어진다.리얼타임 요소들은 일반적인 리눅스에 탑재된 커널 모듈내에서 코딩되어진다. 유저 프로세스는 fifo를 생성하고, 읽고, 쓰기 위한 시스템 호출을 한다. fifo는 실행 우선 순위가 역전되는 엄청난 문제를 피하기 위해서 특별히 디자인되어 있다.rt_task_init 함수를 호출함으로서 태스크들은 체계적으로 초기화된다. 이러한 행위는 메모리, 스택, FIFO 등을 할당한다.인터럽트에 의해서든지, 주기적인 스케줄러에 의해서든지 태스크들은 체계적으로 스케줄한다.

다음은 초기화의 예제이다.

     

    int init_module(void){
             RTIME now = rt_get_time();
             rt_task_init(&mytask1, wave_handler, 1, 3000, 5);
             rt_task_init(&mytask2, wave_handler, 2, 3000, 5);

              rt_task_make_periodic(&mytask2, now, 993);
              rt_task_make_periodic(&mytask1, now+3000, 993);
              return 0; }

위의 예제에서 주기가 993 = @800μs 인 두 개의 태스크가 생성된다.

다음은 신호 발생기를 위한 태스크 코드이다.

     

    while(1) {
            if (rt_fifo_get(t, &command, 1) > 0)
                    outdev(PORT, next(command));
            rt_task_wait();
            }

신호발생기의 사용자 부분은 (1)TCL/TK로 구성된 일반적인 리눅스 어플리케이션을 쓰면 된다.

그리고, fifo를 초기화시키고, (2)RT-task에게 명령을 보내는 매우 간략한 C 프로그램의 집합을 쓸 수 있다.

(1) TCL/TK 사용자 프로그램 예제

     

    frame.f1 -relief groove -borderwidth 3
    frame.f2 -relief groove -borderwidth 3

    label.f1.l1 -text “ Channel 1 “
    label.f2.l2 -text “ Channel 2 “
    button.f1.widget1 -text “ sine wave “ -command { exec ./sinewave 1 1 }
    button.f1.widget2 -text “ square wave “ -command { exec ./sinewave 1 3 }
    button.f1.widget3 -text “ sawtooth wave “ -command { exec ./sinewave 1 2 }
    button.f1.widget4 -text “ flatline “ -command { exec ./sinewave 1 0 }
    button.f1.widget5 -text “ exit “ -command { exec rmmod rt_process.o exit }

 (2)명령을 보내는 프로그램 예제

     

    int main(int argc, char **argv){
            char outbyte;
            int fifo;
            fifo = atoi (argv[1]);
            outbyte = (char) atoi (argv[2]);
            rt_fifo_write(fifo, &outbyte, 1);
            exit(0);     }

 위에서 여러 가지 어려운 코딩을 많이 보았다. 

 어려운 내용이므로 초보자는 많은 연구 후에 보기 바라며, 어떤 개인의 개발 과정에 대한 예시일 뿐이므로 자세한 설명을 하지 못한 점은 애석하게 생각한다.코딩을 이해하려 들지 말고, 이렇게 할 수 있구나 하는 개념을 얻었으면 충분하리라 본다. 최종적으로 RT-Linux 의 구성을 아래와 같이 도해해 본다.

 추가적으로 이야기하자면, RT-Task를 위한 모든 자원은 정적으로 할당되어 있다.메모리, fifo, 그리고 프로세서 타임은 태스크 생성에 고정되어 있다.또한, 리얼타임 태스크는 인터럽트를 불가능하게 할 수도 있다.

RT-Linux에 있어서는 결국 Interrupt Latency가 너무 길다는 것을 어떻게 극복해야 할지가 가장 큰 성능상의 문제가 되고 있다.

장점과 단점

 RT Linux의 구조는 무척 단순하기 때문에 기존 리눅스 프로그래머들이 쉽게 접근할 수 있다는 장점이 있다. real time 기능이 필요한 부분만 RT task로 구현하고 나머지는 이미 어느정도 검증되어 있는 안정된 리눅스 상에서 동작하므로 안정적이고, 디버깅이 용이하다는 장점이 있다.

 RT Linux는 기존의 리눅스가 가진 기능들을 real time으로 바꾼 것은 아니다. 단지 RT task 기능을 추가하였을 뿐, 대부분의 서비스는 기존 리눅스에서 그대로 담당하게 된다. 리눅스 코드의 대부분을 차지하는 디바이스 드라이버도 그대로 사용되고 있으며, 이들에게 real time 기능이 필요한 경우 이들 디바이스 드라이버를 다시 설계해서 만들어야 하는 문제가 있다. 또한 RT Linux는 기존의 리눅스의 기능에 추가를 한 것이기 때문에 여전히 덩치가 크다는 부담이 있다.


참조 

RT-Linux 를 이용한 PC기반의 임베디드 시스템구현 

http://blog.naver.com/section001/120009463355


Real Time Linux, RT-Linux

http://blog.naver.com/dbajjang/60179990139


RT-Linux에관하여

http://blog.naver.com/section001/120009463300



[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linux|작성자 짱가

[출처] Real Time Linux , RT-Linu

Real Time 운영체제 (RTOS)

[출처] Real Time Linux , RT-Linux|작성자 짱가|작성자 Real Time 운영체제 (RTOS)

[출처] Real Time Linux , RT-Linux|작성자 짱가


공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함