내용이 많은건 아닌데.. 블로그 특징상 길게 쓰면 이상해 보여요 ㅜ.ㅜ; 절대 내용 많은거 아님

Internal Function

이부분은 아주 간단한 내용입니다. 일반 C 언어 처럼 함수를 선언해서 쓸수 있다는 내용 입니다. 함수의 형식은 일반 C를 따르며, command, event등을 쓰지 않으며, 다른 컴포넌트는 내부 함수는 호출 할 수 없습니다.

예는 다음과 같습니다.

module BlinkC {
  uses interface Timer<TMilli> as Timer0;
  uses interface Timer<TMilli> as Timer1;
  uses interface Timer<TMilli> as Timer2;
  uses interface Leds;
  uses interface Boot;
}
implementation
{

  void startTimers() {
    call Timer0.startPeriodic( 250 );
    call Timer1.startPeriodic( 500 );
    call Timer2.startPeriodic( 1000 );
  }

  event void Boot.booted()
  {
    startTimers();
  }

  event void Timer0.fired()
  {
    call Leds.led0Toggle();
  }
 
  event void Timer1.fired()
  {
    call Leds.led1Toggle();
  }
 
  event void Timer2.fired()
  {
    call Leds.led2Toggle();
  }
}

보시다 시피 startTimers() 란 내부함수가 있으며, 같은 컴포넌트 내에서는 제한 없이 사용 할 수 있습니다.

물론 함수를 사용 하기전에 정의는 되어 있어야겠죠.

Split-Phase Operations

command, evnet, 외 중요한 개념입니다. NesC는 와이어 처리를 컴파일시 하게 되어 있어 콜백 즉 event 시스템은 TinyOS에서도 매우 효율적으로 동작 할 수 있습니다.

Blocking 실행을 해야 하기 때문에 TinyOS에서 컴포넌트를 넘어가며 최적화 하는 능력은 매우 중요합니다. Blocking 시스템(절차적 언어)에서는 프로그램이 매우 긴 수행시간의 함수를 실행시 함수가 실행이 종료될때 까지 시스템이 멈처진 상태에 있게 됩니다. 하지만 Split-Phase 시스템에서는 함수는 최대한 빠르게 리턴되며 Operaion이 종료된 후에는 콜백을 사용 하여 "완료" 신호를 날리도록 되어 있습니다.

Blocking Split-Phase
if (send() == SUCCESS) {
  sendCount++;
}
// start phase
send(); 

//completion phase
void sendDone(error_t err) {
  if (err == SUCCESS) {
    sendCount++;
  }
}


Split-Phase의 이점은 스택메모리를 실행 될 동안 점유 하지 않으며, 함수의 실행 종료를 기다리지 않으므로 시스템의 정체를 줄이며, 스택의 활용도를 늘리게 됩니다.

Split-Phase 시스템은 TinyOS가 여러개의 작업을 동시에 수행 할 수 있게 하며 메모리를 절약 시킵니다.

Timer.startOneShot 은 Split-Phase의 좋은 예입니다.

사용자 삽입 이미지

위와 같이 Timer를 절차적으로 사용 하게 되면 Sleep()을 사용하게 되며, 그동안 시스템은 멈추게 됩니다 하지만 Split-Phase에서는 startOneShot()함수 호출후 다른 작업을 수행 할 수 있으며, 타이머는 정해진 시간에 시그널을 날리게 되어 있습니다.

, .
중요한 개념인 Task를 구현 합니다. 1.X에서는 상당히 애매한 상황이 벌여 졌지만 앞서 말한 TASK가 각 각 슬롯을 가짐으로 확실해 졌습니다.

TASK
이때까지 본 소스는 동시성과는 거리가 먼소스들이었습니다. 동시 실행 코드가 실행 되고 있을 동안에는 선점이 되지 않습니다. 이 메카니즘은 TinyOS 스케줄러의 자원 소모를 줄였으나 동시 실행 코드는 매우 작은 크기여야 합니다. 즉 동시 실행시 선점 되지 않기 때문에 즉각 반응 시스템엔 역효과를 내는 것입니다. 예로는 긴 코드가 실행 되어 패킷에 반응 하는 속도가 늦어 질 수 있는 것입니다.

