Linux RTOS

7 minute read

Written By KJ Jang, VCANUS

1. 리눅스 기반의 Real-time OS

모든 OS는 preemptive한 작업을 수행을 보장한다. preemptive 란 프로세스 간 우선 순위를 정하고, 현재 수행 중인 프로세스가 다른 프로세스로부터 interrupt를 받으면 수행 하던 프로세스를 내려 놓은 뒤 우선순위가 높은 프로세스를 먼저 처리하는 것을 말하는데, 사실 이는 OS의 정말 기본 중의 기본 컨셉이기 때문에 리눅스는 이 기능을 자체적으로 내장하고 있다. 따라서 하드웨어만 어느 정도 받쳐준다면 어느 정도의 RT 기능은 일반 리눅스도 충분히 커버 할 수 있다.

1.1 일반 리눅스과 와 차이점

차이점은 “시스템 콜에 대한 Preemptive” 이다. 일반 리눅스는 interrupt가 들어왔을 때 현재 수행 중인 시스템을 콜을 끝낸 뒤 Context Swiching이 일어나지만, RT 커널 기반의 리눅스는 현재 작업 중인 프로세스의 시스템 콜 수행마저도 interrupt를 걸어 작업 Swiching에 대한 Latency를 최소화 한다.

2. 리눅스에서 RT 기능 사용법

2.1 리눅스 Scheduling의 종류

1. SCHED_OTHER(또는 SCHED_NORMAL)

일반 프로세스가 사용하는 타입의 Policy

2. SCHED_FIFO, SCHED_RR

RT를 위한 Scheduling Policy

2.2 Priority 제어하기

1. nice 또는 renice

  • +19 ~ -20 값을 가지며, -20이 가장 높은 Priority 값
  • 일반 프로세스 레벨인 SCHED_OTHER 에서 동작 가능
  • PR(priority) 계산 공식은 PR = 20 + NI

2. renice 명령어

$ renice [priority 값] -p [프로세스 번호]

3. chrt

  • +1 ~ +99 값을 가지며, 높은 값일수록 높은 Priority를 의미
  • RT 프로세스 레벨, Scheduling Policy를 변경 가능
  • PR(priority) 계산 공식은 PR = -1 -rt_priority

4. chrt 명령어

$ chrt [policy] -p [priority 값] [프로세스 번호]

5. C,C++ 로 프로세스 우선순위 및 스케줄링 정책 변경하기

#include <sched.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/syscall.h>
#include <unistd.h>
 
struct sched_attr {
   uint32_t size;              /* Size of this structure */
   uint32_t sched_policy;      /* Policy (SCHED_*) */
   uint64_t sched_flags;       /* Flags */
   int32_t  sched_nice;        /* Nice value (SCHED_OTHER,
                                  SCHED_BATCH) */
   uint32_t sched_priority;    /* Static priority (SCHED_FIFO,
                                  SCHED_RR) */
   /* Remaining fields are for SCHED_DEADLINE */
   uint64_t sched_runtime;
   uint64_t sched_deadline;
   uint64_t sched_period;
};
 
// 실제로 스케줄링 속성을 변경하는 sched_setattr 함수
static int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
    return syscall(SYS_sched_setattr, pid, attr, flags);
}
 
int main() {
    int result;
 
    struct sched_attr attr;
    // 초기화
    memset(&attr, 0, sizeof(attr));
    attr.size = sizeof(struct sched_attr);
    
    // RT 프로세스 사용 
    attr.sched_priority = 95; // 우선순위 값 : 95 -> 135였나.. 를 의미
    attr.sched_policy = SCHED_FIFO; // SCHED_FIFO는 상수로서 정의되어 있다.
    
    /* 
    
    //일반 프로세스 사용
    attr.sched_nice = 19 // ni(-20 ~ 19)
    attr.sched_policy = SCHED_OTHER
    
    */
 
    // 스케줄링 속성 
    result = sched_setattr(getpid(), &attr, 0);
    if (result == -1) {
        perror("Error calling sched_setattr.");
    }
}

3. Linux RTOS 와 Timer code 및 실험 결과

1. c++ code

/
// Created by vcanus on 20. 5. 28..
//
#include <iostream>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <chrono>
#include <algorithm>

#include <signal.h>
#include <unistd.h>
#include <mutex>
#include <thread>

