當前位置:首頁 > 嵌入式 > 嵌入式軟件
[導讀] 我們知道,在ANSIC中可以用malloc()和free()兩個函數(shù)動態(tài)地分配內(nèi)存和釋放內(nèi)存。但是,在嵌入式實時操作系統(tǒng)中,多次這樣做會把原來很大的一塊連續(xù)內(nèi)存區(qū)域,逐漸地分割成

 我們知道,在ANSIC中可以用malloc()和free()兩個函數(shù)動態(tài)地分配內(nèi)存和釋放內(nèi)存。但

是,在嵌入式實時操作系統(tǒng)中,多次這樣做會把原來很大的一塊連續(xù)內(nèi)存區(qū)域,逐漸地分割成

許多非常小而且彼此又不相鄰的內(nèi)存區(qū)域,也就是內(nèi)存碎片。由于這些碎片的大量存在,使得

程序到后來連非常小的內(nèi)存也分配不到。在4.02節(jié)的任務堆棧中,我們講到過用malloc()函

數(shù)來分配堆棧時,曾經(jīng)討論過內(nèi)存碎片的問題。另外,由于內(nèi)存管理算法的原因,malloc()和

free()函數(shù)執(zhí)行時間是不確定的。

在μC/OS-II中,操作系統(tǒng)把連續(xù)的大塊內(nèi)存按分區(qū)來管理。每個分區(qū)中包含有整數(shù)個大小相同

的內(nèi)存塊,如同圖F7.1。利用這種機制,μC/OS-II對malloc()和free()函數(shù)進行了改進,使

得它們可以分配和釋放固定大小的內(nèi)存塊。這樣一來,malloc()和free()函數(shù)的執(zhí)行時間也是

固定的了。

如圖F7.2,在一個系統(tǒng)中可以有多個內(nèi)存分區(qū)。這樣,用戶的應用程序就可以從不同的內(nèi)存

分區(qū)中得到不同大小的內(nèi)存塊。但是,特定的內(nèi)存塊在釋放時必須重新放回它以前所屬于的內(nèi)

存分區(qū)。顯然,采用這樣的內(nèi)存管理算法,上面的內(nèi)存碎片問題就得到了解決。

圖F7.1內(nèi)存分區(qū)——Figure7.1

圖F7.2多個內(nèi)存分區(qū)——Figure7.2

內(nèi)存控制塊

為了便于內(nèi)存的管理,在μC/OS-II中使用內(nèi)存控制塊(memorycONtrolblocks)的數(shù)據(jù)結構來跟蹤每一個內(nèi)存分區(qū),系統(tǒng)中的每個內(nèi)存分區(qū)都有它自己的內(nèi)存控制塊。程序清單L7.1是內(nèi)存控制塊的定義。

程序清單L7.1內(nèi)存控制塊的數(shù)據(jù)結構

typedefSTruct{

void*OSMemAddr;

void*OSMemFreeList;

INT32UOSMemBlkSize;

INT32UOSMemNBlks;

INT32UOSMemNFree;

}OS_MEM;

.OSMemAddr是指向內(nèi)存分區(qū)起始地址的指針。它在建立內(nèi)存分區(qū)[見7.1節(jié),建立一個內(nèi)存分區(qū),OSMemCreate()]時被初始化,在此之后就不能更改了。

.OSMemFreeList是指向下一個空閑內(nèi)存控制塊或者下一個空閑的內(nèi)存塊的指針,具體含義要根據(jù)該內(nèi)存分區(qū)是否已經(jīng)建立來決定[見7.1節(jié)]。

.OSMemBlkSize 是內(nèi)存分區(qū)中內(nèi)存塊的大小,是用戶建立該內(nèi)存分區(qū)時指定的[見7.1節(jié)]。

.OSMemNBlks是內(nèi)存分區(qū)中總的內(nèi)存塊數(shù)量,也是用戶建立該內(nèi)存分區(qū)時指定的[見7.1節(jié)]。

.OSMemNFree是內(nèi)存分區(qū)中當前可以得空閑內(nèi)存塊數(shù)量。

如果要在μC/OS-II中使用內(nèi)存管理,需要在OS_CFG.H文件中將開關量OS_MEM_EN設置為1。這樣μC/OS-II在啟動時就會對內(nèi)存管理器進行初始化[由OSInit()調(diào)用OSMemInit()實現(xiàn)]。該初始化主要建立一個圖F7.3所示的內(nèi)存控制塊鏈表,其中的常數(shù)OS_MAX_MEM_PART(見文件OS_CFG.H)定義了最大的內(nèi)存分區(qū)數(shù),該常數(shù)值至少應為2。

