天天看點

RTLinux程式設計總結

做過一個有關rtlinux的項目,時間一長,差不多忘光了,現在盡量把原來做過的東西總結一下,以備後用,同時正在做類似項目的一個借鑒

平台

主機:redhat 8.0

目标機:pc104子產品、isa總線脈沖輸出、實時序列槽通信

         linux-2.4.18.tar.bz2 +rtlinux-3.2-pre1.tar.bz2

簡述

linux是典型的分時應用系統,對于實時性要求很高的應用,必須對核心本身動手術。而rtlinux則采取了一種比較聰明也比較折中的辦法:他們實作一個最底層的精簡的排程器,用于排程實時線程,原來的核心本身則成為實時排程器的一個優先級最低的任務。這樣,當有實時任務時,普通核心已經建立于其上的普通程序被強制中斷,實時線程被強制執行;隻有當若有實時線程都讓出cpu之後,普通核心才被運作,再由普通核心去排程執行普通的應用程式……

執行個體

#include <rtl_fifo.h>

#include <rtl.h>

#include <rtl_sched.h>

#include <time.h>

#include <pthread.h>

#include <rtl_time.h>

#include <signal.h>

#include "rt_com.h"

#include <linux/kernel.h>

#include <linux/module.h>

module_license("gpl v2");

module_author("wind-son");

module_description("pulse-control system");

typedef unsigned short __u16;

void io_bit_on(__u16 port, unsigned int pos, __u16 *status)

{

        __asm__ __volatile__(

                "movl %1,%?x\n\t"

                "movl %0,%?x\n\t"

                "btsl %2,(%?x)\n\t"

                "mov (%?x),%%al\n\t"

        "out %%al,(%%dx)\n\t"

        "out %%al,$0x80\n\t"

                :

                :"m"(status), "rm"(port), "ir"(pos)

        );

}

