最終更新 2006/12/01
RTAI - Real Time Application Interface API解説(5)
 
 
5 RTAI スケジューラー
 
5.3 スケジューラーによって提供される API
 
5.3.1 タスク・プライオリティー
プログラムでタスクの実際のプライオリティをチェックすることができます。

int rt_get_prio (RT_TASK * task)

この関数は指定タスクのベースプライオリティを取得します。
重要な事は、そのタスクが生成時、あるいは rt_change_prio ()関数によって割り当てられたベースネイティブのプライオリティと実際の継承されたプライオリティを持っていることである。
パラメータ: task: は対象とするタスク
戻り値: タスクのプライオリティ

この関数と次の2つのものはタスクのプライオリティについて論じます。 優先順位の値はインクルードファイル base\include\rtai_sched.h で次のように定義されます

#define RT_SCHED_HIGHEST_PRIORITY 0
#define RT_SCHED_LOWEST_PRIORITY 0x3fffFfff
#define RT_SCHED_LINUX_PRIORITY 0x7fffFfff

より高いプライオリティを持っているタスクが先に実行されて、より低いプライオリティのタスクによって中断させられることがないことに注意してください。

int rt_get_inher_prio (RT_TASK * task)

この関数はタスクの実際の継承されたプライオリティをチェックします。
パラメータ:タスクは対象とするタスクです。
リターン: タスクのプライオリティ

この二つのプライオリティを得る関数は:

int rt_get_priorities(RT_TASK *task, int *priority, int *base_priority)

int rt_change_prio(RT_TASK *task,int priority)

この関数で指定タスクのベースプライオリティを変えることが可能である。 タスク生成時割り当てられたベースネイティブのプライオリティと実際の、継承された、プライオリティを持っていることを思い出してください。 それらはプライオリティ継承によって異なる値をとり得ます。

パラメータ:
    task: は対象とするタスクです。
    priority: が新しいプライオリティで範囲は 0 < priority< RT_SCHED_LOWEST_PRIORITY .
戻り値: タスクが変更の前に持ったベースプライオリティを返します。
Note:
タスクのプライオリティを他のタスクから変えることは、もし受け取っているタスクがリソースロックされているなら、実行を保留します。 これは適切な所で最大の効率を達成する時に変更が実行されることを期待する。
5.3.2 タスク構造体
RT_TASK * rt_whoami (void)

これは自タスクのタスク構造体ポインタを得る。
戻り値: は自タスクのタスク構造体です。

 タスク構造体は base\include\rtai_sched.h インクルードファイルで定義されて、シングルタスク関係のすべてのデータを含みます。

この機能の典型的な用途:
rt_task_delete (rt_whoami ());
あるいは
RT_TASK * task;
task = rt_whoami () ;

タスク構造体
typedef struct rt_task_struct {
    long *stack __attribute__ ((__aligned__ (L1_CACHE_BYTES)));
    int uses_fpu;
    int magic;
    volatile int state, running;
    unsigned long runnable_on_cpus;
    long *stack_bottom;
    volatile int priority;
    int base_priority;
    int policy;
    int sched_lock_priority;
    struct rt_task_struct *prio_passed_to;
    RTIME period;
    RTIME resume_time;
    RTIME yield_time;
    int rr_quantum;
    int rr_remaining;
    int suspdepth;
    struct rt_queue queue;
    int owndres;
    struct rt_queue *blocked_on;
    struct rt_queue msg_queue;
    int tid; /* trace ID */
    unsigned long msg;
    struct rt_queue ret_queue;
    void (*signal)(void);
    FPU_ENV fpu_reg __attribute__ ((__aligned__ (L1_CACHE_BYTES)));
    struct rt_task_struct *prev;
    struct rt_task_struct *next;
    struct rt_task_struct *tprev;
    struct rt_task_struct *tnext;
    struct rt_task_struct *rprev;
    struct rt_task_struct *rnext;
    /* Appended for calls from LINUX. */
    long *fun_args;
    long *bstack;
    struct task_struct *lnxtsk;
    long long retval;
    char *msg_buf[2];
    long max_msg_size[2];
    char task_name[16];
    void *system_data_ptr;
    struct rt_task_struct *nextp;
    struct rt_task_struct *prevp;
    /* Added to support user specific trap handlers. */
    RT_TRAP_HANDLER task_trap_handler[HAL_NR_FAULTS];
    /* Added from rtai-22. */
    long unblocked;
    void *rt_signals;
    volatile unsigned long pstate;
    unsigned long usp_flags;
    unsigned long usp_flags_mask;
    unsigned long force_soft;
    volatile int is_hard;
    void *trap_handler_data;
    struct rt_task_struct *linux_syscall_server;
    /* For use by watchdog. */
    int resync_frame;
    /* For use by exit handler functions. */
    XHDL *ExitHook;
    RTIME exectime[2];
    struct mcb_t mcb;
    /* Real time heaps. */
    struct rt_heap_t heap[2];
    volatile int scheduler;
    #ifdef CONFIG_RTAI_LONG_TIMED_LIST
    rb_root_t rbr;
    rb_node_t rbn;
    #endif
} RT_TASK __attribute__ ((__aligned__ (L1_CACHE_BYTES)));
5.3.3 タスク制御
void rt_task_yield (void)
この関数は自タスクの実行放棄を行う。

 自タスクを止めて、同じプライオリティのタスクのリストの最後に移動する。 スケジューラーは同じプライオリティで次の用意ができているタスクをアクティブにします。