圖F7.3空閑內(nèi)存控制塊鏈表——Figure7.3

建立一個內(nèi)存分區(qū),OSMemCreate()在使用一個內(nèi)存分區(qū)之前,必須先建立該內(nèi)存分區(qū)。這個操作可以通過調(diào)用OSMemCreate()函數(shù)來完成。程序清單L7.2說明了如何建立一個含有100個內(nèi)存塊、每個內(nèi)存塊32字節(jié)的內(nèi)存分區(qū)。

程序清單L7.2建立一個內(nèi)存分區(qū)

OS_MEM*CommTxBuf;

INT8UCommTxPart[100][32];

voidmain(void)

{

INT8Uerr;

OSInit();

.

.

CommTxBuf=OSMemCreate(CommTxPart,100,32,&err);

.

.

OSStart();

}

程序清單L7.3是OSMemCreate()函數(shù)的源代碼。該函數(shù)共有4個參數(shù):內(nèi)存分區(qū)的起始地址、分區(qū)內(nèi)的內(nèi)存塊總塊數(shù)、每個內(nèi)存塊的字節(jié)數(shù)和一個指向錯誤信息代碼的指針。如果OSMemCreate()操作失敗,它將返回一個NULL指針。否則,它將返回一個指向內(nèi)存控制塊的指針。對內(nèi)存管理的其它操作,象OSMemGet(),OSMemPut(),OSMemQuery()函數(shù)等,都要通過該指針進行。

每個內(nèi)存分區(qū)必須含有至少兩個內(nèi)存塊[L7.3(1)],每個內(nèi)存塊至少為一個指針的大小,因為同一分區(qū)中的所有空閑內(nèi)存塊是由指針串聯(lián)起來的[L7.3(2)]。接著,OSMemCreate()從系統(tǒng)中的空閑內(nèi)存控制塊中取得一個內(nèi)存控制塊[L7.3(3)],該內(nèi)存控制塊包含相應內(nèi)存分區(qū)的運行信息。OSMemCreate()必須在有空閑內(nèi)存控制塊可用的情況下才能建立一個內(nèi)存分區(qū)[L7.3(4)]。在上述條件均得到滿足時,所要建立的內(nèi)存分區(qū)內(nèi)的所有內(nèi)存塊被鏈接成一個單向的鏈表[L7.3(5)]。然后,在對應的內(nèi)存控制塊中填寫相應的信息[L7.3(6)]。完成上述各動作后,OSMemCreate()返回指向該內(nèi)存塊的指針。該指針在以后對內(nèi)存塊的操作中使用[L7.3(6)]。

程序清單L7.3OSMemCreate()

OS_MEM*OSMemCreate(void*addr,INT32Unblks,INT32Ublksize,INT8U*err)

{

OS_MEM*pmem;

INT8U*pblk;

void**plink;

INT32Ui;

if(nblks<2){(1)

*err=OS_MEM_INVALID_BLKS;

return((OS_MEM*)0);

}

if(blksize

*err=OS_MEM_INVALID_SIZE;

return((OS_MEM*)0);

}

OS_ENTER_CRITICAL();

pmem=OSMemFreeList;(3)

if(OSMemFreeList!=(OS_MEM*)0){

OSMemFreeList=(OS_MEM*)OSMemFreeList->OSMemFreeList;

}

OS_EXIT_CRITICAL();

if(pmem==(OS_MEM*)0){(4)

*err=OS_MEM_INVALID_PART;

return((OS_MEM*)0);

}

plink=(void**)addr;(5)

pblk=(INT8U*)addr+blksize;[!--empirenews.page--]

for(i=0;i<(nblks-1);i++){

*plink=(void*)pblk;

plink=(void**)pblk;

pblk=pblk+blksize;

}

*plink=(void*)0;

OS_ENTER_CRITICAL();

pmem->OSMemAddr=addr;(6)

pmem->OSMemFreeList=addr;

pmem->OSMemNFree=nblks;

pmem->OSMemNBlks=nblks;

pmem->OSMemBlkSize=blksize;

OS_EXIT_CRITICAL();

*err=OS_NO_ERR;

return(pmem);(7)

}

圖F7.4是OSMemCreate()函數(shù)完成后,內(nèi)存控制塊及對應的內(nèi)存分區(qū)和分區(qū)內(nèi)的內(nèi)存塊之間的關系。在程序運行期間,經(jīng)過多次的內(nèi)存分配和釋放后,同一分區(qū)內(nèi)的各內(nèi)存塊之間的鏈接順序會發(fā)生很大的變化。

