嵌入式八股RTOS與Linux---網絡系統篇

前言

??關于計網的什么TCP三次握手 幾層模型啊TCP報文啥的不在這里講,會單獨分成一個計算機網絡模塊
??這里主要介紹介紹lwip和socket

FreeRTOS下的網絡接口–移植LWIP

?? 實際上FreeRTOS并不自帶網絡接口,我們一般會通過移植lwip協議棧讓FreeRTOS可以通過網絡接口收發數據,具體可看博客:
一文帶你掌握LWIP

  1. LWIP是什么
    ??LWIP是一個在嵌入式領域應用的TCP/IP協議棧,除了TCP/IP外還能支持DNS,DHCP等應用。LWIP只需要十幾KB的RAM和幾十KB的ROM就能使用了
  2. 如何在RTOS移植LWIP
    ?? 移植lwip前 結合著OSI模型先來說說LWIP幫我們做了哪些工作
    ?? 當我們的應用想要發起數據傳輸的時候,LWIP幫我們完成了TCP報文封裝(傳輸層)–>IP報文封裝(網絡層)–>IP地址找到MAC地址以及對應封裝(APR協議–數據鏈路層) 我們需要做的就是把這個層層封裝好的報文(p_buf鏈表)通過我們實現的網絡驅動接口發送出去
    在這里插入圖片描述
  • step1 :編寫 sys_arch.c文件
    ??首先我們的lwip在OS下至少需要三種東西:消息郵箱/信號量/線程創建
    ?? ??可是問題是,如果我用FreeRTOS,這三東西是這些API,我用UCOSIII又是一套API,這可怎么辦呢? 那lwip就把這些所有需要的操作抽象出來,然后根據不同的RTOS環境填空就好,這就是sys_arch.c做的工作,我們要去自己寫sys_arch的API
err_t
sys_mutex_new(sys_mutex_t *mutex)
{LWIP_ASSERT("mutex != NULL", mutex != NULL);mutex->mut = xSemaphoreCreateRecursiveMutex();if(mutex->mut == NULL) {SYS_STATS_INC(mutex.err);return ERR_MEM;}SYS_STATS_INC_USED(mutex);return ERR_OK;
}
void
sys_mutex_lock(sys_mutex_t *mutex)
{BaseType_t ret;LWIP_ASSERT("mutex != NULL", mutex != NULL);LWIP_ASSERT("mutex->mut != NULL", mutex->mut != NULL);ret = xSemaphoreTakeRecursive(mutex->mut, portMAX_DELAY);LWIP_ASSERT("failed to take the mutex", ret == pdTRUE);
}
err_t
sys_sem_new(sys_sem_t *sem, u8_t initial_count)
{LWIP_ASSERT("sem != NULL", sem != NULL);LWIP_ASSERT("initial_count invalid (not 0 or 1)",(initial_count == 0) || (initial_count == 1));sem->sem = xSemaphoreCreateBinary();if(sem->sem == NULL) {SYS_STATS_INC(sem.err);return ERR_MEM;}SYS_STATS_INC_USED(sem);if(initial_count == 1) {BaseType_t ret = xSemaphoreGive(sem->sem);LWIP_ASSERT("sys_sem_new: initial give failed", ret == pdTRUE);}return ERR_OK;
}
  • step2: 實現底層網卡驅動程序
    這個就得我們根據硬件自己編寫了
  • step3: 分配/設置/注冊一個netif結構體
    netif結構體是吧我們的網卡驅動程序和lwip鏈接起來的關鍵,netif結構體中包括數據的發送函數等
    struct netif {struct netif *next;		// 以鏈表形式方便管理ip_addr_t ip_addr;		// 本地ip地址ip_addr_t netmask;		// 子網掩碼ip_addr_t gw;				// 網關netif_output_fn output;  			// 供IP層封裝完成后調用 一般就用 etharp_output()netif_linkoutput_fn linkoutput;	// ethernet_output()結束封裝包后調用, 用于發送數據包netif_input_fn input;				// 用于向上層協議提交數據包// 以下是各種call_back沒用上 直接不展示了netif_status_callback_fn status_callback;.....u16_t mtu;							// 最大傳輸字節 mtu = 1500一般u8_t hwaddr[NETIF_MAX_HWADDR_LEN];	// mac地址u8_t hwaddr_len;						// mac地址長度u8_t flags;							// 網卡的狀態void * state;							// 私有數據 看自己怎么用
    };
    