RTAI スケジューラーは、より高いプライオリティタスクがより低いプライオリティのタスク実行をプリエンティブに制御することが可能であったことを思い出してください。 それでも等しいプライオリティタスクがお互いをプリエンティブに制御することができません、もしユーザーが等しいプライオリティタスクの間で協力的なタイムスライスを必要とするなら、 rt_task_yield () を使うべきです。

int rt_task_suspend (RT_TASK * task)
rt_task_suspend はタスクタスクの実行を一時停止します。

rt_task_resume () あるいは rt_task_make_periodic () のコールがされるまで、タスクは実行されない。

パラメータ:タスク構造物へのポインタ。
戻り値:タスク一時停止深度(多重サスペンド)。

 下記の場合、異常終了が戻ります:
 . − EINVAL :タスクは正当なタスクを参照しません。.  NULLポインタが帰る

.  RTE_UNBLKD :停止中に、タスクはアンブロックされた;

同様の機能の関数が次の3つある。
1. int rt_task_suspend_if(RT_TASK *task)
2. int rt_task_suspend_until(RT_TASK *task, RTIME time)
3. int rt_task_suspend_timed(RT_TASK *task, RTIME delay)


サスペンド関数は、同期化ツールとして使うことができます。
セマフォ技術に類似した方法でタスクを制御する。

int rt_task_resume (RT_TASK * タスク)

この関数は前に rt_task_suspend () によってサスペンドされたラスクの実行を再開するか、あるいは新規生成されたタスクを実行させます。マルチプルサスペンドのカウンタがありますから、それをリジュームする前に、複数回サスペンドサスペンドされた場合、rt_task_resume を同様の回数と呼ぶ必要があります。

パラメータ: タスク構造体へのポインタ。
戻り値:  成功=0 負値:下記エラー
EINVAL : 正当なタスク構造体を参照していません。

int rt_get_task_state (RT_TASK * task)
この API はリアルタイムタスクの状態を照会します。

パラメータ:タスク構造体へのポインタです。
戻り値:
タスク状態はビット対応であり、ひとつあるいは複数の状態を表す。.
.   RT_SCHED_READY
  タスクは実行準備ができています(タスクはブロック状態にない)。
.  RT_SCHED_SUSPENDED
  タスクはブロック状態でresumeを待っている。
.  RT_SCHED_DELAYED
  タスクは遅延時間タイムアウトの満期を待ってブロックされている。
.  RT_SCHED_SEMAPHORE
  タスクはセマフォを待って、セマフォでブロックされます。
.  RT_SCHED_SEND
  受信側が RECEIVE状態に無く、メッセージ送信タスクはブロックされる。
.  RT_SCHED_RECEIVE
  タスクは受信状態にあり、メッセージ送信あるいは rpcs を待ってブロックされます。
.  RT_SCHED_RPC
  タスクはRPCでブロックされます、受信側が RECEIVE状態にない。
.  RT_SCHED_RETURN
  タスクはRPCからリターンを待ってブロックされます。レシーバーは RPC を受信したが未だ応答していない。
.  RT_SCHED_RUNNING
  タスクは実行中である。これはSMP スケジューラーのだけで使用。

注意)返送されたタスク状態がただおよそのインフォメーションであることに注意してください。戻り値を評価する前に、タイマとかハードウェア割込みが起こりタスクの状態の変更を起こすかもしれません。もしタスクについて信頼性が高い情報を欲するなら、これは呼び出側で割り込みを停止するべきであることを意味します。
5.3.4 浮動少数点
void rt_linux_use_fpu (int use_fpu_flag)

この関数は FPU 使用の指示を行う。

パラメータ: 0以外で use_fpu_flag
タスクあるいはカーネルがアクティブになるとき、浮動小数点装置(FPU)コンテキストスイッチされます。

int rt_task_use_fpu(RT_TASK *task, int use_fpu_flag)

スケジューラにFPU使用を知らせます

パラメータ:
 task: はリアルタイムタスクへのポインタです。
 use_fpu_flag: が0でない値を持つならば、タスクあるいはカーネルがアクティブになるとき、浮動小数点装置(FPU)コンテキストスイッチは起きます。