分配一個內(nèi)存塊,OSMemGet()應用程序可以調(diào)用OSMemGet()函數(shù)從已經(jīng)建立的內(nèi)存分區(qū)中申請一個內(nèi)存塊。該函數(shù)的唯一參數(shù)是指向特定內(nèi)存分區(qū)的指針,該指針在建立內(nèi)存分區(qū)時,由OSMemCreate()函數(shù)返回。顯然,應用程序必須知道內(nèi)存塊的大小,并且在使用時不能超過該容量。例如,如果一個內(nèi)存分區(qū)內(nèi)的內(nèi)存塊為32字節(jié),那么,應用程序最多只能使用該內(nèi)存塊中的32字節(jié)。當應用程序不再使用這個內(nèi)存塊后,必須及時把它釋放,重新放入相應的內(nèi)存分區(qū)中[見7.03節(jié),釋放一個內(nèi)存塊,OSMemPut()]。

圖F7.4OSMemCreate()——Figure7.4

程序清單L7.4是OSMemGet()函數(shù)的源代碼。參數(shù)中的指針pmem指向用戶希望從其中分配內(nèi)存塊的內(nèi)存分區(qū)[L7.4(1)]。OSMemGet()首先檢查內(nèi)存分區(qū)中是否有空閑的內(nèi)存塊[L7.4(2)]。

如果有,從空閑內(nèi)存塊鏈表中刪除第一個內(nèi)存塊[L7.4(3)],并對空閑內(nèi)存塊鏈表作相應的修改[L7.4(4)]。這包括將鏈表頭指針后移一個元素和空閑內(nèi)存塊數(shù)減1[L7.4(5)]。最后,返回指向被分配內(nèi)存塊的指針[L7.4(6)]。

程序清單L7.4OSMemGet()

void*OSMemGet(OS_MEM*pmem,INT8U*err)(1)

{

void*pblk;

OS_ENTER_CRITICAL();

if(pmem->OSMemNFree>0){(2)

pblk=pmem->OSMemFreeList;(3)

pmem->OSMemFreeList=*(void**)pblk;(4)

pmem->OSMemNFree--;(5)

OS_EXIT_CRITICAL();

*err=OS_NO_ERR;

return(pblk);(6)

}else{

OS_EXIT_CRITICAL();

*err=OS_MEM_NO_FREE_BLKS;

return((void*)0);

}

}

值得注意的是,用戶可以在中斷服務子程序中調(diào)用OSMemGet(),因為在暫時沒有內(nèi)存塊可用的情況下,OSMemGet()不會等待,而是馬上返回NULL指針。

釋放一個內(nèi)存塊,OSMemPut()

當用戶應用程序不再使用一個內(nèi)存塊時,必須及時地把它釋放并放回到相應的內(nèi)存分區(qū)中。這個操作由OSMemPut()函數(shù)完成。必須注意的是,OSMemPut()并不知道一個內(nèi)存塊是屬于哪個內(nèi)存分區(qū)的。例如,用戶任務從一個包含32字節(jié)內(nèi)存塊的分區(qū)中分配了一個內(nèi)存塊,用完后,把它返還給了一個包含120字節(jié)內(nèi)存塊的內(nèi)存分區(qū)。當用戶應用程序下一次申請120字節(jié)分區(qū)中的一個內(nèi)存塊時,它會只得到32字節(jié)的可用空間,其它88字節(jié)屬于其它的任務,這就有可能使系統(tǒng)崩潰。

程序清單L7.5是OSMemPut()函數(shù)的源代碼。它的第一個參數(shù)pmem是指向內(nèi)存控制塊的指針,也即內(nèi)存塊屬于的內(nèi)存分區(qū)[L7.5(1)]。OSMemPut()首先檢查內(nèi)存分區(qū)是否已滿[L7.5(2)]。如果已滿,說明系統(tǒng)在分配和釋放內(nèi)存時出現(xiàn)了錯誤。如果未滿,要釋放的內(nèi)存塊被插入到該分區(qū)的空閑內(nèi)存塊鏈表中[L7.5(3)]。最后,將分區(qū)中空閑內(nèi)存塊總數(shù)加1[L7.5(4)]。

程序清單L7.5OSMemPut()

INT8UOSMemPut(OS_MEM*pmem,void*pblk)(1)