??我們需要配置好這些參數的內容 然后通過netif_setup來使能這個網卡
?? ??為什么會有多個netif?–IP協議會根據ip_route函數去找到最合適的netif把數據發送出去,不過一般來說只有一個網卡啦
具體如何初始化這個網卡的,可以看我上面提到的博客

  • step4: 初始化LWIP的核心線程
    tcpip_init()函數
  • step5: 配置lwip協議棧 lwip的參數(lwipopts.h )
  1. LWIP數據接收/發送過程?
    在這里插入圖片描述

接收過程: 底層網卡通過DMA/中斷收到數據–>把數據轉成p_buf結構體–>調用netif->input提交給上層協議棧–>LWIP的核心線程會來處理這個數據的
發送過程: 應用層發起操作–>TCP協議封包–>IP協議封包并找到最合適的netif結構體–>ARP協議封包–>底層網卡驅動把數據發送輸出
4. LWIP參數配置?–lwipopts.h
根據自己的實際需求去配置了
比如是否啟用哪些協議 / 堆棧內存的大小 / 是否需要硬件校驗
5. LWIP的幾種API
LWIP有RAW API / NETCOON API / SOCKET API三種
在這里插入圖片描述

  1. LWIP的內存管理?
    ??LWIP提供了兩種內存管理方式: 堆內存管理和內存池內存管理 這倆中內存管理方式是可以共存的,也可以強行只用一種—(忽略標準庫的malloc和free)
    內存池的使用范圍:固定大小的場景,比如TCP/IP的首部用內存池就更快
    在這里插入圖片描述

    • 內存池的定義:實際上就是一個大數組–通過DECRLAR宏定義
      在這里插入圖片描述

    堆內存管理的使用: 靈活的大小,比如我們的數據包大小就是不確定的 通過堆內存管理算法分配–

    • 內存堆的定義:實際上也是一個大數組–通過DECRLAR宏定義
      在這里插入圖片描述

    • 如何兩者都啟用(默認就是)?或者只啟用一種
      在這里插入圖片描述

Linux下的網絡接口–Socket

  1. 請說一下socket網絡編程中客戶端和服務端用到哪些函數?
    • TCP服務器(Server)
      1. 使用函數socket()創建一個socket
      int socket(int domain, int type, int protocol);
      
      1. 設置端口復用(可選):允許多個進程或線程共享同一端口號進行通信的技術 — 提高服務器并發能力,防止端口資源耗盡
      2. 使用函數bind()綁定IP地址,端口等信息到socket上,設置全通規則
          struct sockaddr_in serv_addr;serv_addr.sin_family = AF_INET;serv_addr.sin_port = htons(8080);serv_addr.sin_addr.s_addr = INADDR_ANY;bind(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); // 綁定IP地址和端口號
      
      1. 使用函數listen()設置監聽,使用函數accept()接收客戶端上來的連接
      int listen(int sockfd, int backlog);  //backlog等待隊列的長度
      
      1. 使用函數send()和recv(),或者read()和write()收發數據
      ssize_t send(int sockfd, const void *buf, size_t len, int flags);
      ssize_t recv(int sockfd, void *buf, size_t len, int flags);
      
      1. 關閉網絡連接
    • TCP客戶端(Client)
      1. 使用函數socket()創建一個socket
      2. 使用函數connect()連接服務器
      int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
      
      1. 使用函數send()和recv(),或者read()和write()收發數據
      2. 關閉網絡連接
        UDP是基于無連接的協議,發送數據時不需要先建立連接,而是直接把數據發送過去
    • UDP服務器(Server)
      1. 使用函數socket()創建一個socket
      2. 使用函數bind() 綁定IP地址、端口等信息到socket上
      3. 收發數據,用函數recvfrom(),sendto()
      ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags,struct sockaddr *src_addr, socklen_t *addrlen);
      ssize_t sendto(int sockfd, const void *buf, size_t len, int flags,const struct sockaddr *dest_addr, socklen_t addrlen);
      
      1. 關閉網絡連接close()
    • UDP客戶端(Client)
      1. 使用函數socket()創建一個socket
      2. 使用函數recvfrom(),sendto()收發數據
      3. 關閉網絡連接close()
  2. 網絡字節序是大小端?
  • 大端字節序(Big Endian):最高有效位存于最低內存地址處,最低有效位存于最高內存處;
  • 小端字節序(Little Endian):最高有效位存于最高內存地址,最低有效位存于最低內存處
    在這里插入圖片描述

