嵌入式八股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.mzph.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. 图像分割流程 我们将按照以下步骤完成图像分割和边缘检…