戻り値:  成功=0
 負値:  − EINVAL :不正なtaskポインタ

(参照)



 
5.3.5 タスク制御
×int rt_task_signal_handler (RT_TASK *task, void(*)(void)handler)

リアルタイムタスクにシグナルハンドラーをインストールあるいは変更する。

パラメータ:
task: はリアルタイムタスクへのポインタです。
handler: はシグナル関数のエントリーポイントです。

タスクが rt_task_init () で生成されるとき、シグナルハンドラー関数を設定することができる。

int rt_task_make_periodic_relative_ns(RT_TASK * task, RTIME start_delay,RTIME period)

rt_task_make_periodic_relative_ns は周期的にタスク実行をします。

これはrt_task_init()で生成されたタスク、を rt_task_wait_period ()で周期的実行を指定することと同じです。最初の実行時は現在時刻から相対的 start_delay時間(ns)を定義します。

パラメータ:
task: タスクはあなたが周期的にすることを望むタスクへのポインタです。
 start_delay: 最初の実行までに待つ時間(ns)です。
period: タスクの期間(ns)です。
戻り値:
 0:       正常
 − EINVAL : taskが異常

例題による説明
黄色い蛍光ペンが特記すべき行であり、比較のために青色蛍光ペンの行があります。

Example 1 speaker.c:

#include <linux/kernel.h>
#include <linux/module.h>
#include <asm/io.h>
#include <rtai_nam2num.h>
#include <rtai_sched.h>
#include <rtai_scb.h>
#include "pcsp_tables.h"
MODULE_LICENSE("GPL");
//#define CONFIG_X86_64
#define SCBSUPRT USE_GFP_ATOMIC
#define SCBSIZ 2000
#define TICK_PERIOD 25000 /* 40 khz */
#define DIVISOR 5
#define STACK_SIZE 4000
/*You can make this bigger, but then you start to get
*clipping, which sounds bad. 29 is good.
*/
#define VOLUME 30
static RT_TASK thread;
static int cpu_used[NR_RT_CPUS];
static unsigned char vl_tab[256];
static int port61;
static void *scb;
static volatile int end;
#define PORT_ADR 0x61

static int filter(int x)
{
    static int oldx;
    int ret;
    if (x & 0x80) {
        x = 382 . x;
    }
    ret = x > oldx;
    oldx = x;
    return ret;
}

static void intr_handler(long t)
{
    char data, temp;
    int go = 0;
    int divisor = DIVISOR;
    while (!end) {
        if (!(--divisor)) {
            divisor = DIVISOR;
            cpu_used[hard_cpu_id()]++;
            go = !rt_scb_get(scb, &data, 1);
        }
        else {
            go = 0;
        }
        if (go) {
#ifdef CONFIG_X86_64
            data = filter(data);
            temp = inb(PORT_ADR);
            temp &= 0xfd;
            temp |= (data & 1) << 1;
            outb(temp, PORT_ADR);
#else
            outb(port61, 0x61);
            outb(port61^1, 0x61);
            outb(vl_tab[((unsigned int)data)&0xff], 0x42);
#endif
        }
        rt_task_wait_period();
    }
}

int init_module(void)
{
    int i;
    outb_p(0x92, 0x43); /* binary, mode1, LSB only, ch 2 */
    for (i = 0; i < 256; vl_tab[i] = 1 + ((VOLUME*ulaw[i]) >> 8), i++);
    port61 = inb(0x61) | 0x3;
    scb = rt_scb_init(nam2num("SCB"), SCBSIZ, SCBSUPRT);
    rt_task_init(&thread, intr_handler, 0, STACK_SIZE, 0, 0, 0);
   
rt_set_oneshot_mode();
   
start_rt_timer(0);
  
 rt_task_make_periodic_relative_ns(&thread, 10000000, TICK_PERIOD);
    return 0;
}

void cleanup_module(void)
{
    int cpuid;
    end = 1;
    stop_rt_timer();
    rt_task_delete(&thread);
    rt_scb_delete(nam2num("SCB"));
    printk("\n\nCPU USE SUMMARY\n");
    for (cpuid = 0; cpuid < NR_RT_CPUS; cpuid++) {
         printk("# %d -> %d\n", cpuid, cpu_used[cpuid]);
    }
    printk("END OF CPU USE SUMMARY\n\n");
}

int rt_task_make_periodic (RT_TASK * task, RTIME start_time, RTIME period)

この関数は rt_task_make_periodic_relative_nsと同様であり、start_time とperiodの単位がnsろクロックへ変更されたものである。

パラメータ:
task: は周期実行タスクへのポインタ
start_time: タスクスタートまでのクロック数
period: タスク起動周期クロック数
戻り値:
 0: 正常に関する
 − EINVAL : 異常taskを参照