void io_bit_off(__u16 port, unsigned int pos, __u16 *status)

        __asm__ __volatile__(      

                "btrl %2,(%?x)\n\t"

#define dbg_print rtl_printf

#define min_time              5000

static void get_time_interval(void)

void* pulse_generate_thread(void *arg)

        static __u16 io_status = 0;

        struct sched_param p;

        hrtime_t current_time;

        real_time_get_enable;

        int intrrupt_sched_period = 180000;

        p.sched_priority = 1;

        struct timespec resolution;

        rtl_setclockmode(clock_realtime, rtl_clock_mode_periodic,

                        intrrupt_sched_period);

        clock_getres(rtl_getschedclock(), &resolution);

        intrrupt_sched_period = timespec_to_ns(&resolution);

        pthread_make_periodic_np(pthread_self(), clock_gethrtime(rtl_getschedclock()),

                intrrupt_sched_period);

        pthread_setschedparam (pthread_self(), sched_fifo, &p);

        for (;;) {

                dbg_print("debug entry\n");

                while (!ready)

                    pthread_wait_np();

                dbg_print("debug exit\n");

                if (!init_rt_clock) {

                        init_rt_clock = 1;

                        pthread_wait_np();

                        current_time = clock_gethrtime(clock_realtime);

                } else {

                    if (intrrupt_sched_period < min_time)

                                intrrupt_sched_period = min_time;

                    current_time += intrrupt_sched_period;

                    clock_nanosleep(clock_realtime, timer_abstime, hrt2ts(current_time), null);

                }

                io_bit_on(io_port_out, xpulse, &io_status);

            intrrupt_sched_period = get_time_interval();

        }

        return 0;

static void init_for_rt_mm(void)

static void rt_alloc_mm(void)

        thread_wait_np();

        buf = kmalloc(size, gfp_atomic);

static int kmalloc_thread(void * kthread_arg)

        unsigned long timeout = hz;

        init_for_rt_mm();

                while (!get_flag(mm_alloc_flag)) {

                        if( signal_pending(current))

                                return 0;

                        timeout = interruptible_sleep_on_timeout(&wq, timeout);

                rt_alloc_mm();

                clear_flag(mm_alloc_flag);

        return -1;

wait_queue_head_t wq;

static pid_t kmalloc_kthread_id;

static int kmalloc_kthread_state = 1;

static int pulse_generate_thread_created = 0;

static int main_ctrl_thread_created = 0;

static pthread_t pulse_generate_pthread;      

static pthread_t main_ctrl_pthread;

static pthread_mutex_t cache_mutex;

void rt_mm_request(void)

        set_flag(mm_alloc_flag);

        while(get_flag(mm_alloc_flag))      

                pthread_wait_np();

void* main_ctrl_thread(void *arg)

        int work_sched_period = 160000;

        int ret1 = rtl_setclockmode(rtl_getschedclock(), rtl_clock_mode_periodic,

                work_sched_period);              

        if (ret1) {

                dbg_print("seting periodic mode failed\n");

                clear_flag(work_sched_mode);

        work_sched_period = timespec_to_ns(&resolution);

            work_sched_period);

        init_task();      

                if (work) {

                        dbg_print("work\n");

                        rt_mm_request();

                        calc_time_interval();

                        if (exit)

                            break;

                } else

        exit_task();

    return 0;

int init_module(void)

        pthread_attr_t attr;

        int ret;

        rtf_destroy(0);

        rtf_destroy(1);

        rt_com_clr_in(0);

        rt_com_clr_out(0);

        int fifo_status = rtf_create(0,100);

        if(fifo_status)

                dbg_print("fifo create failed!");

        fifo_status = rtf_create(1, 4000);

        rt_com_setup(0, 9600, rt_com_parity_none, 1, 8);

        hrtime_t now = gethrtime();

        pthread_attr_init(&attr);

        pthread_mutex_init(&cache_mutex, null);

        pthread_attr_setfp_np(&attr, 1);

        ret = pthread_create(&pulse_generate_pthread, &attr,

                pulse_generate_thread, (void *)0);

        if (!ret)

                pulse_generate_thread_created = 1;

        pthread_make_periodic_np (pulse_generate_pthread, now + 2 * 240000, 80000);

        p . sched_priority = 1;

        pthread_setschedparam (pulse_generate_pthread, sched_fifo, &p);

        ret = pthread_create(&main_ctrl_pthread, &attr, main_ctrl_thread, (void *)1);

                main_ctrl_thread_created=1;

        pthread_make_periodic_np (main_ctrl_pthread, now + 2 * 160000, 30000);

        p . sched_priority = 2;

        pthread_setschedparam (main_ctrl_pthread, sched_fifo, &p);

        init_waitqueue_head(&wq);

        kmalloc_kthread_id = kernel_thread(kmalloc_thread, null, 0);

        if (kmalloc_kthread_id < 0) {

                printk(kern_err "fork failed, errno %d\n", -kmalloc_kthread_id);

                return kmalloc_kthread_id;

        return ret;

void cleanup_module(void)

        int ret = kill_proc(kmalloc_kthread_id, sigkill, 1);

        if (!ret) {

                int count = 10 * hz;

                while (kmalloc_kthread_state && --count) {

                        current->state = task_interruptible;

                        schedule_timeout(1);

        if (main_ctrl_thread_created) {

                pthread_cancel(main_ctrl_pthread);

                pthread_join(main_ctrl_pthread, null);

                pthread_delete_np(main_ctrl_pthread);

        if (pulse_generate_thread_created) {

                pthread_cancel(pulse_generate_pthread);

                pthread_join(pulse_generate_pthread, null);

                pthread_delete_np(pulse_generate_pthread);

        rt_com_setup(0, -1, 0, 0, 0);

        pthread_mutex_destroy (&cache_mutex);

複制代碼

其實作在有關linux實時應用的原理和應用方面的介紹已經不少,是以我主要是想從自己的親身實踐中的經驗教訓出發總結一下。

我遇到的主要問題主要有以下幾個:

1、硬實時排程精度不夠的問題。剛開始産生脈沖驅動的線程我按照例子程式采樣如下方式

   pthread_make_periodic_np();  //設定排程方式和周期等參數

    pthread_setschedparam (pthread_self(), sched_fifo, &p);

    pthread_wait_np(); //讓出cpu進入睡眠

  可實際情況總是不理想,輸出波形不夠穩定,離預想的效果也很遠。試着将排程政策sched_fifo改其他幾種方式,也一樣。最後嘗試用clock_nanosleep()才達到了比較理想的效果。理論上clock_nanosleep()應該達到ns級别的精度,當然實際精度還要取決于硬體。

2、實時線程所能到達的實時效果和精度極限也就是定時器本身的精度了。有過在51上做開發經驗的都有這樣一個意識:定時器中斷處理例程裡盡量隻做最簡單、最必須的工作,但畢竟還是有開銷的。如果你對精度還有更高的要求,可在main_ctrl_thread()即負責計算脈沖間隔時間的例程中加入補償值,以抵消脈沖輸出例程中的時間開銷。

3、實時線程中頻繁的動态申請記憶體時常當機。後來經過實驗摸索才采取了上面代碼中所述的拐彎抹角的辦法。如果誰碰到過類似問題有更好的辦法,還望指出。

4、應用程式直接向序列槽輸出時總出錯。

   開始方法是system("/bin/ls ./data/ >> /dev/ttys0";在沒有實時線程的影響的情況下,這樣是沒有問題。開啟實時線程後就老出錯。

後改成如下方式就好了:由實時子產品通過實時調用rt_com_write()和rt_com_read()讀寫序列槽;再通過實時管道rtl_fifo轉發到應用程式

另外,純粹經驗談,實時線程如果不主動讓出cpu,任何使用者程式無法運作,包括你的鍵盤響應!如果你的某個環節可能陷入循環,你能做的就隻有poweroff了;被迫重新開機後在ext2檔案系統上随之而來的是漫長的fscheck……是以我在調試階段,基本上是隻要有循環的的方,就加上pthread_wait_np();以後再慢慢把不必要的去掉。

繼續閱讀