嵌入式八股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.rhkb.cn/news/41387.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练

全分辨率免ROOT懒人精灵-自动化编程思维-设计思路-实战训练 1.2025新版懒人精灵-实战红果搜索关键词刷视频:https://www.bilibili.com/video/BV1eK9kY7EWV 2.懒人精灵-全分辨率节点识别(红果看广告领金币小实战):https://www.bili…

【更新中】【React】基础版React + Redux实现教程(Vite + React + Redux + TypeScript)

本项目是一个在react中,使用 redux 管理状态的基础版实现教程,用简单的案例练习redux的使用,旨在帮助学习 redux 的状态管理机制,包括 store、action、reducer、dispatch 等核心概念。 项目地址:https://github.com/Yv…

【MySQL】从零开始:掌握MySQL数据库的核心概念(四)

人们之所以不愿改变,是因为害怕未知。但历史唯一不变的事实,就是一切都会改变。 前言 这是我自己学习mysql数据库的第四篇博客总结。后期我会继续把mysql数据库学习笔记开源至博客上。 上一期笔记是关于mysql数据库的表格约束,没看的同学可以…

AP 场景架构设计(一) :OceanBase 读写分离策略解析

说明:本文内容对应的是 OceanBase 社区版,架构部分不涉及企业版的仲裁副本功能。OceanBase社区版和企业版的能力区别详见: 官网链接。 概述​ 当两种类型的业务共同运行在同一个数据库集群上时,这对数据库的配置等条件提出了较高…

CPU架构和微架构

CPU架构(CPU Architecture) CPU架构是指处理器的整体设计框架,定义了处理器的指令集、寄存器、内存管理方式等。它是处理器设计的顶层规范,决定了软件如何与硬件交互。 主要特点: 指令集架构(ISA, Instr…

6.4 模拟专题:LeetCode1419.数青蛙

1.题目链接:数青蛙 - LeetCode 2.题目描述 给定一个字符串 croakOfFrogs,表示青蛙的鸣叫声序列。每个青蛙必须按顺序发出完整的 “croak” 字符,且多只青蛙可以同时鸣叫。要求计算最少需要多少只青蛙才能完成该字符串,若无法完成…

Linux 搭建dns主域解析,和反向解析

#!/bin/bash # DNS主域名服务 # user li 20250325# 检查当前用户是否为root用户 # 因为配置DNS服务通常需要较高的权限,只有root用户才能进行一些关键操作 if [ "$USER" ! "root" ]; then# 如果不是root用户,输出错误信息echo "…

Leetcode 二进制求和

java solution class Solution {public String addBinary(String a, String b) {StringBuilder result new StringBuilder();//首先设置2个指针, 从右往左处理int i a.length() - 1;int j b.length() - 1;int carry 0; //设置进位标志位//从2个字符串的末尾向前遍历while(…

【NLP 49、提示工程 prompt engineering】

目录 一、基本介绍 语言模型生成文本的基本特点 提示工程 prompt engineering 提示工程的优势 使用注意事项 ① 安全问题 ② 可信度问题 ③ 时效性与专业性 二、应用场景 能 ≠ 适合 应用场景 —— 百科知识 应用场景 —— 写文案 应用场景 —— 解释 / 编写…

【NLP 43、文本生成任务】

目录 一、生成式任务 二、seq2seq任务 1.模型结构 2.工作原理 3.局限性 三、自回归语言模型训练 Decoder only 四、自回归模型结构:文本生成任务 —— Embedding LSTM 代码示例 🚀 数据文件 代码流程 Ⅰ、模型初始化 Ⅱ、前向计算 代码运行流程 Ⅲ、加载…

vscode 通过Remote-ssh远程连接服务器报错 could not establish connection to ubuntu

vscode 通过Remote-ssh插件远程连接服务器报错 could not establish connection to ubuntu,并且出现下面的错误打印: [21:00:57.307] Log Level: 2 [21:00:57.350] SSH Resolver called for "ssh-remoteubuntu", attempt 1 [21:00:57.359] r…

Linux之编辑器vim命令

vi/vim命令: 终端下编辑文件的首选工具,号称编辑器之神 基本上分为三种模式,分别是 命令模式(command mode)>输入vi的命令和快捷键,默认打开文件的时候的模式插入模式(insert mode&#x…

第一天学爬虫

阅读提示:我今天才开始尝试爬虫,写的不好请见谅。 一、准备工具 requests库:发送HTTP请求并获取网页内容。BeautifulSoup库:解析HTML页面并提取数据。pandas库:保存抓取到的数据到CSV文件中。 二、爬取步骤 发送请求…

MySQL实战(尚硅谷)

要求 代码 # 准备数据 CREATE DATABASE IF NOT EXISTS company;USE company;CREATE TABLE IF NOT EXISTS employees(employee_id INT PRIMARY KEY,first_name VARCHAR(50),last_name VARCHAR(50),department_id INT );DESC employees;CREATE TABLE IF NOT EXISTS departments…

windows下安装sublime

sublime4 alpha 4098 版本 下载 可以根据待破解的版本选择下载 https://www.sublimetext.com/dev crack alpha4098 的licence 在----- BEGIN LICENSE ----- TwitterInc 200 User License EA7E-890007 1D77F72E 390CDD93 4DCBA022 FAF60790 61AA12C0 A37081C5 D0316412 4584D…

激光线检测算法的FPGA实现

激光线检测算法的FPGA实现 1. 常见的激光线检测算法 激光线检测中常用的三种算法 MAX(最大值法)、THRESH(阈值法)、COG(灰度重心法) 分别具有以下特点和工作原理: 1.1 MAX(最大值法…

小样本微调大模型

一、环境搭建 conda create -n dseek python=3.10 conda activate dseek pip install bitsandbytes Pip install numpy python -m pip install --upgrade pip setuptools wheel 安装cuda,torch,Unsloth, huggingface,wandb等,见前述章节; 微调服务器配置:单机笔记本显卡4…

深入理解指针(2)(C语言版)

文章目录 前言一、数组名的理解二、使用指针访问数组三、一维数组传参的本质四、冒泡排序五、二级指针六、指针数组七、指针数组模拟二维数组总结 前言 在上一篇文章中,我们初步了解了指针的基本概念和用法。今天,我们将继续深入探索指针在数组、函数传…

高效内存管理:x86-64架构中的分页机制

在 x86-64 架构的世界里,内存分页机制扮演着举足轻重的角色,它就像是一座桥梁,连接着虚拟地址与物理地址。简单来说,内存分页机制就是将线性地址(也就是虚拟地址)切分成一个个固定大小的页,并把…

统一开放世界与开放词汇检测:YOLO-UniOW无需增量学习的高效通用开放世界目标检测框架

目录 一、摘要 二、引言 三、相关工作 开放词汇对象检测 开放世界目标检测 参数高效学习 四、高效通用的开放世界目标检测 问题定义 高效的自适应决策学习 开放世界通配符学习 五、Coovally AI模型训练与应用平台 六、实验 数据集 评价指标 实施细节 定量结果 …