1. 程式人生 > >從零開始的鐳射通訊(第1章 協議棧)——2、環形陣列

從零開始的鐳射通訊(第1章 協議棧)——2、環形陣列

從零開始的鐳射通訊(第1章 協議棧)——2、環形陣列

github

https://github.com/HaHaHaHaHaGe/mynetstack

簡介

在說明環形陣列之前,先來看一看通常大家使用記憶體的方式
在這裡插入圖片描述
這是一段記憶體空間,大小16位元組,起始地址0x80000000,截止地址0x8000000F

在這段記憶體中若想為其記憶體空間進行賦值操作,C語言下應當:

*(unsigned char*)(0x80000000) = 0x66;

很明顯這條指令將0x66 賦值到了 0x80000000這個記憶體空間中

同樣你也可以:

unsigned
char i = 16 while(i--) *(unsigned char*)(0x80000000 + i) = 0x66;

這樣你將把整個記憶體空間從 0x80000000 ~ 0x8000000F 全部賦值為0x66,當然在現實應用中會賦值更有意義的資料,並非0x66

同樣你也可以讀取陣列內的資料

unsigned char recv;
recv = *(unsigned char*)(0x80000000);

或者連續讀取記憶體中的資料

unsigned char recv[16];
unsigned char i = 16
while(i--)
	recv = *(unsigned char*)(0x80000000 + i);

啊~一切是那麼的美好,我可以隨意的儲存、寫入我希望的資料

好,接下來咱們在做一些假設:首先寫入的人不再是你,而是其他人(程式)
那麼這樣一來問題就變得有些複雜了,
你並不能知道,對方什麼時間會向裡面寫入資料

所以我需要有一個標誌來代表是否有人向記憶體中寫入了資料

然後輪詢的檢測這個標誌,若此標誌被置位了,則代表裡面有新的資料,需要讀取資料了

好的,問題解決了

但是這只是理想情況下,但在現實應用中,很可能當標誌被置位時,我沒有能力一口氣全部讀取新資料,這時候又該怎麼辦呢?

我們可以在增加一個標誌,這個標誌表示我讀到的位置,而之前的寫標誌也更改為寫到的位置,這樣一來,就會變成下面的樣子:
在這裡插入圖片描述
當寫入方寫入資料時只需要挪動寫標誌,讀取方只是要挪動讀標誌

而且,我也只需要檢測讀寫標誌的距離就可知道當前是否有新的資料了

一切看似近乎完美,但是卻有一個致命的問題:
在這裡插入圖片描述
當寫位置達到末尾時,這時候該怎麼辦呢?

如果置之不理,那麼很明顯下一次寫入的位置將是未知區域!,這是很危險的,一個野指標的存在比空指標更加的危險。

這時候就到環形陣列發揮功效的時候了
在這裡插入圖片描述
環形陣列,顧名思義,就像他的名字一樣,結構是個環形,當讀\寫指標超過0x8000000F時也不會造成嚴重事故,因為他被重定向到了0x80000000

這便是環形陣列了,當然記憶體自己本身不可能從硬體修改成環形結構,環形的結構是在軟體層面上定義的

函式說明

有關環形陣列的檔案在\mynetstack\Source\BasicDataStream\inc 與\mynetstack\Source\BasicDataStream\src 中
名稱為 ringbuffer.c 與 ringbuffer.h

注:程式還在更新,以GITHUB為準

/*
寫資料到緩衝區函式
入口引數:
ptr:操作物件
data:寫入的資料指標
datalen:寫入的資料長度
*/
void write_buffer_data(ringbuffer *ptr, u8* data, u32 datalen);


/*
釋放緩衝區函式
注:如果在建立時指定了已經開闢好的記憶體,
那麼該函式只會清理此模組內部的變數,
不會更改外部的資料
入口引數:
ptr:操作物件
*/
u8 deinitial_buffer(ringbuffer *ptr);




/*
建立緩衝區函式
注:可在建立時指定已經開闢好的記憶體,
入口引數:
ptr:操作物件
self_mem: 是否由本函式自行分配空間,如果不需要,
應輸入NO並在操作物件的start ptr中分配好相應的空間
size: 需要開闢空間的大小
*/
u8 initial_buffer(ringbuffer *ptr, u8 self_mem, u32 size);


/*
外部寫入資料後使用此函式更新內部引數
入口引數:
ptr:操作物件
datalen:外部寫入的資料長度
*/
void write_buffer_len(ringbuffer *ptr, u32 datalen);


/*
獲取全部未讀資料
入口引數:
ptr:操作物件
len: 讀出資料的長度(位元組)
preview:若為YES則此次讀取不會修改ringbuff內指標狀態
*/
u8* get_unread_data(ringbuffer *ptr, u32 *len,u8 preview);





/*
獲取全部未讀資料的指標(分兩部分)
入口引數:
ptr:操作物件
ptr_1: 儲存第一個指標
ptr_2: 儲存第二個指標
len_1: 第一個指標內容的大小(位元組)
len_2: 第二個指標內容的大小(位元組)
preview:若為YES則此次讀取不會修改ringbuff內指標狀態
*/
void get_unread_ptr(ringbuffer *ptr, u8** ptr_1, u8** ptr_2, u32* len_1, u32* len_2, u8 preview);








/*
手動更新read指標
注:一般在get_unread_ptr後根據自身情況使用
入口引數:
ptr:操作物件
*/
void update_readlocation(ringbuffer *ptr);