為了使英創(chuàng)的嵌入式主板產(chǎn)品滿足工業(yè)實(shí)時(shí)控制的應(yīng)用需求,我們對(duì)ESM335x系列主板的內(nèi)核進(jìn)行了調(diào)整,重新移植了Linux 4.1.7的系統(tǒng),在Linux 4.1.7的基礎(chǔ)上使用了kernel的RT PATCH(實(shí)時(shí)補(bǔ)丁),并且修改了相關(guān)接口的驅(qū)動(dòng)代碼,來(lái)保證操作系統(tǒng)的實(shí)時(shí)性。關(guān)于實(shí)時(shí)Linux(又稱RT Linux)系統(tǒng)的相關(guān)技術(shù)知識(shí)可以瀏覽網(wǎng)站:https://wiki.linuxfoundation.org/realtime/documentation/start進(jìn)行了解。我們將在下面介紹如何編寫面向?qū)崟r(shí)控制的應(yīng)用程序,以及在實(shí)時(shí)Linux系統(tǒng)和標(biāo)準(zhǔn)Linux系統(tǒng)環(huán)境中,應(yīng)用程序?qū)τ布录憫?yīng)延時(shí)的測(cè)試結(jié)果。
實(shí)時(shí)應(yīng)用程序編寫
盡管實(shí)時(shí)Linux系統(tǒng)對(duì)內(nèi)核做了大量的改動(dòng)來(lái)提供系統(tǒng)的實(shí)時(shí)性,但對(duì)于應(yīng)用程序來(lái)說(shuō),只需要對(duì)有實(shí)時(shí)要求的線程設(shè)置實(shí)時(shí)優(yōu)先級(jí)屬性,以及設(shè)置響應(yīng)的實(shí)時(shí)線程調(diào)度策略就可以了,并沒(méi)有特殊的API函數(shù)。關(guān)于設(shè)置實(shí)時(shí)優(yōu)先級(jí)以及調(diào)度策略請(qǐng)參考我們網(wǎng)站上的文章:《Linux系統(tǒng)調(diào)度簡(jiǎn)介》,本文中用到的函數(shù)以及結(jié)構(gòu)體都在其中有更為詳細(xì)的介紹。
設(shè)置實(shí)時(shí)優(yōu)先級(jí)以及調(diào)度策略有兩種方式,一種是在創(chuàng)建線程時(shí)設(shè)置線程的屬性,這樣線程創(chuàng)建后就具有了實(shí)時(shí)優(yōu)先級(jí)并且使用設(shè)置的調(diào)度策略,函數(shù)原型如下:
設(shè)置實(shí)時(shí)優(yōu)先級(jí)屬性:
int pthread_attr_setschedparam (pthread_attr_t *attr, const struct sched_param *param)
上述函數(shù)所包含的2個(gè)參數(shù)說(shuō)明如下:
● pthread_attr_t *attr 由pthread_attr_init函數(shù)初始化,是線程的屬性
● struct sched_param結(jié)構(gòu)體包含 int sched_priority,也即實(shí)施優(yōu)先級(jí),取值范圍0~99。數(shù)值越大優(yōu)先級(jí)越高,所有的實(shí)時(shí)線程優(yōu)先級(jí)都高于普通線程。為了提高系統(tǒng)的實(shí)時(shí)性,RT Linux將大部分中斷服務(wù)都改為了線程的形式,使得中斷服務(wù)可以被實(shí)時(shí)要求更高的線程搶占,中斷處理線程實(shí)時(shí)優(yōu)先級(jí)為50,操作系統(tǒng)中最重要的線程(如看門狗線程)的優(yōu)先級(jí)為99,當(dāng)知道應(yīng)用程序的處理需要等待某個(gè)特定的中斷才能繼續(xù)執(zhí)行,而且該段代碼實(shí)時(shí)性要求很高時(shí),就可以將該段代碼所在線程的實(shí)時(shí)優(yōu)先級(jí)設(shè)置為大于50小于99的值,這樣就能夠不受其他中斷處理線程的影響,最大限度的滿足實(shí)時(shí)性要求,比如我們?cè)谙旅娴臏y(cè)試程序中設(shè)置我們的線程實(shí)施優(yōu)先級(jí)為80。
設(shè)置調(diào)度策略:
int pthread_attr_setschedpolicy (pthread_attr_t *attr, int policy)
● policy 為調(diào)度策略,可選SCHED_FIFO或者SCHED_RR,兩者都是實(shí)時(shí)調(diào)度策略,SCHED_FIFO為先進(jìn)先出,只有高實(shí)時(shí)優(yōu)先級(jí)線程能搶占當(dāng)前運(yùn)行的實(shí)時(shí)線程,SCHED_RR增加了時(shí)間片機(jī)制,使得同優(yōu)先級(jí)的實(shí)時(shí)線程能夠輪轉(zhuǎn)運(yùn)行。
使用示例:
// 設(shè)置調(diào)度策略為SCHED_FIFO
res = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if(res)
{
printf("pthread setschedpolicy faild\n");
}
struct sched_param param;
// 設(shè)置實(shí)時(shí)優(yōu)先級(jí)為80
param.sched_priority = 80;
res = pthread_attr_setschedparam(&attr, ¶m);
if(res)
{
printf("pthread setschedparam faild\n");
}
另一種方式是修改已經(jīng)存在的線程,設(shè)置其實(shí)時(shí)優(yōu)先級(jí)和使用的調(diào)度策略,可以在其他線程中設(shè)置,也可以在線程中自行設(shè)置,只需要知道線程的PID,線程自行設(shè)置時(shí)PID為0,函數(shù)原型如下:
int sched_setscheduler (pid_t pid, int policy, const struct sched_param *param)
使用示例:
struct sched_param param;
// 設(shè)置實(shí)時(shí)優(yōu)先級(jí)為80并且使用調(diào)度策略SCHED_FIFO
param.sched_priority = 80;
if(sched_setscheduler(0, SCHED_FIFO, ¶m))
printf("wrong sched_setscheduler\n");
還有其他的函數(shù)可以設(shè)置這些相關(guān)屬性,具體請(qǐng)參考我們網(wǎng)站的文章《Linux調(diào)度策略簡(jiǎn)介》,或者其他相關(guān)資料。一般的使用是將有實(shí)時(shí)要求的功能代碼放入單獨(dú)的線程中,主程序以普通線程啟動(dòng)后建立實(shí)時(shí)線程來(lái)完成相關(guān)功能。由于實(shí)時(shí)線程的不可搶占性,不恰當(dāng)?shù)氖褂糜锌赡軙?huì)造成系統(tǒng)資源被無(wú)限占用,而影響系統(tǒng)的正常運(yùn)行,因此在開(kāi)發(fā)階段需要特別注意,規(guī)劃好實(shí)時(shí)線程的任務(wù),尤其是要檢查循環(huán)語(yǔ)句,對(duì)于循環(huán)任務(wù)的設(shè)計(jì)可以參考網(wǎng)站資料:https://wiki.linuxfoundation.org/realtime/documentation/howto/applications/cyclic。
RT Linux的實(shí)時(shí)性測(cè)試
在我們的測(cè)試中,由GPIO中斷作為硬件事件,具體是使用外部的方波信號(hào)發(fā)生器作為中斷源,驅(qū)動(dòng)檢測(cè)到GPIO中斷后會(huì)喚醒用戶層的中斷處理線程(該線程已設(shè)置為實(shí)時(shí)線程),實(shí)時(shí)中斷處理線程作為對(duì)中斷事件的響應(yīng),是將另一位GPIO的輸出電平反相(Toggle處理)。用示波器測(cè)量輸入的中斷信號(hào)和作為響應(yīng)的GPIO信號(hào)之間的時(shí)間延遲。程序部分代碼如下:
struct paraset
{
int fd; // 中斷設(shè)備對(duì)應(yīng)的文件
int gpio; // 線程使用的GPIO進(jìn)行toggle處理
int rt_prio; // 線程的實(shí)時(shí)優(yōu)先級(jí),0則設(shè)置為普通線程
};
int IRQSelectThreadFunc(void* lparam)
{
struct paraset * p = (struct paraset*)lparam;
int fd = p->fd ; // 獲取中斷設(shè)備對(duì)應(yīng)的文件識(shí)別號(hào)
int i=0;
printf ( "fd = %d \n", fd );
struct sched_param param;
param.sched_priority = p->rt_prio; // 設(shè)置實(shí)時(shí)優(yōu)先級(jí),由主線程傳入
if(p->rt_prio)
if(sched_setscheduler(0, SCHED_FIFO, ¶m))
printf("wrong sched_setscheduler\n");
fd_set fdRead;
struct timeval aTime;
int ret;
GPIO_OutEnable(gpio_fd, 1<<p->gpio);
while(1)
{
FD_ZERO(&fdRead);
FD_SET(fd,&fdRead);
aTime.tv_sec = 2;
aTime.tv_usec = 0;
// 等待中斷事件
ret = select ( fd+1, &fdRead, NULL, NULL, &aTime );
if ( ret<0 )
printf( "select, something wrong!\n " );
if ( ret>0 )
{
if ( FD_ISSET(fd, &fdRead) )
{
//toggel one gpio
nIrqCounter++;
if(nIrqCounter%2)
GPIO_OutClear(gpio_fd, 1<<p->gpio);
else
GPIO_OutSet(gpio_fd, 1<<p->gpio);
}
}
}
pthread_exit( NULL );
return 0;
}
int StartPulseThread( struct paraset *p )
{
pthread_attr_t attr;
pthread_t m_thread;
int res;
struct sched_param param;
// 創(chuàng)建gpio線程
res = pthread_attr_init(&attr);
if( res!=0 )
{
printf("Create attribute failed\n" );
}
// 主程序傳入的參數(shù),判斷實(shí)時(shí)優(yōu)先級(jí)是否為0,不為0則設(shè)置為實(shí)時(shí)線程
if(p->rt_prio)
{
// 設(shè)置調(diào)度策略為SCHED_FIFO
res = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
if(res)
{
printf("pthread setschedpolicy faild\n");
}
// 設(shè)置實(shí)時(shí)優(yōu)先級(jí)
param.sched_priority = p->rt_prio;
res = pthread_attr_setschedparam(&attr, ¶m);
if(res)
{
printf("pthread setschedparam faild\n");
}
res = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if(res)
{
printf("pthread setinheritshed faild\n");
}
printf("set %d rt_priority: %d\n", p->fd, p->rt_prio);
}
// 創(chuàng)建線程
res = pthread_create( &m_thread, &attr, (void *(*) (void *))&IRQSelectThreadFunc, p );
if( res!=0 )
{
return -1;
}
pthread_attr_destroy( &attr );
return 0;
}
我們用同一個(gè)測(cè)試程序分別在ESM3354和ESM3352的標(biāo)準(zhǔn)Linux版本和RT Linux上運(yùn)行,當(dāng)輸入100Hz方波信號(hào),即硬件中斷間隔為10ms時(shí),隨機(jī)測(cè)試100次中斷延時(shí)的統(tǒng)計(jì)結(jié)果如下:
主板型號(hào) | ESM3354 | ESM3352 | ||
CPU信息 | Cortex-A8 主頻1GHz | Cortex-A8 主頻600MHz | ||
操作系統(tǒng) | Linux-4.1.6 | RT Linux-4.1.7 | Linux-4.1.6 | RT Linux-4.1.7 |
空載中斷延時(shí)最小值(us) | 16 | 15 | 26 | 20 |
空載中斷延時(shí)最大值(us) | 24 | 20 | 39 | 27 |
空載中斷延時(shí)平均值(us) | 19.64 | 16.16 | 30.92 | 25.04 |
空載中斷延時(shí)標(biāo)準(zhǔn)值(us) | 1.22 | 0.69 | 2.41 | 0.65 |
滿載中斷延時(shí)最小值(us) | 88 | 58 | 100 | 80 |
滿載中斷延時(shí)最大值(us) | 1050 | 185 | 2200 | 220 |
滿載中斷延時(shí)平均值(us) | 140.73 | 94.36 | 183.06 | 108.94 |
滿載中斷延時(shí)標(biāo)準(zhǔn)值(us) | 115.17 | 17.33 | 246.15 | 18.81 |
從上表可以看到:
● 盡管在空載情況下普通Linux系統(tǒng)與RT Linux系統(tǒng)的中斷延時(shí)參數(shù)差異不大,但在滿負(fù)荷下差異就非常明顯了,特別是中斷延時(shí)最大值。均超過(guò)1ms,這意味著普通Linux只能用于對(duì)實(shí)時(shí)性要求很低的場(chǎng)合。相對(duì)的,RT Linux在這項(xiàng)指標(biāo)上有了很大的提高,已可滿足最小硬件中斷間隔為1ms的應(yīng)用。
● RT Linux無(wú)論在空載還是滿載的情況下,中斷延時(shí)的方差,相對(duì)其平均值都比較小,這表示RT Linux系統(tǒng)對(duì)硬件中斷的響應(yīng)延時(shí)很一致,響應(yīng)時(shí)間更穩(wěn)定,這也是良好實(shí)時(shí)性的重要指標(biāo)。
● ESM3354相比于ESM3352由于CPU主頻更高,響應(yīng)時(shí)間更短,但是在RT Linux上的響應(yīng)時(shí)間均方差差別不大。由于ESM3352成本更低,而對(duì)于實(shí)時(shí)應(yīng)用來(lái)說(shuō)響應(yīng)的實(shí)時(shí)性與ESM3354基本相同,所以在選購(gòu)時(shí)客戶可以優(yōu)先考慮性價(jià)比更高的ESM3352。
進(jìn)一步,把外部中斷頻率提高一個(gè)數(shù)量級(jí),即把中斷間隔縮短至1ms,再來(lái)比較中斷響應(yīng)延時(shí)的變化。測(cè)試數(shù)據(jù)如下:
主板型號(hào) | ESM3354 | ESM3352 | ||
CPU信息 | Cortex-A8 主頻1GHz | Cortex-A8 主頻600MHz | ||
操作系統(tǒng) | RT Linux-4.1.7 | |||
硬件中斷間隔 | 10000us | 1000us | 10000us | 1000us |
空載中斷延時(shí)最小值(us) | 15 | 14 | 20 | 22 |
空載中斷延時(shí)最大值(us) | 20 | 18 | 27 | 30 |
空載中斷延時(shí)平均值(us) | 16.16 | 14.80 | 25.04 | 23.25 |
空載中斷延時(shí)標(biāo)準(zhǔn)值(us) | 0.69 | 0.57 | 0.65 | 0.92 |
滿載中斷延時(shí)最小值(us) | 58 | 40 | 80 | 54 |
滿載中斷延時(shí)最大值(us) | 185 | 125 | 220 | 160 |
滿載中斷延時(shí)平均值(us) | 94.36 | 57.30 | 108.94 | 70.58 |
滿載中斷延時(shí)標(biāo)準(zhǔn)值(us) | 17.33 | 9.69 | 18.81 | 11.78 |
可以看到,外部中斷間隔變小之后,響應(yīng)延時(shí)并沒(méi)有變得更大,反而由于代碼執(zhí)行頻率增加而更有可能常駐高速cache從而更快的得到運(yùn)行,減小響應(yīng)延時(shí)。考慮到我們測(cè)試程序的中斷處理線程在收到中斷后處理比較簡(jiǎn)單,而實(shí)際應(yīng)用中往往需要執(zhí)行更為復(fù)雜的操作,需要更多的執(zhí)行時(shí)間,一般在50us的水平,這樣從硬件中斷開(kāi)始,到處理線程完成響應(yīng)動(dòng)作,最長(zhǎng)需要大約270us時(shí)間。
根據(jù)以上的測(cè)試分析,可以認(rèn)為ESM335x + RT Linux系統(tǒng)是完全能滿足最小硬件中斷間隔不低于1ms的實(shí)時(shí)控制應(yīng)用,這意味著可滿足大部分的實(shí)時(shí)控制的應(yīng)用需求。
我們也將會(huì)在近期推出ESM6800的RT Linux版本,ESM6800采用iMX6UL Cortex-A7 528MHz CPU,以低功耗、低成本為特色,特別適用于對(duì)成本敏感的批量工業(yè)智能設(shè)備。請(qǐng)關(guān)注我們官網(wǎng)以及官方微信公眾號(hào)的最新信息。
有興趣的客戶可以直接和我們的工程師進(jìn)行溝通獲取相關(guān)文件進(jìn)行評(píng)估測(cè)試。
成都英創(chuàng)信息技術(shù)有限公司 028-8618 0660