網絡字節序時大端字節序
//將主機字節序轉換為網絡字節序
unit32_t htonl (unit32_t hostlong);
unit16_t htons (unit16_t hostshort);
//將網絡字節序轉換為主機字節序
unit32_t ntohl (unit32_t netlong);
unit16_t ntohs (unit16_t netshort);

  • 為什么在數據結構 struct sockaddr_in 中, sin_addr 和 sin_port 需要轉換為網絡字節順序,而sin_family 需不需要呢?
    sin_addr 和 sin_port 分別封裝在包的 IP 和 UDP 層。因此,它們必須要 是網絡字節順序。但是 sin_family 域只是被內核 (kernel) 使用來決定在數 據結構中包含什么類型的地址,所以它必須是本機字節順序。同時, sin_family 沒有發送到網絡上,它們可以是本機字節順序

3 Socket的阻塞和非阻塞模式

  • 阻塞模式
    調用 send()/recv() 時,若數據未就緒或緩沖區滿,線程會掛起,直到操作完成
  • 非阻塞模式
    調用 send()/recv() 立即返回,通過錯誤碼(如 EWOULDBLOCK)通知需重試
    需配合 ?I/O 多路復用?(如 select()/poll()/epoll)實現高效事件驅動
    • 非阻塞下的Socket
      在非阻塞模式下,connect() 會立即返回 EINPROGRESS(而不會等三次握手完成再返回),此時需通過 select/poll 監聽 Socket 的可寫事件,再通過 getsockopt(SO_ERROR) 檢查連接是否成功。關鍵點包括:嚴格錯誤檢查、超時控制、與非阻塞 IO 的協同處理

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/73521.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/73521.shtml
英文地址,請注明出處:http://en.pswp.cn/web/73521.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

推薦一款好看的 vue3 后臺模板

SoybeanAdmin 項目簡介 SoybeanAdmin 是一個基于最新前端技術棧的清新、優雅、高顏值且功能強大的后臺管理模板。它采用 Vue3, Vite5, TypeScript, Pinia, NaiveUI 和 UnoCSS 構建,為開發者提供了一個現代化、高效且易于擴展的后臺管理系統解決方案。 主要特點&am…

【django】1-1 django構建web程序的基礎知識

文章目錄 1 構建web應用的基礎知識1.1 互聯網相關的概念1.2 互聯網協議DNS(域名系統)IP協議(互聯網絡協議)TCP(傳輸控制協議)HTTP(超文本傳輸協議)SSL(安全套接字層)TLS(傳輸層安全) 1.3 URL 2 web程序2.1 web程序的本質2.2 web框架的設計模式1.2.1 經典的MVC設計模式1.2.2 Dja…

【智能體】從一個聊天工作流了解LangGraph

1. 前言 這篇文章將從如何搭建一個帶網絡搜索功能的聊天機器人工作流,帶你初步了解 LangGraph。 2. 前提條件 已搭建 Python 開發環境,使用 3.11 以上版本。 已熟悉 Python 基礎語法。可參考:【LLM】Python 基礎語法_llm python入門-CSDN博…

JAVA開發:實例成員與靜態成員