이때까지 코드는 아주 짧은 실행 코드만을 지녀 별문제 없이 작동 했습니다. 하지만 매우 긴 시간을 요구하는 실행 코드의 경우 선점 되지 않는 문제는 많은 문제를 야기 할 수 있습니다. 그래도 2.0의 경우 매우 긴 시간을 요구하는 코드의 경우 여러부분으로 나눌것을 권장 합니다.

Task - Task는 어플리케이션에 백그라운드 프로세싱을 가능하게 합니다. 이정도로 생각 하시면 됩니다. OS에서 이야기하는 bottom halves와 deferred procedure call과 비슷 합니다.(*하드웨어 인터럽트가 먼저라 생각 하시면 됩니다.)

이제 Task를 실습 하도록 하겠습니다. cp 명령어를 사용 하여 새로운 BlinkC 백업 본을 만듭니다.(BlinkTask)

cp -R Blink BlinkTask

Timer0.fired() 이벤트를 수정하여 작업을 좀 넣도록 하겠습니다.

event void Timer0.fired()
  {
    uint32_t i;
    dbg("BlinkC", "Timer 0 fired @ %s.\n", sim_time_string());
    for(i =0; i<400001;i++){
      call Leds.led0Toggle();
    }
  }

대충 이작업은 MicaZ , Telosb 계열 모트가 20번의 패킷을 보낼 수 있는 시간의 작업을 수행 합니다. 퓨징 하시면 지연으로 인해 이상하게 동작 하는 모트를 보실 수 있습니다.

동작의 결과를 설명 드리면 Timer0에서 발생하는 연산이 Timer이벤트들의 실행을 방해 하며 결국은 Timer0의 연산이 끝나는 시점에서 동시에 발생 하게 되는것이다. 그래서 두개가 계속 나란히 켜지면 한개만 켜지는 경우는 없게 된다.

이문제를 해결 하기 위해서는 백그라운드로 저 작업을 실행 하면된다.

Task의 정의는 다음과 같다.

task void taskname(){}

Task는 인자를 가질수도 리턴값을 가질수도 없다. 그리고 Task를 실행 할때는 post를 사용한다.

post taskname();

여기서 중요한거 컴포넌트의 command,task,event는 task를 포스팅 할 수 있다. 그리고 이벤트 시그널을 위해서는 Task를 사용 하기 바란다. command에서 event를 사용하는건 루프를 발생 시킬 소지가 매우크다. 나중에 설명함(예로 인터페이스 I 제공 하는 컴포넌트 A 사용하는 B가 있을 경우 B의 이벤트에 핸들러에서 A의 command를 실행 하며 그 command에서 시그널을 날린다면 바로 루프가 발생하는 것이다.)

task를 다음과 같이 수정하여 만든다.

 task void computeTask(){
    uint32_t i;
    for(i=0;i<400001;i++){}
  }
  event void Timer0.fired()
  {
      call Leds.led0Toggle();
      post computeTask();
  }

그럼 TelosB는 아직 이상하지만 MicaZ는 작동 잘 할 것이다.

post는 Task를 큐에 FIFO 방식으로 넣는 역활을 한다. Task는 긴 수행시간을 가져서는 안된다. Task는 서로는 선점 하지 못하지만, 하드웨어 인터럽트는 Task를 선점 할수 있다. 긴시간의 수행시간을 가지는 Task의 경우 나누도록 한다. post 명령은 error_t 를 반환한다. (task가 반환 하는것이 아닌 post명령이다.) 이경우는 Task가 벌써 성공적으로 스팅 된 경우 이다.(물론 아직 실행은 안되었다.)

TelosB의 경우 아직 Task 가 많은 시간을 소모 하기 때문에 정상 작동 하지 않는 것이다. 다음과 같이 수정한다.

외적으로 static 변수를 사용하는건 생략 한다. 대략 컴포넌트안의 변수는 모두 Private이기때문에 static를 사용하는건 의미가 없다.
, .