{

OS_ENTER_CRITICAL();

if(pmem->OSMemNFree>=pmem->OSMemNBlks){(2)

OS_EXIT_CRITICAL();

return(OS_MEM_FULL);

}

*(void**)pblk=pmem->OSMemFreeList;(3)

pmem->OSMemFreeList=pblk;

pmem->OSMemNFree++;(4)

OS_EXIT_CRITICAL();

return(OS_NO_ERR);

}

查詢一個內(nèi)存分區(qū)的狀態(tài),OSMemQuery()

在μC/OS-II中,可以使用OSMemQuery()函數(shù)來查詢一個特定內(nèi)存分區(qū)的有關消息。通過該函數(shù)可以知道特定內(nèi)存分區(qū)中內(nèi)存塊的大小、可用內(nèi)存塊數(shù)和正在使用的內(nèi)存塊數(shù)等信息。所有這些信息都放在一個叫OS_MEM_DATA的數(shù)據(jù)結構中,如程序清單L7.6。

程序清單L7.6OS_MEM_DATA數(shù)據(jù)結構

typedefstruct{

void*OSAddr;/*指向內(nèi)存分區(qū)首地址的指針*/

void*OSFreeList;/*指向空閑內(nèi)存塊鏈表首地址的指針*/

INT32UOSBlkSize;/*每個內(nèi)存塊所含的字節(jié)數(shù)*/

INT32UOSNBlks;/*內(nèi)存分區(qū)總的內(nèi)存塊數(shù)*/

INT32UOSNFree;/*空閑內(nèi)存塊總數(shù)*/

INT32UOSNUsed;/*正在使用的內(nèi)存塊總數(shù)*/

}OS_MEM_DATA;

程序清單L7.7是OSMemQuery()函數(shù)的源代碼,它將指定內(nèi)存分區(qū)的信息復制到OS_MEM_DATA定義的變量的對應域中。在此之前,代碼首先禁止了外部中斷,防止復制過程中某些變量值被修改[L7.7(1)]。由于正在使用的內(nèi)存塊數(shù)是由 OS_MEM_DATA中的局部變量計算得到的,所以,可以放在(criticalsection中斷屏蔽)的外面。

程序清單L7.7OSMemQuery()

INT8UOSMemQuery(OS_MEM*pmem,OS_MEM_DATA*pdata)

{

OS_ENTER_CRITICAL();

pdata->OSAddr=pmem->OSMemAddr;(1)

pdata->OSFreeList=pmem->OSMemFreeList;

pdata->OSBlkSize=pmem->OSMemBlkSize;

pdata->OSNBlks=pmem->OSMemNBlks;

pdata->OSNFree=pmem->OSMemNFree;

OS_EXIT_CRITICAL();

pdata->OSNUsed=pdata->OSNBlks-pdata->OSNFree;(2)

return(OS_NO_ERR);

}

UsingMemoryPartitions[!--empirenews.page--]

圖F7.5是一個演示如何使用μC/OS-II中的動態(tài)分配內(nèi)存功能,以及利用它進行消息傳遞[見第6章]的例子。程序清單L7.8是這個例子中兩個任務的示意代碼,其中一些重要代碼的標號和圖F7.5中括號內(nèi)用數(shù)字標識的動作是相對應的。

第一個任務讀取并檢查模擬輸入量的值(如氣壓、溫度、電壓等),如果其超過了一定的閾值,就向第二個任務發(fā)送一個消息。該消息中含有時間信息、出錯的通道號和錯誤代碼等可以想象的任何可能的信息。

錯誤處理程序是該例子的中心。任何任務、中斷服務子程序都可以向該任務發(fā)送出錯消息。錯誤處理程序則負責在顯示設備上顯示出錯信息,在磁盤上登記出錯記錄,或者啟動另一個任務對錯誤進行糾正等。

圖F7.5使用動態(tài)內(nèi)存分配——Figure7.5

程序清單L7.8內(nèi)存分配的例子——掃描模擬量的輸入和報告出錯

AnalogInputTask()

{

for(;;){

for(所有的模擬量都有輸入){

讀入模擬量輸入值;(1)

if(模擬量超過閾值){

得到一個內(nèi)存塊;(2)

得到當前系統(tǒng)時間(以時鐘節(jié)拍為單位);(3)

將下列各項存入內(nèi)存塊:(4)

系統(tǒng)時間(時間戳);

超過閾值的通道號;

錯誤代碼;

錯誤等級;

等.

向錯誤隊列發(fā)送錯誤消息;(5)

(一個指向包含上述各項的內(nèi)存塊的指針)

}

}

延時任務,直到要再次對模擬量進行采樣時為止;

}

}