#include <sched.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <stdint.h>

using namespace  std;

chrono::steady_clock::time_point start_clock;
chrono::steady_clock::time_point end_clock;
long min_time = 2000000;
long max_time = 0;
double average = 0;
int cnt = 0;
int error_time = 0;

struct sched_attr {
    uint32_t size;              /* Size of this structure */
    uint32_t sched_policy;      /* Policy (SCHED_*) */
    uint64_t sched_flags;       /* Flags */
    int32_t  sched_nice;        /* Nice value (SCHED_OTHER,
                                  SCHED_BATCH) */
    uint32_t sched_priority;    /* Static priority (SCHED_FIFO,
                                  SCHED_RR) */
    /* Remaining fields are for SCHED_DEADLINE */
    uint64_t sched_runtime;
    uint64_t sched_deadline;
    uint64_t sched_period;
};

// 실제로 스케줄링 속성을 변경하는 sched_setattr 함수
static int sched_setattr(pid_t pid, const struct sched_attr *attr, unsigned int flags)
{
    return syscall(SYS_sched_setattr, pid, attr, flags);
}


void timer_handler(int sig,siginfo_t *si,void *uc)
{
    cnt +=1;
    end_clock = chrono::steady_clock::now();
    long elapsed_time = chrono::duration_cast<chrono::microseconds>(end_clock-start_clock).count();
    min_time = min(min_time,elapsed_time);
    max_time = max(max_time ,elapsed_time);
    average += elapsed_time;
    //cout << elapsed_time << endl;

    if(elapsed_time <= 9000 || elapsed_time >= 11000){
        error_time++;
        //cout << "error_time : "  << elapsed_time << endl;
        //cout <<  "error_cnt : "<< error_time << endl;
    }

    for(int i=0; i<=100; i++)
    {

    }

    start_clock = chrono::steady_clock::now();
}

int createTimer(timer_t *timerID, int sec , int msec)
{
    struct sigevent te;
    struct itimerspec its;
    struct sigaction sa;
    int sigNo = SIGRTMIN;

    /* Set up signal handler */
    sa.sa_flags = SA_SIGINFO;
    sa.sa_sigaction = timer_handler; // 타이머 호출 함수
    sigemptyset(&sa.sa_mask);

    if(sigaction(sigNo, &sa , NULL) == -1)
    {
        cout << "sigaction error" << endl;
        return -1;
    }

    /* Set and enable alarm */
    te.sigev_notify = SIGEV_SIGNAL;
    te.sigev_signo = sigNo;
    te.sigev_value.sival_ptr = timerID;
    timer_create(CLOCK_REALTIME, &te , timerID);

    its.it_interval.tv_sec = sec;
    its.it_interval.tv_nsec = msec * 1000000;

    its.it_value.tv_sec = sec;
    its.it_value.tv_nsec = msec * 1000000;

    timer_settime(*timerID,0 ,&its,NULL);
}

int main() {

    int result;

    struct sched_attr attr;
    // 초기화
    memset(&attr, 0, sizeof(attr));
    attr.size = sizeof(struct sched_attr);

    attr.sched_nice = 19;
    attr.sched_policy = SCHED_OTHER; // SCHED_FIFO는 상수로서 정의되어 있다.

    // 스케줄링 속성
    result = sched_setattr(getpid(), &attr, 0);
    if (result == -1) {
        perror("Error calling sched_setattr.");
    }


    timer_t timerID;
    /*
     * parame
     * */
    createTimer(&timerID,0,10);
    start_clock = chrono::steady_clock::now();
    int lv = 0;

    /* Do busy work . */
    thread t1([&](){
        sleep(60);
    });
    t1.join();

    cout << "total cnt : " << cnt << endl;
    cout << "error cnt : " << error_time << endl;
    cout << "min_time : " << min_time << endl;
    cout << "max_time : " << max_time << endl;
    cout << "average : " << average/cnt << endl;
    return 0;
}

2. cmake

cmake_minimum_required(VERSION 3.16)
project(vcx_linux_timer_example)

set(CMAKE_CXX_STANDARD 17)

link_directories(/usr/lib64) ## librt.so 파일 경로
add_executable(vcx_linux_timer_example main.cpp)


target_link_libraries(${PROJECT_NAME} rt)

3. Timer 정확성 실험 조건 및 결과