判斷Java中的實例成員與靜態成員 在Java中,可以通過以下幾種方式判斷一個成員是實例成員還是靜態成員: 1. 通過聲明方式判斷 靜態成員使用static關鍵字修飾,實例成員不使用: public class MyClass {// 實例成員int instanceVa…

Softmax 回歸 + 損失函數 + 圖片分類數據集

Softmax 回歸 softmax 回歸是機器學習另外一個非常經典且重要的模型,是一個分類問題。 下面先解釋一下分類和回歸的區別: 簡單來說,分類問題從回歸的單輸出變成了多輸出,輸出的個數等于類別的個數。 實際上,對于分…

MySQL-存儲過程

介紹 基本語法 創建 調用 查看 刪除 變量 系統變量 查看 設置 用戶定義變量 賦值 使用 局部變量 聲明 賦值 流程控制 參數 條件結構 IF case 循環結構 while repeat loop 游標 條件處理程序 介紹 舉個簡單的例子,我們先select某數據&…

使用 Go 和 Gin 實現高可用負載均衡代理服務器

前言 在現代分布式系統中,負載均衡是保障服務高可用性和性能的核心技術。本文將基于 Go 語言和 Gin 框架實現一個支持動態路由、健康檢查、會話保持等特性的企業級負載均衡代理服務器,并提供完整的壓力測試方案和優化建議。 通過本方案實現的負載均衡代理具備以下優勢: 單…

在 Linux(Ubuntu / CentOS 7)上快速搭建我的世界 MineCraft 服務器,并實現遠程聯機,詳細教程

Linux 部署 MineCraft 服務器 詳細教程(丐版,無需云服務器) 一、虛擬機 Ubuntu 部署二、下載 Minecraft 服務端三、安裝 JRE 21四、安裝 MCS manager 面板五、搭建服務器六、本地測試連接七、下載櫻花,實現內網穿透,邀…

批量取消 PDF 文檔中的所有超鏈接

在 PDF 文檔中我們可以插入各種各樣的文本也可以給文本設置字體,顏色等多種樣式,同時還可以給文字或者圖片添加上超鏈接,當我們點擊超鏈接之后,就會跳轉到對應的網頁。有時候這會對我們的閱讀或者使用形成一定的干擾,今…

Ubuntu xinference部署本地模型bge-large-zh-v1.5、bge-reranker-v2-m3

bge-large-zh-v1.5 下載模型到指定路徑: modelscope download --model BAAI/bge-large-zh-v1.5 --local_dir ./bge-large-zh-v1.5自定義 embedding 模型,custom-bge-large-zh-v1.5.json: {"model_name": "custom-bge-large…

Vue的實例

Every Vue application starts with a single Vue component instance as the application root. Any other Vue component created in the same application needs to be nested inside this root component. 每個 Vue 應用都以一個 Vue 組件實例作為應用的根開始。在同一個應…

Linux學習筆記(應用篇三)

基于I.MX6ULL-MINI開發板 LED學習GPIO應用編程輸入設備 開發板中所有的設備(對象)都會在/sys/devices 體現出來,是 sysfs 文件系統中最重要的目錄結構 /sys下的子目錄說明/sys/devices這是系統中所有設備存放的目錄,也就是系統中…

【圖論】網絡流算法入門

(決定狠狠加訓圖論了,從一直想學但沒啟動的網絡流算法開始。) 網絡流問題 ? 問題定義:在帶權有向圖 G ( V , E ) G(V, E) G(V,E) 中,每條邊 e ( u , v ) e(u, v) e(u,v) 有容量 c ( u , v ) c(u, v) c(u,v)&am…

遞歸、搜索與回溯第四講:floodfill算法

遞歸、搜索與回溯第四講:floodfill算法 1.Floodfill算法介紹2.圖像渲染3.島嶼數量4.島嶼的最大面積5.被圍繞的區域6.太平洋大西洋水流問題7.掃雷游戲8.衣櫥整理 1.Floodfill算法介紹 2.圖像渲染 3.島嶼數量 4.島嶼的最大面積 5.被圍繞的區域 6.太平洋大西洋水流問題…

【深度學習與實戰】2.3、線性回歸模型與梯度下降法先導案例--最小二乘法(向量形式求解)

為了求解損失函數 對 的導數,并利用最小二乘法向量形式求解 的值? 這是?線性回歸?的平方誤差損失函數,目標是最小化預測值 與真實值 之間的差距。 ?損失函數?: 考慮多個樣本的情況,損失函數為所有樣本的平方誤差之和&a…

氣象可視化衛星云圖的方式:方法與架構詳解

氣象衛星云圖是氣象預報和氣候研究的重要數據來源。通過可視化技術,我們可以將衛星云圖數據轉化為直觀的圖像或動畫,幫助用戶更好地理解氣象變化。本文將詳細介紹衛星云圖可視化的方法、架構和代碼實現。 一、衛星云圖可視化方法 1. 數據獲取與預處理 衛星云圖數據通常來源…

瀏覽器渲染原理與優化詳解

一、瀏覽器渲染基礎原理 瀏覽器渲染流程主要包括以下步驟(也稱為"關鍵渲染路徑"): 構建DOM樹:將HTML解析為DOM(文檔對象模型)樹構建CSSOM樹:將CSS解析為CSSOM(CSS對象模…

基于Spring Boot的成績管理系統后臺實現

下面是一個完整的成績管理系統后臺實現,使用Spring Boot框架,包含學生管理、課程管理和成績管理功能。 1. 項目結構 src/main/java/com/example/grademanagement/ ├── config/ # 配置類 ├── controller/ # 控制器 ├── dto/ …

實現極限網關(INFINI Gateway)配置動態加載

還在停機更新 Gateway 配置,OUT 了。 今天和大家分享一個 Gateway 的功能:動態加載配置(也稱熱更新或熱加載)。 這個功能可以在 Gateway 不停機的情況下更新配置并使之生效。 配置樣例如下: path.data: data path.…

Mean Shift 圖像分割與 Canny 邊緣檢測教程

1. Mean Shift 簡介 Mean Shift 是一種聚類算法,通過尋找圖像中顏色相似的區域來實現分割。它非常適合用于場景分割或物體檢測等任務。本教程將它與 Canny 邊緣檢測結合,突出分割區域的邊界。 2. 圖像分割流程 我們將按照以下步驟完成圖像分割和邊緣檢…