1、前言
近些年,大量的智能設備已經進入人們的生活, 如智能音箱,各種支付設備,大街小巷上各種大小的廣告機等。這些設備中大多還是採用Linux,帶屏的設備則大多采用Android,導致他們必須採用較高成本的硬件。
AliOS Things是阿里雲IoT研發的應用於物聯網智能設備的嵌入式實時操作系統,是HaaS100搭載的操作系統。目前AliOS Things + 小程序框架已經可以替代Linux甚至Android,大大降低設備的成本。可是Linux(包括Android)上的成熟穩定的框架,尤其是音視頻處理的框架,圖形渲染等,如果可以拿來使用,再加以適配優化,不需要重複造輪子,豈不美哉。
那如何把Unix/Linux這些優秀的框架輕鬆地移植到AliOS Things上呢?支持POSIX就是實現這個目標的一把利器。
2、POSIX是什麼
POSIX(Portable Operating System Interface)是IEEE組織爲了維護應用在不同操作系統之間的兼容性而制定的標準。 主要包括API,Shell和Utility等一整套應用運行環境。廣泛應用於UNIX/LINUX操作系統,和一些嵌入式操作系統中(如Zephyr, VxWorks, QNX, Fuchsia,FreeRTOS,RT-Thread,AliOS Things)。
POSIX標準也被稱爲IEEE 1003,ISO/IEC 9945,目前標準的開發者是Austin Group,它是IEEE, Open Group, ISO/IEC的聯合組織,目前POSIX標準的最新版本是POSIX.1-2017。 剛開始時POSIX標準(IEEE 1003)又分爲不同的子集,其中還處於有效狀態的有IEEE 1003.13。 IEEE 1003.13是針對嵌入式領域制定的標準,根據範圍的大小又分爲4個不同的Profile,PSE51, PSE52, PSE53和PSE54,其關係如下圖。
3、爲什麼需要POSIX
AliOS Things作爲一個物聯網領域的嵌入式實時操作系統爲什麼要支持POSIX標準呢?除了解決前言引入的問題外,還有沒有其他的目的呢?本章節將進行一個全面的闡述。
3.1、POSIX解決的核心問題
軟件生態
軟件生態是一個OS的生存核心,但建設一個OS的軟件生態又不能急於求成, 需要經過多年的積累沉澱。 顯然AliOS Things的生態還不成熟,而Linux的生態則經過了幾十年的沉澱,變得非常強大。支持POSIX則可以
- 兼容Unix/Linux軟件生態。
- 兼容支持POSIX標準的嵌入式系統(如FreeRTOS)的軟件生態。
標準
- API模型由國際權威組織定義,且被廣泛使用驗證過,成熟穩定。
易用
- API被廣大開發者所熟悉,降低開發者學習成本。
- 每個API都有標準化的文檔詳細說明,方便查詢使用。
- 爲AliOS Things內部組件提供標準接口,方便使用和支持多平臺如移植到Linux。
3.2、其它支持POSIX標準的操作系統
其實不只有我們這麼想,讓我們一起看看業界的嵌入式實時操作系統是否支持POSIX標準。
VxWorks
作爲比較老牌的嵌入式實時操作系統,VxWorks被廣泛應用於多個領域,如航空航天,工業控制等對實時性要求很高的領域, 它也是非常重視對POSIX標準的支持,其全部支持了PSE52標準 + BSD Socket。並通過官方的PSE52認證。
QNX
作爲被廣泛應用於汽車領域的嵌入式實時操作系統,同時也是比較成功的商用微內核操作系統,QNX也是比較重視對POSIX標準的支持,其全部支持了PSE52標準 + BSD Socket。
Fuchsia
作爲Google全新設計研發的微內核操作系統, Fuchsia也是支持POSIX標準的。
FreeRTOS
FreeRTOS雖然主要應用於資源比較受限的MCU設備, 其也實現部分PSE52範圍內的API。
RT-Thread
RT-Thread主要應用於物聯網領域的智能設備, 其也比較重視對POSIX標準的支持,實現PSE52範圍內的大部分API。
AliOS Things支持POSIX的長遠目標是實現POSIX.1的最新版本,如目前是POSIX.1-2017,它共有1191個API,數量是非常大的,但是有很多並不是經常使用的API,因此短期目標是實現PSE52 + Networking標準的API + 項目中需要的API。
4、POSIX的設計與實現
POSIX作爲內核與應用的接口層, 涉及到內核的多個方面。 下面僅以POSIX線程和POSIX條件變量爲例介紹其設計與實現,POSIX組件的代碼位於core/osal/posix/, 頭文件位於include/posix/。
4.1、POSIX線程
關鍵數據結構
typedef struct _pthread_tcb {
unsigned int magic;
pthread_attr_t attr;
ktask_t *tid;
void *(*thread_entry)(void *para);
void *thread_para;
ksem_t *join_sem;
unsigned char cancel_state;
volatile unsigned char cancel_type;
volatile unsigned char canceled;
_pthread_cleanup_t *cleanup;
_pthread_environ_t *environ;
void **tls;
void *return_value;
} _pthread_tcb_t;
_pthread_tcb_t是POSIX線程內部的核心數據結構,保存着POSIX線程的關鍵數據,與內核task的tcb結構相對應,且通過tid相互關聯。POSIX線程的關鍵數據類型pthread_t會關聯到這個數據結構上。
- magic 是POSIX線程的魔術字,以區別於使用AliOS Things原生的AOS API創建的線程。
- attr 線程屬性,爲POSIX線程設置屬性以更細粒度地控制線程的行爲。如線程的棧地址,棧大小,線程的調度策略,調度參數等。
- tid 指向內核task的tcb結構。
- thread_entry 新創建線程的執行入口函數的指針。
- thread_para 新創建線程的執行入口函數的參數結構指針。
- join_sem 實現線程的JOINABLE的信號量,線程退出時自身不釋放ptcb資源,由其他線程調用pthread_join獲取返回值,並釋放資源。
- cancel_state/cancel_type/canceled 分別表示POSIX線程取消狀態(是否使能取消),取消類型(延遲取消、立馬取消),是否取消。目前POSIX線程的取消功能暫未實現。
- cleanup 線程在退出時要執行的清理函數指針的鏈表。
- tls 存放線程私有數據。
- return_value 存放POSIX線程的返回值的指針。
POSIX線程的創建與銷燬
使用pthread_create創建POSIX線程。
使用pthread_exit銷燬一個POSIX線程, 或者從一個線程的入口函數返回,也會走到線程的銷燬流程。
下面流程圖爲保持邏輯清晰,略去了很多實現細節如異常處理等。
4.2、POSIX條件變量
POSIX條件變量的標準定義:https://pubs.opengroup.org/onlinepubs/9699919799/
關鍵數據結構
typedef struct pthread_cond {
kmutex_t *lock;
int waiting;
int signals;
ksem_t *wait_sem;
ksem_t *wait_done;
pthread_condattr_t attr;
} pthread_cond_t;
pthread_cond_t是實現POSIX條件變量的核心數據結構。
- lock 是保護內部數據的一把mutex鎖。
- waiting 等待這個條件變量的線程數。
- signals 已發送信號還未收到確認的數目。
- wait_sem 線程等待的信號量,底層內核的信號量原語。
- wait_done 用於發送線程與等待線程之間握手確認的信號量, 底層內核的信號量原語。
- attr 記錄pthread條件變量的屬性,比如條件變量用到的時鐘clock。
POSIX條件變量的處理
POSIX 條件變量不僅支持觸發單個等待的線程,同時還支持廣播(pthread_cond_broadcast),觸發多個等待的線程。
4.3、AliOS Things上POSIX接口實現
AliOS Things 支持了pthread,semaphore, message queue, timer, fs等多個模塊的豐富的API,開發者可以利用這些POSIX API,只需要簡單地修改,甚至無需修改,就可以移植Unix/Linux的應用到AliOS Things上。再結合HaaS100的開發板,開發者可以更快速地構建智能設備所需的軟件和硬件。
5、動手試試
5.1、寫一個Linux下基於POSIX接口的應用
下面動手編寫一個簡單的使用POSIX PTHREAD API的Linux應用,保存爲pthread_test.c
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
static volatile int count = 1;
pthread_mutex_t count_lock;
void* increase_count(void *arg)
{
while (1) {
sleep(1);
pthread_mutex_lock(&count_lock);
count += 10;
printf("In new thread: count:%d\n", count);
pthread_mutex_unlock(&count_lock);
}
}
int main(int argc, char* argv[])
{
int ret = 0;
pthread_t new_thread;
pthread_mutex_init(&count_lock, NULL);
ret = pthread_create(&new_thread, NULL, increase_count, NULL);
if (ret != 0) {
printf("Error:%s:%d:ret:%d\n", __FILE__, __LINE__, ret);
return -1;
}
while(1) {
sleep(1);
pthread_mutex_lock(&count_lock);
count++;
printf("In main thread: count:%d\n", count);
pthread_mutex_unlock(&count_lock);
}
return 0;
}
在Linux上編譯
gcc pthread_test.c -o pthread_test -lpthread
運行結果如下:
$ ./pthread_test
In main thread: count:2
In new thread: count:12
In main thread: count:13
In new thread: count:23
In main thread: count:24
In new thread: count:34
In main thread: count:35
In new thread: count:45
In main thread: count:46
In new thread: count:56
In main thread: count:57
In new thread: count:67
In main thread: count:68
In new thread: count:78
In main thread: count:79
In new thread: count:89
In main thread: count:90
In new thread: count:100
5.2、移植到AliOS Things上
把上面的pthread_test.c 移植到AliOS Things上,改寫application/example/helloworld_demo 這個demo應用。 把pthread_test.c 的內容替換application/example/helloworld_demo/appdemo.c的全部內容,並做如下2個簡單的修改。
$ diff -ru ~/app/pthread_test.c ~/project/gitee/AliOS-Things/AliOS-Things/application/example/helloworld_demo/appdemo.c
--- ~/app/pthread_test.c 2020-12-10 23:02:56.915084914 +0800
+++ ~/project/gitee/AliOS-Things/AliOS-Things/application/example/helloworld_demo/appdemo.c 2020-12-10 23:06:10.616376591 +0800
@@ -1,6 +1,6 @@
#include <stdio.h>
#include <unistd.h>
-#include <pthread.h>
+#include <posix/pthread.h>
static volatile int count = 1;
pthread_mutex_t count_lock;
@@ -17,7 +17,7 @@
}
-int main(int argc, char* argv[])
+int application_start(int argc, char* argv[])
{
int ret = 0;
pthread_t new_thread;
由上可知,AliOS Things的應用入口是application_start, 不是main。 AliOS Things上的posix頭文件在posix目錄下,如posix/pthread.h。
編譯helloworld_demo
aos make helloworld_demo@haas100 -c config
aos make
下載燒錄並啓動,log如下, 與Linux運行的結果一致。
Welcome to AliOS Things
1592/main_task | sys_init aos_components_init done
1592/main_task | mesh has been opened
1986/mcu_audio | mcu_audio_main exit
[Jan 01 00:00:01.491]<I>ULOG-test sys_init aos_components_init done
In main thread: count:2
In new thread: count:12
In main thread: count:13
In new thread: count:23
In main thread: count:24
In new thread: count:34
In main thread: count:35
In new thread: count:45
In main thread: count:46
In new thread: count:56
In main thread: count:57
In new thread: count:67
In main thread: count:68
In new thread: count:78
In main thread: count:79
In new thread: count:89
In main thread: count:90
In new thread: count:100
移植Linux下應用到AliOS Things之下是不是很簡單,一起來試試吧。
6、參考鏈接
7、開發者技術支持
如需更多技術支持,可加入釘釘開發者羣
更多技術與解決方案介紹,請訪問阿里雲AIoT首頁https://iot.aliyun.com/