See also: rt_task_make_periodic_relative_ns().

int rt_task_wait_period(void)

次の周期が到達されるまで、この関数は現在走っているリアルタイムタスクの実行をサスペンドし次の周期まで待ちます。
予めrt_task_make_periodic () あるいは rt_task_make_periodic_relative_ns () を呼び出してタスク周期実行を宣言していなければなりません。

戻り値:
  0: 期限切れで正常終了
 異常終了
    RTE_UNBLKD : スリープ中にブロックが解除された。
    RTE_TMROVRN : 次の周期がすでに期限が切れで、即リターンされた。

(注意)タスクがただ一時的にしばらく見合わせられ、次の時間周期までコントロールを断念することを意味します。

RTIME next_period (void)

rt_task_wait_period を呼び出す際、周期的なタスクが再開されるであろう時間を得ます。 この関数は呼び出しタスクが次に走るであろう時間を返します。 適当な rt_get_time関数 と組み合わせて、使用済みの周期時間部分あるいは周期超過時間などのチェックに使うことができます。
戻り値: 内部のカウントユニットでの次の臭気時間。

void rt_busy_sleep (int ns)

この関数はスケジューラーにコントロールを返さないで指定された一定の時間タスクの実行を 遅延/サスペンドします。 これは rt_busy_sleep が単にCPU時間を消費することを意味します。
従って、非常に短い同期化遅延のためにだけ使われるべきです。

パラメータ: ns は遅延ナノセカンド数です


rt_sleep(RTIME delay)

それは内部のカウントユニットで自タスクの実行を遅延時間サスペンドします。スリープ間にCPUは他のタスクによって使われます。
戻り値: もし正常に遅延が終了するか、ブロック状態解除あるいは、すでに時間遅延時間を経過した、この機能は0を返します。

rt_sleep_until(RTIME time)

rt_sleep()と同様ですが、遅延時間は絶対時刻で指定します。従って、絶対時刻後にこの関数を呼んでも効果を持たない。

これらのsleep関数のスリープ中により高いプライオリティーのタスク、割り込みが実行されることがある。この場合sleep時間経過後直ちにコントロールを得られない場合があるかも知れません。
 
 
5.3.6 オブジェクト関係
int rt_register (unsigned long name, void *adr, int type, struct task_struct * t)
 
この関数は adr が指すオブジェクトを名称nameで(タスク構造体へ)記録するために使われます。

リターン:
=<0 : 成功
 >0 : エラー

次のソースコードはハードリアルタイムが割込みハンドラからすぐに処理するユーザースペースをウェイクアップさせるための2つの方法を示します。


Example 2 rt_handler.c:
#include <linux/module.h>
#include <asm/io.h>
#include <rtai_registry.h>
#include <rtai_mbx.h>
#include "period.h"
static MBX mbx;
static SEM sem;
static char wakeup;

static void rt_timer_tick(void)
{
    RT_TASK *task;
    rt_times.tick_time = rt_times.intr_time;
    rt_times.intr_time = rt_times.tick_time + rt_times.periodic_tick;
    rt_set_timer_delay(0);
    if (rt_times.tick_time >= rt_times.linux_time) {
        rt_times.linux_time += rt_times.linux_tick;
        rt_pend_linux_irq(TIMER_8254_IRQ);
    }
    rt_mbx_receive_if(&mbx, &wakeup, 1);
    if (wakeup) {
        if ((task = rt_get_adr(nam2num("PRCTSK")))) {
            if (wakeup==1) rt_sem_signal(&sem);
            else rt_task_resume(task);
        }
    }
}

int init_module(void)
{
    rt_mbx_init(&mbx, 1);
    rt_register(nam2num("RESMBX"), &mbx, IS_MBX, 0);
    rt_sem_init(&sem, 0);
    rt_register(nam2num("RESEM"), &sem, IS_SEM, 0);
    rt_request_timer(rt_timer_tick, imuldiv(PERIOD, FREQ_8254, 1000000000), 0);
    return 0;
}
void cleanup_module(void)
{
    rt_free_timer();
    rt_mbx_delete(&mbx);
    rt_sem_delete(&sem);
}

int rt_drg_on_name (unsigned long name)

この関数はnameで指定されたオブジェクトの記録削除する。
戻り値:
  <0: 正の成功
  
0:  エラー

int rt_drg_on_adr(void * adr)

同様にポインタが指すオブジェクト名のオブジェクトの記録削除。

void* rt_get_adr (unsigned long name)

nameで指定されたオブジェクトを取得します。
戻り値: オブジェクトへのポインタ
 0: エラー

unsigned long rt_get_name (void * adr)

この関数はそのadrが指すオブジェクトからオブジェクト名を取得します。
戻り値: オブジェクト名へのポインタ
 0: エラー
 

Guest No.