실험1 : CentOS7 Linux PR 값에 따른 Timer 정확성 테스트 | PR | max | min | average | 상황 | Timer 호출 시간 | 1ms 이상 오차 발생 수 | 오차확률 | 측정시간 | |:—:|:—:|:—:|:—:|:—-:|:—-:|:—-:|:—-:|:—-:| | 39(min) | 17,528 | 3,172 | 9997.72 | 동영상 | 10ms | 248 | 4.13% | 1분 | | 20 | 14,378 | 5,624 | 9997.71 | 동영상 | 10ms | 171 | 2.85% | 1분 | | 0 | 11,210 | 8,795 | 9997.93 | 동영상 | 10ms | 4 | 0.07% | 1분 | | -2 | 10,174 | 9,812 | 9997.96 | 동영상 | 10ms | 0 | 0.00% | 1분 | | -51 | 10,483 | 9,510 | 9998.07 | 동영상 | 10ms | 0 | 0.00% | 1분 | | -100(max) | 10,174 | 9,839 | 9998.1 | 동영상 | 10ms | 0 | 0.00% | 1분 |


실험2 : VMware(ubuntu) Linux PR값에 따른 Timer 정확성 테스트 | PR | max | min | average | 상황 | Timer 호출 시간 | 1ms 이상 오차 발생 수 | 오차확률 | 측정시간 | |:—:|:—:|:—:|:—:|:—-:|:—-:|:—-:|:—-:|:—:| | 20 | 15,932 | 4,284 | 9999.25 | 동영상 | 10ms | 353 | 5.90% | 1분 | | 0 | 17,926 | 2,549 | 9999.3 | 동영상 | 10ms | 52 | 0.86% | 1분 | | -2 | 11,528 | 8,467 | 9999.24 | 동영상 | 10ms | 4 | 0.06% | 1분 | | -51 | 10,545 | 9,537 | 9999.26 | 동영상 | 10ms | 0 | 0.00% | 1분 | | -100(max) | 10,612 | 9,381 | 9999.35 | 동영상 | 10ms | 0 | 0.00% | 1분 |


실험3 : VMware(ubuntu) Linux timer,PR 값에 따른 Timer 정확성 테스트 | Timer 호출 시간 | PR | max | min | average | 상황 | 1ms 이상 오차 발생 수 | 오차확률 | 측정시간 | |:—:|:—:|:—:|:—:|:—:|:—:|:—:|:—:|:—:| | 10ms | -2 | 11,528 | 8,467 | 9999.24 | 동영상 | 4 | 0.06% | 1분 | | 10ms | -51 | 10,545 | 9,537 | 9999.26 | 동영상 | 0 | 0.00% | 1분 | | 10ms | -100 | 10,612 | 9,381 | 9999.35 | 동영상 | 0 | 0.00% | 1분 | | 20ms | -2 | 27,138 | 19,218 | 20001.7 | 동영상 | 1 | 0.03% | 1분 | | 20ms | -51 | 20,485 | 19,516 | 19999.3 | 동영상 | 0 | 0.00% | 1분 | | 20ms | -100 | 20,545 | 19,392 | 19999.3 | 동영상 | 0 | 0.00% | 1분 | | 30ms | -2 | 31,028 | 29,041 | 29999.3 | 동영상 | 2 | 0.10% | 1분 | | 30ms | -51 | 30,543 | 29,515 | 29999.3 | 동영상 | 0 | 0.00% | 1분 | | 30ms | -100 | 30,397 | 29,544 | 29999.3 | 동영상 | 0 | 0.00% | 1분 | | 40ms | -2 | 41,007 | 39,067 | 39999.3 | 동영상 | 0 | 0.00% | 1분 | | 40ms | -51 | 40,333 | 39,591 | 39999.4 | 동영상 | 0 | 0.00% | 1분 | | 40ms |-100 | 40,547 | 39,314 | 39999.5 | 동영상 | 0 | 0.00% | 1분 | | 50ms | -2 | 50,660 | 49,399 | 49999.3 | 동영상 | 0 | 0.00% | 1분 | | 50ms | -51 | 50,476 | 49,577 | 49999.4 | 동영상 | 0 | 0.00% | 1분 | | 50ms | -100 | 50,516 | 49,338 | 49999.3 | 동영상 | 0 | 0.00% | 1분 |

Leave a comment