ErrorHandlerTask()

{

for(;;){

等待錯誤隊列的消息;(6)

(得到指向包含有關錯誤數(shù)據(jù)的內(nèi)存塊的指針)

讀入消息,并根據(jù)消息的內(nèi)容執(zhí)行相應的操作;(7)

將內(nèi)存塊放回到相應的內(nèi)存分區(qū)中;(8)

}

}

等待一個內(nèi)存塊

有時候,在內(nèi)存分區(qū)暫時沒有可用的空閑內(nèi)存塊的情況下,讓一個申請內(nèi)存塊的任務等待也是有用的。但是,μC/OS-II本身在內(nèi)存管理上并不支持這項功能。如果確實需要,則可以通過為特定內(nèi)存分區(qū)增加信號量的方法,實現(xiàn)這種功能(見6.05節(jié),信號量)。應用程序為了申請分配內(nèi)存塊,首先要得到一個相應的信號量,然后才能調(diào)用OSMemGet()函數(shù)。整個過程見程序清單L7.9。

程序代碼首先定義了程序中使用到的各個變量[L7.9(1)]。該例中,直接使用數(shù)字定義了各個變量的大小,實際應用中,建議將這些數(shù)字定義成常數(shù)。在系統(tǒng)復位時,μC/OS-II調(diào)用OSInit()進行系統(tǒng)初始化[L7.9(2)],然后用內(nèi)存分區(qū)中總的內(nèi)存塊數(shù)來初始化一個信號量[L7.9(3)],緊接著建立內(nèi)存分區(qū)[L7.9(4)]和相應的要訪問該分區(qū)的任務[L7.9(5)]。當然,到此為止,我們對如何增加其它的任務也已經(jīng)很清楚了。顯然,如果系統(tǒng)中只有一個任務使用動態(tài)內(nèi)存塊,就沒有必要使用信號量了。這種情況不需要保證內(nèi)存資源的互斥。事實上,除非我們要實現(xiàn)多任務共享內(nèi)存,否則連內(nèi)存分區(qū)都不需要。多任務執(zhí)行從OSStart()開始[L7.9(6)]。當一個任務運行時,只有在信號量有效時[L7.9(7)],才有可能得到內(nèi)存塊[L7.9(8)]。一旦信號量有效了,就可以申請內(nèi)存塊并使用它,而沒有必要對OSSemPend()返回

的錯誤代碼進行檢查。因為在這里,只有當一個內(nèi)存塊被其它任務釋放并放回到內(nèi)存分區(qū)后,

μC/OS-II才會返回到該任務去執(zhí)行。同理,對OSMemGet()返回的錯誤代碼也無需做進一步的檢查(一個任務能得以繼續(xù)執(zhí)行,則內(nèi)存分區(qū)中至少有一個內(nèi)存塊是可用的)。當一個任務不再使用某內(nèi)存塊時,只需簡單地將它釋放并返還到內(nèi)存分區(qū)[L7.9(9)],并發(fā)送該信號量[L7.9(10)]。

程序清單L7.9等待從一個內(nèi)存分區(qū)中分配內(nèi)存塊

OS_EVENT*SemaphorePtr;(1)

OS_MEM*PartitionPtr;

INT8UPartition[100][32];

OS_STKTaskStk[1000];

voidmain(void)

{

INT8Uerr;

OSInit();(2)

.

.

SemaphorePtr=OSSemCreate(100);(3)

PartitionPtr=OSMemCreate(Partition,100,32,&err);(4)

.

OSTaskCreate(Task,(void*)0,&TaskStk[999],&err);(5)

.

OSStart();(6)

}

voidTask(void*pdata)

{

INT8Uerr;

INT8U*pblock;

for(;;){

OSSemPend(SemaphorePtr,0,&err);(7)

pblock=OSMemGet(PartitionPtr,&err);(8)

.

./*使用內(nèi)存塊*/

.

OSMemPut(PartitionPtr,pblock);(9)

OSSemPost(SemaphorePtr);(10)

}

}

本站聲明: 本文章由作者或相關機構授權發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉型技術解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務中斷的風險,如企業(yè)系統(tǒng)復雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務連續(xù)性,提升韌性,成...

關鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關鍵字: 華為 12nm EDA 半導體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權最終是由生態(tài)的繁榮決定的。

關鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務引領增長 以科技創(chuàng)新為引領,提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術學會聯(lián)合牽頭組建的NVI技術創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術創(chuàng)新聯(lián)...

關鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關鍵字: BSP 信息技術
關閉
關閉