0%

Linux高性能服务器编程

第一篇 TCP/IP协议族

第一章 TCP/IP协议族

TCP/IP协议族体系结构及主要协议

image-20241105093533877

数据链路层
1.数据链路层实现了网卡接口的网络驱动程序,以处理数据在物理媒介(比如以太网、令牌环等)上的传输。不同的物理网络具有不同的电气特性,网络驱动程序隐藏了这些细节,为上层协议提供一个统一的接口。
2.数据链路层两个常用的协议是ARP协议(Address Resolve Protocol,地址解析协议)和RARP协议(Reverse Address Resolve Protocol,逆地址解析协议)。它们实现了IP地址和机器物理地址(通常是MAC地址,以太网、令牌环和802.11无线网络都使用MAC地址)之间的相互转换。
3.网络层使用IP地址寻址一台机器,而数据链路层使用物理地址寻址一台机器,因此网络层必须先将目标机器的IP地址转化成其物理地址,才能使用数据链路层提供的服务,这就是ARP协议的用途。RARP协议仅用于网络上的某些无盘工作站。因为缺乏存储设备,无盘工作站无法记住自己的IP地址,但它们可以利用网卡上的物理地址来向网络管理者(服务器或网络管理软件)查询自身的IP地址。运行RARP服务的网络管理者通常存有该网络上所有机器的物理地址到IP地址的映射

网络层
1.网络层实现数据包的选路和转发。WAN(Wide Area Network,广域网)通常使用众多分级的路由器来连接分散的主机或LAN(Local Area Network,局域网),因此,通信的两台主机一般不是直接相连的,而是通过多个中间节点(路由器)连接的。网络层的任务就是选择这些中间节点,以确定两台主机之间的通信路径。同时,网络层对上层协议隐藏了网络拓扑连接的细节,使得在传输层和网络应用程序看来,通信的双方是直接相连的。
2.网络层最核心的协议是IP协议(Internet Protocol,因特网协议)。IP协议根据数据包的目的IP地址来决定如何投递它。如果数据包不能直接发送给目标主机,那么IP协议就为它寻找一个合适的下一跳(next hop)路由器,并将数据包交付给该路由器来转发。多次重复这一过程,数据包最终到达目标主机,或者由于发送失败而被丢弃。可见, IP协议使用逐跳(hop by hop)的方式确定通信路径。我们将在第2章详细讨论IP协议。
3.网络层另外一个重要的协议是ICMP协议(Internet Control Message Protocol,因特网控制报文协议)。它是IP协议的重要补充,主要用于检测网络连接。 8位类型字段用于区分报文类型。它将ICMP报文分为两大类:一类是差错报文,这类报文主要用来回应网络错误,比如目标不可到达(类型值为3)和重定向(类型值为5);另一类是查询报文,这类报文用来查询网络信息,比如ping程序就是使用ICMP报文查看目标是否可到达(类型值为8)的。有的ICMP报文还使用8位代码字段来进一步细分不同的条件。比如重定向报文使用代码值0表示对网络重定向,代码值1表示对主机重定向。ICMP报文使用16位校验和字段对整个报文(包括头部和内容部分)进行循环冗余校验(Cyclic Redundancy Check,CRC),以检验报文在传输过程中是否损坏。不同的ICMP报文类型具有不同的正文内容。我们将在第2章详细讨论主机重定向报文,其他ICMP报文格式请参考ICMP协议的标准文档RFC 792。
4.ICMP协议并非严格意义上的网络层协议,因为它使用处于同一层的IP协议提供的服务(一般来说,上层协议使用下层协议提供的服务)。

image-20241105100006449

传输层
1.ICMP协议并非严格意义上的网络层协议,因为它使用处于同一层的IP协议提供的服务(一般来说,上层协议使用下层协议提供的服务)。
2.垂直的实线箭头表示TCP/IP协议族各层之间的实体通信(数据包确实是沿着这些线路传递的),而水平的虚线箭头表示逻辑通信线路。该图中还附带描述了不同物理网络的连接方法。可见,数据链路层(驱动程序)封装了物理网络的电气细节;网络层封装了网络连接的细节;传输层则为应用程序封装了一条端到端的逻辑通信链路,它负责数据的收发、链路的超时重连等。传输层协议主要有三个:TCP协议、UDP协议和SCTP协议。
3.TCP协议(Transmission Control Protocol,传输控制协议)为应用层提供可靠的、面向连接的和基于流(stream)的服务。TCP协议使用超时重传、数据确认等方式来确保数据包被正确地发送至目的端,因此TCP服务是可靠的。使用TCP协议通信的双方必须先建立TCP连接,并在内核中为该连接维持一些必要的数据结构,比如连接的状态、读写缓冲区,以及诸多定时器等。当通信结束时,双方必须关闭连接以释放这些内核数据。TCP服务是基于流的。基于流的数据没有边界(长度)限制,它源源不断地从通信的一端流入另一端。发送端可以逐个字节地向数据流中写入数据,接收端也可以逐个字节地将它们读出。
4.UDP协议(User Datagram Protocol,用户数据报协议)则与TCP协议完全相反,它为应用层提供不可靠、无连接和基于数据报的服务。“不可靠”意味着UDP协议无法保证数据从发送端正确地传送到目的端。如果数据在中途丢失,或者目的端通过数据校验发现数据错误而将其丢弃,则UDP协议只是简单地通知应用程序发送失败。因此,使用UDP协议的应用程序通常要自己处理数据确认、超时重传等逻辑。UDP协议是无连接的,即通信双方不保持一个长久的联系,因此应用程序每次发送数据都要明确指定接收端的地址(IP地址等信息)。基于数据报的服务,是相对基于流的服务而言的。每个UDP数据报都有一个长度,接收端必须以该长度为最小单位将其所有内容一次性读出,否则数据将被截断。

image-20241105100540693

应用层
1.应用层负责处理应用程序的逻辑。数据链路层、网络层和传输层负责处理网络通信细节,这部分必须既稳定又高效,因此它们都在内核空间中实现,如图1-1所示。而应用层则在用户空间实现,因为它负责处理众多逻辑,比如文件传输、名称查询和网络管理等。如果应用层也在内核中实现,则会使内核变得非常庞大。当然,也有少数服务器程序是在内核中实现的,这样代码就无须在用户空间和内核空间来回切换(主要是数据的复制),极大地提高了工作效率。不过这种代码实现起来较复杂,不够灵活,且不便于移植。
2.ping是应用程序,而不是协议,前面说过它利用ICMP报文检测网络连接,是调试网络环境的必备工具。
3.telnet协议是一种远程登录协议,它使我们能在本地完成远程任务,本书后续章节将会多次使用telnet客户端登录到其他服务上。
4.OSPF(Open Shortest Path First,开放最短路径优先)协议是一种动态路由更新协议,用于路由器之间的通信,以告知对方各自的路由信息。
5.DNS(Domain Name Service,域名服务)协议提供机器域名到IP地址的转换,我们将在后面简要介绍DNS协议。
6.应用层协议(或程序)可能跳过传输层直接使用网络层提供的服务,比如ping程序和OSPF协议。应用层协议(或程序)通常既可以使用TCP服务,又可以使用UDP服务,比如DNS协议。我们可以通过/etc/services文件查看所有知名的应用层协议,以及它们都能使用哪些传输层服务。

封装

​ 1.上层协议是如何使用下层协议提供的服务的呢?其实这是通过封装(encapsulation)实现的。应用程序数据在发送到物理网络上之前,将沿着协议栈从上往下依次传递。每层协议都将在上层数据的基础上加上自己的头部信息(有时还包括尾部信息),以实现该层的功能,这个过程就称为封装.

image-20241105102104036

​ 2.经过TCP封装后的数据称为TCP报文段(TCP message segment),或者简称TCP段。前文提到,TCP协议为通信双方维持一个连接,并且在内核中存储相关数据。这部分数据中的TCP头部信息和TCP内核缓冲区(发送缓冲区或接收缓冲区)数据一起构成了TCP报文段,如图1-5中的虚线框所示。

image-20241105102116094

​ 3.当发送端应用程序使用send(或者write)函数向一个TCP连接写入数据时,内核中的TCP模块首先把这些数据复制到与该连接对应的TCP内核发送缓冲区中,然后TCP模块调用IP模块提供的服务,传递的参数包括TCP头部信息和TCP发送缓冲区中的数据,即TCP报文段。关于TCP报文段头部的细节,我们将在第3章讨论。
​ 4.经过UDP封装后的数据称为UDP数据报(UDP datagram)UDP对应用程序数据的封装与TCP类似。不同的是,UDP无须为应用层数据保存副本,因为它提供的服务是不可靠的。当一个UDP数据报被成功发送之后,UDP内核缓冲区中的该数据报就被丢弃了。如果应用程序检测到该数据报未能被接收端正确接收,并打算重发这个数据报,则应用程序需要重新从用户空间将该数据报拷贝到UDP内核发送缓冲区中。
​ 5.经过IP封装后的数据称为IP数据报(IP datagram)IP数据报也包括头部信息和数据部分,其中数据部分就是一个TCP报文段、UDP数据报或者ICMP报文。我们将在第2章详细讨论IP数据报的头部信息。
​ 6.经过数据链路层封装的数据称为帧(frame)。传输媒介不同,帧的类型也不同。比如,以太网上传输的是以太网帧(ethernet frame),而令牌环网络上传输的则是令牌环帧(token ring frame)。以以太网帧为例,其封装格式如图1-6所示。

image-20241105160508502

​ 7.以太网帧使用6字节的目的物理地址和6字节的源物理地址来表示通信的双方。关于类型(type)字段,我们将在后面讨论。4字节CRC字段对帧的其他部分提供循环冗余校验。帧的最大传输单元(Max Transmit Unit,MTU),即帧最多能携带多少上层协议数据(比如IP数据报),通常受到网络类型的限制。图1-6所示的以太网帧的MTU是1500字节。正因为如此,过长的IP数据报可能需要被分片(fragment)传输。帧才是最终在物理网络上传送的字节序列。至此,封装过程完成。

分用

1.当帧到达目的主机时,将沿着协议栈自底向上依次传递。各层协议依次处理帧中本层负责的头部数据,以获取所需的信息,并最终将处理后的帧交给目标应用程序。这个过程称为分用(demultiplexing)。分用是依靠头部信息中的类型字段实现的。标准文档RFC 1700定义了所有标识上层协议的类型字段以及每个上层协议对应的数值。图1-7显示了以太网帧的分用过程。

image-20241105160651493

​ 2.因为IP协议、ARP协议和RARP协议都使用帧传输数据,所以帧的头部需要提供某个字段(具体情况取决于帧的类型)来区分它们。以以太网帧为例,它使用2字节的类型字段来标识上层协议(见图1-6)。如果主机接收到的以太网帧类型字段的值为0x800,则帧的数据部分为IP数据报(见图1-4),以太网驱动程序就将帧交付给IP模块;若类型字段的值为0x806,则帧的数据部分为ARP请求或应答报文,以太网驱动程序就将帧交付给ARP模块;若类型字段的值为0x835,则帧的数据部分为RARP请求或应答报文,以太网驱动程序就将帧交付给RARP模块。
​ 3.同样,因为ICMP协议、TCP协议和UDP协议都使用IP协议,所以IP数据报的头部采用16位的协议(protocol)字段来区分它们。
​ 4.TCP报文段和UDP数据报则通过其头部中的16位的端口号(port number)字段来区分上层应用程序。比如DNS协议对应的端口号是53,HTTP协议(Hyper-Text Transfer Protocol,超文本传送协议)对应的端口号是80。所有知名应用层协议使用的端口号都可在/etc/services文件中找到。
​ 5.帧通过上述分用步骤后,最终将封装前的原始数据送至目标服务(图1-7中的ARP服务、RARP服务、ICMP服务或者应用程序)。这样,在顶层目标服务看来,封装和分用似乎没有发生过。

测试网络

image-20241105113238137

​ 1.该测试网络主要用于分析ARP协议、IP协议、ICMP协议、TCP协议和DNS协议。我们通过抓取该网络上的以太网帧,查看其中的以太网帧头部、IP数据报头部、TCP报文段头部信息,以获取网络通信的细节。这样,以理论结合实践,我们就清楚TCP/IP通信具体是如何进行的了。作者编写的多个客户端、服务器程序都是使用该网络来调试和测试的 。
​ 2.对于路由器,我们仅列出了其LAN网络IP地址(192.168.1.1),而忽略了ISP(Internet Service Provider,因特网服务提供商)给它分配的WAN网络IP地址

ARP协议工作原理
  • ARP 协议的 传输和广播 发生在数据链路层。
  • ARP 的调用和需求 来自网络层,因为 IP 地址到 MAC 地址的映射是网络层通信所需要的。
  • 虽然数据链路层并不直接“拥有” IP 地址,但通过操作系统的跨层共享机制,数据链路层的 ARP 模块可以访问本机的 IP 地址,从而实现 IP 地址到 MAC 地址的转换和匹配功能。这种跨层信息共享是为了 ARP 协议的运行需求设计的。

image-20241105155740681

ARP协议能实现任意网络层地址到任意物理地址的转换,不过本书仅讨论从IP地址到以太网地址(MAC地址)的转换。其工作原理是:主机向自己所在的网络广播一个ARP请求,该请求包含目标机器的网络地址。此网络上的其他机器都将收到这个请求,但只有被请求的目标机器会回应一个ARP应答,其中包含自己的物理地址 。

image-20241105154103140

在网络中数据包传输也是如此,源IP地址和和目标IP地址在传输过程中是不会变化的(前提没有使用NAT网络),只有源MAC地址和目标MAC一直在变化。
IP作用时主机之间通信用的,而MAC作用时实现直连的两个设备之间通信,而IP负责在没有直连的两个网络之间进行通信传输。

image-20241105154919133

image-20241105154733524

以太网ARP请求/应答报文详解

image-20241105153150509

❑硬件类型字段定义物理地址的类型,它的值为1表示MAC地址。❑协议类型字段表示要映射的协议地址类型,它的值为0x800,表示IP地址。
❑硬件地址长度字段和协议地址长度字段,顾名思义,其单位是字节。对MAC地址来说,其长度为6;对IP(v4)地址来说,其长度为4。
❑操作字段指出4种操作类型:ARP请求(值为1)、ARP应答(值为2)、RARP请求(值为3)和RARP应答(值为4)。
❑最后4个字段指定通信双方的以太网地址和IP地址。发送端填充除目的端以太网地址外的其他3个字段,以构建ARP请求并发送之。接收端发现该请求的目的端IP地址是自己,就把自己的以太网地址填进去,然后交换两个目的端地址和两个发送端地址,以构建ARP应答并返回之(当然,如前所述,操作字段需要设置为2)。

由图1-9可知,ARP请求/应答报文的长度为28字节。如果再加上以太网帧头部和尾部的18字节(见图1-6),则一个携带ARP请求/应答报文的以太网帧长度为46字节。不过有的实现要求以太网帧数据部分长度至少为46字节(见图1-4),此时ARP请求/应答报文将增加一些填充字节,以满足这个要求。在这种情况下,一个携带ARP请求/应答报文的以太网帧长度为64字节。

ARP高速缓存的查看和修改

image-20241105153907517

使用tcpdump观察ARP通信过程

​ 为了清楚地了解ARP的运作过程,我们从ernest-laptop上执行telnet命令登录Kongming20的echo服务(已经开启),并用tcpdump(详见第17章)抓取这个过程中两台测试机器之间交换的以太网帧。具体的操作过程如下:

image-20241105163441348

​ 在执行telnet命令之前,应先清除ARP缓存中与Kongming20对应的项,否则ARP通信不被执行,我们也就无法抓取到期望的以太网帧。当执行telnet命令并在两台通信主机之间建立TCP连接后(telnet输出“Connected to 192.168.1.109”),输入Ctrl+]以调出telnet程序的命令提示符,然后在telnet命令提示符后输入quit,退出telnet客户端程序(因为ARP通信在TCP连接建立之前就已经完成,故我们不关心后续内容)。tcpdump抓取到的众多数据包中,只有最靠前的两个和ARP通信有关系,现在将它们列出(数据包前面的编号是笔者加入的,后同):

image-20241105163502740

​ 由tcpdump抓取的数据包本质上是以太网帧,我们通过该命令的众多选项来控制帧的过滤(比如用dst和src指定通信的目的端IP地址和源端IP地址)和显示(比如用-e选项开启以太网帧头部信息的显示)。

​ 第一个数据包中,ARP通信的源端的物理地址是00:16:d3:5c:b9:e3(ernest-laptop),目的端的物理地址是ff:ff:ff:ff:ff:ff,这是以太网的广播地址,用以表示整个LAN。该LAN上的所有机器都会收到并处理这样的帧。数值0x806是以太网帧头部的类型字段的值,它表示分用的目标是ARP模块。该以太网帧的长度为42字节(实际上是46字节,tcpdump未统计以太网帧尾部4字节的CRC字段),其中数据部分长度为28字节。“Request”表示这是一个ARP请求,“who-has 192.168.1.109 tell 192.168.1.108”则表示是ernest-laptop要查询Kongming20的IP地址。

​ 第二个数据包中,ARP通信的源端的物理地址是08:00:27:53:10:67(Kongming20),目的端的物理地址是00:16:d3:5c:b9:e3(ernest-laptop)。“Reply”表示这是一个ARP应答,“192.168.1.109 is-at 08:00:27:53:10:67”则表示目标机器Kongming20报告其物理地址。该以太网帧的长度为60字节(实际上是64字节),可见它使用了填充字节来满足最小帧长度。

关于该图,需要说明三点:

  • 我们将两次传输的以太网帧按照图1-6所描述的以太网帧封装格式绘制在图的下半部分。
  • ARP请求和应答是从以太网驱动程序发出的,而并非像图中描述的那样从ARP模块直接发送到以太网上,所以我们将它们用虚线表示,这主要是为了体现携带ARP数据的以太网帧和其他以太网帧(比如携带IP数据报的以太网帧)的区别。
  • 路由器也将接收到以太网帧1,因为该帧是一个广播帧。不过很显然,路由器并没有回应其中的ARP请求,正如前文讨论的那样。
DNS工作原理

我们通常使用机器的域名来访问这台机器,而不直接使用其IP地址,比如访问因特网上的各种网站。那么如何将机器的域名转换成IP地址呢?这就需要使用域名查询服务。域名查询服务有很多种实现方式,比如NIS(Network Information Service,网络信息服务)、DNS和本地静态文件等。本节主要讨论DNS。

DNS是一套分布式的域名服务系统。每个DNS服务器上都存放着大量的机器名和IP地址的映射,并且是动态更新的。众多网络客户端程序都使用DNS协议来向DNS服务器查询目标主机的IP地址 。

linux下访问DNS服务

image-20241105165406313

host命令的输出告诉我们,机器名www.baidu.com是www.a.shifen.com.的别名,并且该机器名对应两个IP地址。host命令使用DNS协议和DNS服务器通信,其-t选项告诉DNS协议使用哪种查询类型。我们这里使用的是A类型,即通过机器的域名获得其IP地址(但实际上返回的资源记录中还包含机器的别名)。关于host命令的详细使用方法,请参考其man手册。

使用tcpdump观察DNS通信过程

​ 这两个数据包开始的“IP”指出,它们后面的内容描述的是IP数据报。tcpdump以“IP地址.端口号”的形式来描述通信的某一端;以“>”表示数据传输的方向,“>”前面是源端,后面是目的端。可见,第一个数据包是测试机器ernest-laptop(IP地址是192.168.1.108)向其首选DNS服务器(IP地址是219.239.26.42)发送的DNS查询报文(目标端口53是DNS服务使用的端口,这一点我们在前面介绍过),第二个数据包是服务器反馈的DNS应答报文。

​ 第一个数据包中,数值57428是DNS查询报文的标识值,因此该值也出现在DNS应答报文中。“+”表示启用递归查询标志。“A?”表示使用A类型的查询方式。“www.baidu.com”则是DNS查询问题中的查询名。括号中的数值31是DNS查询报文的长度(以字节为单位)。

​ 第二个数据包中,“3/4/4”表示该报文中包含3个应答资源记录、4个授权资源记录和4个额外信息记录。“CNAME www.a.shifen.com.,A 119.75.218.77,A 119.75.217.56”则表示3个应答资源记录的内容。其中CNAME表示紧随其后的记录是机器的别名,A表示紧随其后的记录是IP地址。该应答报文的长度为226字节。

socket和TCP/IP协议族的关系

​ 1.数据链路层、网络层、传输层协议是在内核中实现的。因此操作系统需要实现一组系统调用,使得应用程序能够访问这些协议提供的服务。实现这组系统调用的API(Application Programming Interface,应用程序编程接口)主要有两套:socket和XTI。XTI现在基本不再使用 。
​ 2.由socket定义的这一组API提供如下两点功能:一是将应用程序数据从用户缓冲区中复制到TCP/UDP内核发送缓冲区,以交付内核来发送数据(比如图1-5所示的send函数),或者是从内核TCP/UDP接收缓冲区中复制数据到用户缓冲区,以读取数据;二是应用程序可以通过它们来修改内核中各层协议的某些头部信息或其他数据结构,从而精细地控制底层通信的行为。比如可以通过setsockopt函数来设置IP数据报在网络上的存活时间。
​ 3.socket是一套通用网络编程接口,它不但可以访问内核中TCP/IP协议栈,而且可以访问其他网络协议栈(比如X.25协议栈、UNIX本地域协议栈等)。

第二章 IP协议详解

第三章 TCP协议详解

第四章 TCP/IP通信案例访问Internet

Quartus II开发FPGA

一、开发流程

1.FPGA工程创建

参考博客Quartus FPGA工程创建流程-CSDN博客
参考视频零基础轻松学习FPGA,小梅哥FPGA设计思想与验证方法视频教程_哔哩哔哩_bilibili

2.Quartus创建工程

  • 新建工程,将verilog代码与激励块代码导入
  • 选择FPGA芯片型号,仿真模式与开发语言

3.逻辑功能开发

  • 完成设计文件与测试文件代码编写(testbench仿真激励代码)
  • 逻辑综合(编译)

4.Modelsim仿真

  • Tools-Run Simulation Tool-RTL Simulation

image-20241025135444403

image-20241025135718571

5.程序烧录下载

二、软件使用

  • 查看门级列表 Tools-Netlist Viewer-RTL list

image-20241025122551691

unique_ptr

前置知识

智能指针与RAII

RAII是一种编程技术,用于管理资源(如内存、文件句柄、网络连接等),其核心思想是将资源的生命周期与对象的生命周期绑定。
当对象被创建时,它获取必要的资源;当对象被销毁时,它释放这些资源。这样可以保证即使发生异常,资源也能被正确地释放。

智能指针是一种特殊的指针,它封装了原始指针,并在对象生命周期结束时自动释放其指向的资源。C++标准库提供了几种智能指针,如std::unique_ptr、std::shared_ptr和std::weak_ptr。
智能指针利用了RAII的概念,它们在构造时获取资源(通常是通过new操作符分配内存),并在析构时释放资源(通过delete操作符)。

智能指针是实现RAII的一种方式。通过使用智能指针,你可以确保动态分配的内存在不再需要时被自动释放,从而避免内存泄漏。

const修饰符

const 修饰成员函数仅用于类中的成员函数时,其作用是保证该成员函数不会修改所属对象的状态

1
2
3
void display() const {
std::cout << "Student: " << name << ", Score: " << score << std::endl;
}

default修饰符

显式默认构造函数

1
2
3
4
class Student{
public:
Student() = default;
};
  • 构造函数:如 Student() = default;,告诉编译器自动生成默认的无参构造函数。
  • 析构函数:~Student() = default;,告诉编译器自动生成默认的析构函数。
  • 拷贝构造函数和拷贝赋值运算符:如果没有特别的要求,使用 = default 可以让编译器生成默认的拷贝构造和赋值运算符。

当你需要让类有特定行为(如自定义构造、析构等),但仍然希望保留编译器生成的某些默认行为时,可以用 = default 显式要求编译器生成这些函数。

类型转换

static_cast

含义

  • static_cast 是一种类型安全的转换,可以在不进行运行时检查的情况下转换类型。

用途

  • 用于已知类型之间的转换,如基本数据类型、类层次结构中的上行或下行转换(当确定转换是安全时)。

示例

1
2
3
4
5
6
7
8
int a = 10;
double b = static_cast<double>(a); // 基本数据类型转换

class Base {};
class Derived : public Base {};

Base* basePtr = new Derived();
Derived* derivedPtr = static_cast<Derived*>(basePtr); // 注意:没有安全检查
reinterpret_cast

含义

  • reinterpret_cast 用于进行低级别的类型转换,几乎可以将任何指针类型转换为任何其他指针类型。

用途

  • 主要用于处理底层操作,如指针的位表示。由于它不进行任何类型检查,使用时需要小心。

示例

1
2
3
int a = 10;
void* ptr = reinterpret_cast<void*>(&a); // 将 int* 转换为 void*
int* intPtr = reinterpret_cast<int*>(ptr); // 再次转换回 int*
const_cast

含义

  • const_cast 用于添加或去除对象的 constvolatile 属性。

用途

  • 主要用于需要修改一个常量对象的场景,但要小心使用,因为这可能导致未定义行为。

示例

1
2
3
const int a = 10;
int* b = const_cast<int*>(&a); // 去除 const 属性
*b = 20; // 这是不安全的,可能导致未定义行为
dynamic_cast

含义

  • dynamic_cast 用于在类层次结构中进行安全的向下转换(downcasting)。

用途

  • 主要用于多态类型(即有虚函数的类)之间的安全转换,能够检查转换是否成功。如果转换失败,返回 nullptr(对于指针)或抛出 std::bad_cast(对于引用)。

示例

1
2
3
4
5
6
7
8
class Base { virtual void func() {} }; // 有虚函数
class Derived : public Base {};

Base* basePtr = new Derived();
Derived* derivedPtr = dynamic_cast<Derived*>(basePtr); // 安全转换
if (derivedPtr) {
// 转换成功
}
总结
  • **const_cast**:添加或去除 const 属性。用于处理常量性。

  • **dynamic_cast**:安全地在类层次结构中转换,支持运行时检查。用于多态类型的安全转换。

  • **static_cast**:类型安全的转换,适用于已知的类型关系。基本类型之间转换

  • **reinterpret_cast**:用于低级别转换,没有安全检查。

move/forward

左值

定义:左值是指有名称并且可以在内存中持久存在的对象。它们可以出现在赋值操作的左侧。

特点

  • 具有持久的地址,可以取地址(使用 & 运算符)。

  • 通常是变量或对象。

右值

定义:右值是指临时对象或字面量,它们没有名称,通常是不能在内存中持久存在的对象。右值出现在赋值操作的右侧。

特点

  • 不具有持久的地址,通常是临时计算结果。
  • 不能取地址。
区别
  • 持久性:左值有持久的内存地址,可以在代码中多次引用;而右值是临时的,通常只在表达式中存在一次。
  • 可修改性:左值可以被修改,右值通常不能直接修改(因为它们没有名称)。
move
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
#include <utility>

class MyClass {
public:
MyClass() { std::cout << "Constructed\n"; }
MyClass(const MyClass&) { std::cout << "Copied\n"; }
MyClass(MyClass&&) noexcept { std::cout << "Moved\n"; }
};

void process(MyClass obj) {
// Do something with obj
}

int main() {
MyClass a; // a 是左值
process(std::move(a)); // std::move(a) 是右值
return 0;
}

在这个示例中,process 函数的参数是一个 MyClass 对象:

  • 当我们调用 process(std::move(a)) 时,我们将 a 转换为右值,以便在 process 函数中通过移动构造函数创建 obj

  • 这样,obj 可能会通过移动构造来初始化,而不是通过复制构造。输出将会显示 “Moved”,而不是 “Copied”,这表明资源被成功转移而不是重复创建。

  • main 函数中,a 是一个左值,因为它是一个有名称的对象,存在于内存中。

  • 当我们调用 std::move(a) 时,std::movea 转换为右值,这表示我们希望转移 a 的资源到 process 函数中。此时,a 可以被视为一个临时对象,所有权可以被移动。

移动语义
  • 减少复制开销:右值可以通过移动构造函数或移动赋值运算符来转移资源,而不是复制。这对于管理大量数据的对象(如容器、文件句柄等)尤其重要。
  • 效率:移动操作通常比复制操作要快,因为它只需要转移指针或其他简单的资源标识符,而不是复制整个对象的内容。
表示所有权转移
  • 语义清晰:使用 std::move 明确表示我们不再需要原来的对象,可以将其资源转移到另一个对象中。这样,调用者(如 process 函数)能够明确知道它将获得资源的所有权。
forward

noexcept

用于指示某个函数不会抛出异常。使用 noexcept 可以帮助编译器进行优化,生成更高效的代码,并在运行时提供更好的异常安全性

使用方式

  • 在函数声明中使用:

    1
    2
    3
    void myFunction() noexcept {
    // 该函数不会抛出异常
    }
  • 在表达式中使用,判断一个函数是否可能抛出异常:

    1
    static_assert(noexcept(myFunction()), "myFunction may throw!");

static_assert(noexcept(myFunction()), "myFunction may throw!"); 是一个编译时断言,用于检查 myFunction 是否声明为 noexcept

详细解释:

  1. **noexcept(myFunction())**:这个表达式检查 myFunction 是否会抛出异常。如果 myFunctionnoexcept 的,结果为 true;否则为 false
  2. **static_assert**:这是一个在编译时进行检查的语句。如果其第一个参数为 false,编译器会产生错误,并输出第二个参数的错误信息。这样,开发者可以在编译时捕捉到潜在的问题。
  3. 错误信息:如果 myFunction 可能抛出异常(即 noexcept(myFunction())false),编译器将显示 "myFunction may throw!" 的错误信息,提示开发者。

示例:

1
2
3
4
5
6
7
8
9
10
11
void myFunction() noexcept {
// 不会抛出异常
}

static_assert(noexcept(myFunction()), "myFunction may throw!"); // 通过检查

void anotherFunction() {
throw std::runtime_error("Error");
}

static_assert(noexcept(anotherFunction()), "anotherFunction may throw!"); // 编译时错误

constexpr

用于指示某个函数或变量在编译时可被求值。它使得在编译期间进行常量计算成为可能,通常用于优化性能。

使用方式

  • 用于函数声明:

    1
    2
    3
    constexpr int add(int a, int b) {
    return a + b;
    }
  • 用于变量声明:

    1
    constexpr int maxSize = 100; // 编译时常量
  • 可以与 iffor 等控制结构结合使用,使得复杂计算在编译时完成:

    1
    2
    3
    constexpr int factorial(int n) {
    return (n <= 1) ? 1 : n * factorial(n - 1);
    }

优势

  1. 编译时计算:减少运行时计算的开销,提高程序性能。
  2. 类型安全:使用 constexpr 可以确保在编译时进行类型检查,从而降低运行时错误的可能性。
  3. 优化常量表达式:可以用于数组大小、模板参数等需要编译时常量的场合。

左值引用与右值引用

左值引用

通过&获得左值引用,左值引用只能绑定左值。

1
2
3
4
5
6
7
int intValue1 = 10;
//将intValue1绑定到intValue2和intValue3
int &intValue2 = intValue1, &intValue3 = intValue2;
intValue2 = 100;
std::cout << intValue1 << std::endl;//100
std::cout << intValue2 << std::endl;//100
std::cout << intValue3 << std::endl;//100

不能将左值引用绑定到一个右值,但是const的左值引用可以,常量引用不能修改绑定对象的值

1
2
int &intValue1 = 10;//错误
const int &intValue2 = 10;//正确
右值引用

通过&&获得右值引用,右值引用只能绑定右值
右值引用的好处是减少右值作为参数传递时的复制开销

1
2
3
int intValue = 10;
int &&intValue2 = 10;//正确
int &&intValue3 = intValue;//错误

使用std::move可以获得绑定到一个左值的右值引用

1
2
int intValue = 10;
int &&intValue3 = std::move(intValue);

C++移动构造/拷贝构造

1
2
3
4
5
6
7
8
9
10
11
12
// 拷贝构造函数 拷贝构造:复制资源,两个对象拥有自己的副本,可能导致性能问题。
Student(const Student& other) : name(other.name), score(other.score) {
std::cout << "Copied: " << name << "\n";
}

// 移动构造函数 移动构造:转移资源的所有权,通常更加高效,源对象在移动后可能处于未定义状态。
Student(Student&& other) noexcept
: name(std::move(other.name)), score(other.score) {
std::cout << "Moved: " << name << "\n";
other.score = 0; // 清空源对象的状态
other.name = "Unknown";
}

移动构造函数的触发:

1
2
Student student1("Ros", 95);
Student student2 = std::move(student1); // 这里触发了移动构造

相关api

std::unique_ptr 提供了一个 reset() 方法,用于重置智能指针的管理对象或将其指向新的对象。

1
void reset(Student* ptr = nullptr);
  • 作用:reset 用于替换当前管理的指针对象。如果智能指针已经持有一个对象,reset() 会释放当前管理的对象,然后将其指向新对象(或为空)。
  • 参数:它接受一个原始指针 ptr,该指针将成为新的管理对象。如果不传参数,则会将当前指针对象重置为 nullptr,释放原来的资源。

在 std::make_unique() 中,<> 表示这是一个模板函数的调用。具体来说,std::make_unique 是一个模板,Student 是其模板参数,表示要创建的对象类型。

在 std::make_unique() 的末尾有一个空的圆括号 (),这表示调用 make_unique 函数

1
2
3
4
5
std::make_unique源码:
template<typename _Tp, typename... _Args>
inline typename _MakeUniq<_Tp>::__single_object
make_unique(_Args&&... __args)
{ return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); }

1.代码分析:

  • 模板参数:

    • typename _Tp:表示要创建的对象类型(比如 Student)。
    • typename… _Args:表示任意数量的参数,允许传递构造对象所需的任意数量和类型的参数。
  • 返回类型:

    • typename _MakeUniq<_Tp>::__single_object:这里使用了一个类型别名,通常是 std::unique_ptr<_Tp>。这个返回类型表示这个函数返回一个指向 _Tp 类型的智能指针。
  • 函数体:

    • new _Tp(std::forward<_Args>(__args)…):这里调用了 Tp 的构造函数,传递了使用 std::forward 转发的参数。std::forward 用于完美转发参数,保持参数的值类别(左值或右值)。
    • _return unique_ptr<_Tp>(…):创建并返回一个 std::unique_ptr,它管理新创建的对象的生命周期。

2.与 std::make_unique() 的关系

当你调用 std::make_unique() 时,发生的事情如下:

  • Student 被推断为 _Tp,即要创建的对象类型。
  • 空的 () 表示没有构造参数,因此 _Args 为零参数。
  • 这个调用实际上转发了空的参数给 make_unique 函数,即 __args 是一个空包(没有参数)。

3.小括号和尖括号的区别

  • 尖括号 <>:用于指明模板参数类型。在 std::make_unique() 中,尖括号用于指定模板类型 Student,这是模板函数的类型参数。

  • 小括号 ():用于调用函数或构造对象。在 std::make_unique() 中,小括号表示调用 make_unique 函数。如果有参数,则小括号中会包含这些参数;如果没有参数,如 std::make_unique(),则小括号为空。

4.参数数量的不同

  • 在 make_unique 的实现中,使用了可变参数模板 (typename… Args) 来支持任意数量的构造参数。
  • _在调用 std::make_unique() 时,如果没有提供参数,_Args 就是一个空包,允许你创建一个 Student 对象的默认实例。

总结

  • std::make_unique() 通过模板函数 make_unique 实现了创建智能指针的功能。

  • 尖括号 <> 用于指定模板类型,而小括号 () 用于调用函数。

  • make_unique 的实现灵活地支持任意数量的构造参数,允许你根据需要创建不同的对象。通过这种方式,std::make_unique 不仅提高了代码的安全性和可读性,也减少了手动内存管理的负担。

不可拷贝特性

  1. 使用 std::move显式地将一个对象转换为右值引用,从而启用移动语义。它不会真的移动对象,而是指示编译器可以安全地窃取资源。
  2. 将 std::unique_ptr 移入 vector 中是不可以直接push_back(), 在 C++ 中,std::unique_ptr 是不可拷贝的。
1
2
3
4
5
6
7
8
9
10
11
std::unique_ptr<Student> p1(new Student());
std::unique_ptr<Student> p2 = p1; // 错误:不能拷贝 unique_ptr
如果你想要将 p1 的所有权转移给 p2,必须使用移动语义,即使用 std::move(),将 p1 变成右值引用:

std::unique_ptr<Student> p2 = std::move(p1); // 合法:p1 的所有权转移到 p2
在这之后,p1 会变为空 (nullptr),而 p2 将接管 p1 原本指向的资源。

students.push_back(std::move(student));
将 unique_ptr 对象放入 std::vector 中,原因如下:
1.unique_ptr 不可拷贝:由于 unique_ptr 是独占的,不能进行拷贝操作。如果直接尝试 students.push_back(student),编译器会报错,因为 push_back 要求对 student 进行拷贝
2.转移所有权:使用 std::move(student),你告诉编译器,student 的资源可以被转移到 students 容器中,student 之后将不再拥有资源(即变为空)。

完整程序如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#include <iostream>
#include <string>
#include <vector>
#include <memory>


class Student{
private:
std::string name;
double score;
public:
Student() = default;
void set(const std::string& n, double s)
{
this->name = n;
this->score = s;
}
double get_score() const {return score;}
const std::string& get_name() const {return name;}
void display() const {
std::cout << "name : " << name << "\t score " << score << std::endl;
}
~Student(){

}
};

int compare(const std::unique_ptr<Student>& a, const std::unique_ptr<Student>& b)
{
if(a->get_score() < b->get_score()) return 1;
if(a->get_score() > b->get_score()) return -1;
return -1;
}


int main()
{
//max(new Student())和max(new Student)效果一样 都使用默认构造函数
//!智能指针写法 两种后者更好一些
// std::unique_ptr<Student> max(new Student());
// std::unique_ptr<Student> min(new Student);

// std::unique_ptr<Student> min = std::make_unique<Student>();
// std::unique_ptr<Student> max = std::make_unique<Student>();

//!这样是开辟在栈上;注意野指针 由于最后还是会指向栈上的指针,这里就不必要new开辟内存
// Student *min = nullptr;
// Student *max = nullptr;

//! 下面这种写法是最傻逼的,一个变量一行!//
// Student *min,*max = new Student();
//! 这样语法正确,但是此场景存在内存泄漏,因为max min后面改变指针指向,指向了栈上的元素
// Student *min = new Student();
// Student *max = new Student();


std::vector<std::unique_ptr<Student>> students;
std::string stu_name[] = { "Rose","Mike","Eve","Micheal","Jack" };
double stu_score[] = { 95,84,88,64,100 };

// 动态创建学生对象并存储在智能指针中
for(int i = 0; i < 5; i ++){
//使用 std::make_unique<Student>() 创建动态分配的学生对象,确保它们都在堆上分配。
auto student = std::make_unique<Student>();
student->set(stu_name[i], stu_score[i]);
student->display();
students.push_back(std::move(student));
}

std::unique_ptr<Student> *min = &students[0];
std::unique_ptr<Student> *max = &students[0];

for(int i = 0; i < 5; i ++){
if(compare(*min, students[i]) == 1)
min = &students[i]; // min指向新的智能指针
if(compare(*max, students[i]) == -1)
max = &students[i]; // max指向新的智能指针
}

//!min 和 max 指向 std::unique_ptr<Student> 的指针 解引用一层才是指向Student的指针 才可以使用->
std::cout << "The worst student : " << (*min)->get_name() << std::endl;
std::cout << "The best student : " << (*max)->get_name() << std::endl;
return 0;
}

shared_ptr

信号与槽机制

lambda表达式

linux使用GCC编译STM32并移植FreeRTOS

交叉编译工具链

CubeMX生成Makefile工程

编译烧写

后续学习OpenOCD研究一下编译

FreeRTOS系统移植

FreeRTOS应用之消息队列

webserver

boa

boa概述

image-20241015223050748

boa移植

1.解压源代码
1
tar -xvf boa-0.94.13.tar.gz 
2.生成Makefile
1
2
cd ./boa-0.94.13/src 
./configure
3.编译源代码
1
2
3
4
5
6
将Makefile中的下面内容:
CC = gcc
CPP = gcc –E
改为(即采用交叉编译工具):
CC = arm-linux-gcc
CPP = arm-linux-gcc –E
4.修改源代码 修改src/compat.h

出现错误:

1
2
3
4
compat.h:120:30: note: in definition of macro ‘TIMEZONE_OFFSET’
#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff
^
make: *** [util.o] Error 1

解决方案:

vim compat.h 
#define TIMEZONE_OFFSET(foo) (foo)->tm_gmtoff        //修改成这个
//#define TIMEZONE_OFFSET(foo) foo##->tm_gmtoff        //注释掉

vim boa.c  //注释掉下面部分
#if 0     
    if (setuid(0) != -1) {
        DIE("icky Linux kernel bug!");
    }
#endif   
5.编译 make clean && make -j8生成boa文件

此时再次按照自己的需求修改安装目录,着重修改如下内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Port  80  
User 0
Group 0
#Listen 192.68.0.5
ErrorLog /boa/log/error_log
AccessLog /boa/log/access_log
DocumentRoot /boa/www
UserDir public_html
DirectoryIndex index.html
DirectoryMaker /boa/boa_indexer
KeepAliveMax 1000
KeepAliveTimeout 10
MimeTypes /boa/mime.types
DefaultType text/plain
CGIPath /bin:/usr/bin:/usr/local/bin
Alias /doc /usr/doc
ScriptAlias /cgi-bin/ /boa/cgi-bin/

我的目录在/etc/boa,boa.conf配置文件如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196

# Boa v0.94 configuration file
# File format has not changed from 0.93
# File format has changed little from 0.92
# version changes are noted in the comments
#
# The Boa configuration file is parsed with a lex/yacc or flex/bison
# generated parser. If it reports an error, the line number will be
# provided; it should be easy to spot. The syntax of each of these
# rules is very simple, and they can occur in any order. Where possible
# these directives mimic those of NCSA httpd 1.3; I saw no reason to
# introduce gratuitous differences.

# $Id: boa.conf,v 1.25 2002/03/22 04:33:09 jnelson Exp $

# The "ServerRoot" is not in this configuration file. It can be compiled
# into the server (see defines.h) or specified on the command line with
# the -c option, for example:
#
# boa -c /usr/local/boa


# Port: The port Boa runs on. The default port for http servers is 80.
# If it is less than 1024, the server must be started as root.

Port 80

# Listen: the Internet address to bind(2) to. If you leave it out,
# it takes the behavior before 0.93.17.2, which is to bind to all
# addresses (INADDR_ANY). You only get one "Listen" directive,
# if you want service on multiple IP addresses, you have three choices:
# 1. Run boa without a "Listen" directive
# a. All addresses are treated the same; makes sense if the addresses
# are localhost, ppp, and eth0.
# b. Use the VirtualHost directive below to point requests to different
# files. Should be good for a very large number of addresses (web
# hosting clients).
# 2. Run one copy of boa per IP address, each has its own configuration
# with a "Listen" directive. No big deal up to a few tens of addresses.
# Nice separation between clients.
# The name you provide gets run through inet_aton(3), so you have to use dotted
# quad notation. This configuration is too important to trust some DNS.

#Listen 192.68.0.5

# User: The name or UID the server should run as.
# Group: The group name or GID the server should run as.

User 0
Group 0

# ServerAdmin: The email address where server problems should be sent.
# Note: this is not currently used, except as an environment variable
# for CGIs.

#ServerAdmin root@localhost

# ErrorLog: The location of the error log file. If this does not start
# with /, it is considered relative to the server root.
# Set to /dev/null if you don't want errors logged.
# If unset, defaults to /dev/stderr

ErrorLog /etc/boa/log/error_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#ErrorLog "|/usr/sbin/cronolog --symlink=/var/log/boa/error_log /var/log/boa/error-%Y%m%d.log"

# AccessLog: The location of the access log file. If this does not
# start with /, it is considered relative to the server root.
# Comment out or set to /dev/null (less effective) to disable
# Access logging.

AccessLog /etc/boa/log/access_log
# Please NOTE: Sending the logs to a pipe ('|'), as shown below,
# is somewhat experimental and might fail under heavy load.
# "Usual libc implementations of printf will stall the whole
# process if the receiving end of a pipe stops reading."
#AccessLog "|/usr/sbin/cronolog --symlink=/var/log/boa/access_log /var/log/boa/access-%Y%m%d.log"

# UseLocaltime: Logical switch. Uncomment to use localtime
# instead of UTC time
#UseLocaltime

# VerboseCGILogs: this is just a logical switch.
# It simply notes the start and stop times of cgis in the error log
# Comment out to disable.

#VerboseCGILogs

# ServerName: the name of this server that should be sent back to
# clients if different than that returned by gethostname + gethostbyname

#ServerName www.your.org.here

# VirtualHost: a logical switch.
# Comment out to disable.
# Given DocumentRoot /var/www, requests on interface 'A' or IP 'IP-A'
# become /var/www/IP-A.
# Example: http://localhost/ becomes /var/www/127.0.0.1
#
# Not used until version 0.93.17.2. This "feature" also breaks commonlog
# output rules, it prepends the interface number to each access_log line.
# You are expected to fix that problem with a postprocessing script.

#VirtualHost

# DocumentRoot: The root directory of the HTML documents.
# Comment out to disable server non user files.

DocumentRoot /etc/boa/www

# UserDir: The name of the directory which is appended onto a user's home
# directory if a ~user request is recieved.

UserDir public_html

# DirectoryIndex: Name of the file to use as a pre-written HTML
# directory index. Please MAKE AND USE THESE FILES. On the
# fly creation of directory indexes can be _slow_.
# Comment out to always use DirectoryMaker

DirectoryIndex index.html

# DirectoryMaker: Name of program used to create a directory listing.
# Comment out to disable directory listings. If both this and
# DirectoryIndex are commented out, accessing a directory will give
# an error (though accessing files in the directory are still ok).

DirectoryMaker /etc/boa/boa_indexer

# DirectoryCache: If DirectoryIndex doesn't exist, and DirectoryMaker
# has been commented out, the the on-the-fly indexing of Boa can be used
# to generate indexes of directories. Be warned that the output is
# extremely minimal and can cause delays when slow disks are used.
# Note: The DirectoryCache must be writable by the same user/group that
# Boa runs as.

# DirectoryCache /var/spool/boa/dircache

# KeepAliveMax: Number of KeepAlive requests to allow per connection
# Comment out, or set to 0 to disable keepalive processing

KeepAliveMax 1000

# KeepAliveTimeout: seconds to wait before keepalive connection times out

KeepAliveTimeout 10

# MimeTypes: This is the file that is used to generate mime type pairs
# and Content-Type fields for boa.
# Set to /dev/null if you do not want to load a mime types file.
# Do *not* comment out (better use AddType!)

MimeTypes /etc/boa/mime.types


# DefaultType: MIME type used if the file extension is unknown, or there
# is no file extension.

DefaultType text/plain

# CGIPath: The value of the $PATH environment variable given to CGI progs.

CGIPath /bin:/usr/bin:/usr/local/bin

# SinglePostLimit: The maximum allowable number of bytes in
# a single POST. Default is normally 1MB.

# AddType: adds types without editing mime.types
# Example: AddType type extension [extension ...]

# Uncomment the next line if you want .cgi files to execute from anywhere
#AddType application/x-httpd-cgi cgi

# Redirect, Alias, and ScriptAlias all have the same semantics -- they
# match the beginning of a request and take appropriate action. Use
# Redirect for other servers, Alias for the same server, and ScriptAlias
# to enable directories for script execution.

# Redirect allows you to tell clients about documents which used to exist in
# your server's namespace, but do not anymore. This allows you to tell the
# clients where to look for the relocated document.
# Example: Redirect /bar http://elsewhere/feh/bar

# Aliases: Aliases one path to another.
# Example: Alias /path1/bar /path2/foo

Alias /doc /usr/doc

# ScriptAlias: Maps a virtual path to a directory for serving scripts
# Example: ScriptAlias /htbin/ /www/htbin/

ScriptAlias /cgi-bin/ /etc/boa/cgi-bin/

6.新建index.html和test.c

index.html:

1
2
3
4
5
6
7
8
<html>
<body>
<h3>this is a test! 2024-10-15 jym</h3><br/>
<img src="oei.jpg"/>
<h3>tree picture</h3><br/>
<a href="/cgi-bin/test.cgi">to cgi page</a> //指定了cgi可执行文件存放的路径,默认从/boa的根目录开始查找
</body>
</html> j

test.c:

1
2
3
4
5
6
7
8
9
10
#include <stdio.h>
int main()
{
printf("Content-type:text/html\n\n"); //这句一定要加上
printf("<html><body>");
printf("<font style=\"color:red; font-size:30px;\">Hello, CGI!</font><br/>");
printf("<a href=\"/index.html\">return index.html</a>");
printf("</body></html>");
return 0;
}

gcc -o test.cgi test.c 生成测试所用的test.cgi

7.拷贝相关文件
1
2
3
4
5
6
7
cp boa boa_indexer test.cgi index.html ../boa.conf ../../arm64_boa
cd ../../arm64_boa
mkdir www cgi-bin log
sudo cp /etc/mime.types ./
mv index.html www
mv test.cgi cgi-bin
cp ~/pics/oei.jpg ./www

image-20241017093215392

8.ARM移植

PC:

1
cp ./arm64_boa ../rootfs #rootfs是我所设置的nfs挂载目录

虚拟ARM:

1
mount -t nfs -o proto=tcp,nolock 10.0.0.1:/home/jym/code/linux/qemu/rootfs /mnt

将对应文件从/mnt文件夹中拷贝到对应目录 可执行文件放在/usr/sbin boa对应文件放在/etc/boa

第一遍运行出现如下问题:

1
2
[root@FriendlyARM /]# ./boa
gethostbyname:: Success

解决办法:

1
2
vim boa.conf #添加如下内容即可
ServerName www.mydomain.com
9.运行测试

image-20241017092231141

注意这里我们的虚拟开发板是10.0.0.2,在移植过程中,我先在x86平台上移植了boa,本地地址为127.0.0.1或者10.0.0.1都是如下所示画面,但是10.0.0.2却是原来的bo官方界面,后续移植的时候,我将虚拟平台上的boa重命名为boa_old,其中/etc/boa和/usr/sbin中都存在,然后将我生成的文件拷贝到/etc/boa中关掉之前的boa服务再次运行即可

1
2
ps | grep boa
kill 进程号

image-20241017092608730

image-20241017092209961

GoAhead

后续移植

GoAhead概述

GoAhead移植

madplay

madplay概述

Madplay是一个开源mp3解码库,对mp3解码算法做了很多优化

单纯想要播放音乐,在linux下播放MP3文件,我们可以使用sox,运行下面语句即可

1
2
sudo apt-get install sox libsox-fmt-all
play shanghaitan.mp3

madplay-PC-Linux

1.解压源码包

1
2
3
4
5
6
7
8
9
10
11
12
13
cd /arm
mkdir madplay
cd madplay
mkdir tarball src-x86 src-arm target-x86 target-arm

cd tarball
for f in $(ls *.tar.gz); do tar -zxvf $f –C ../src-x86; done
或者:
cd tarball
tar -zxvf zlib-1.2.3.tar.gz -C ../src-x86
tar -zxvf libid3tag-0.15.1b.tar.gz -C ../src-x86
tar -zxvf libmad-0.15.1b.tar.gz -C ../src-x86
tar -zxvf madplay-0.15.2b.tar.gz -C ../src-x86

目录说明:
tarball 目录用来存放所有的源代码包
src-x86 目录用来存放 X86 版本的所有源代码文件
src-arm 目录用来存放 ARM 版本的所有源代码文件
target-x86 目录是 X86 版本的安装目录
target-arm 目录是 ARM 版本的安装目录
接下来把从网上下载到的源代码包放入 tarball 目录

2.编译madplay及它所依赖的库文件

zlib
1
2
3
4
cd ../src-x86/zlib-1.2.3
./configure --prefix=/home/jym/code/linux/qemu/madplay/target-x86
make -j8
make install
libid3tag
1
2
3
4
cd ../libid3tag-0.15.1b
./configure --prefix=/home/jym/code/linux/qemu/madplay/target-x86/ --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/home/jym/code/linux/qemu/madplay/target-x86/include LDFLAGS=-L/home/jym/code/linux/qemu/madplay/target-x86/lib
make -j8
make install

其中,–disable-shared –enable-static 是指定为静态编译,不过这样并不能够进行静态编译.

libmad
1
2
cd ../libmad-0.15.1b
./configure --prefix=/home/jym/code/linux/qemu/madplay/target-x86 --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/home/jym/code/linux/qemu/madplay/target-x86/include LDFLAGS=-L/home/jym/code/linux/qemu/madplay/target-x86/lib

如果此时 make,make install 就会报错,说是”-fforce-mem”参数不能识别。打开当前目录下的 Makefile 文件,去掉里面出现的”-fforce-mem”,再进行后续操作

1
2
make -j8
make install
madplay
1
2
3
4
cd ../madplay-0.15.2b
./configure --prefix=/home/jym/code/linux/qemu/madplay/target-x86 --disable-debugging --disable-shared --enable-static CPPFLAGS=-I/home/jym/code/linux/qemu/madplay/target-x86/include LDFLAGS=-L/home/jym/code/linux/qemu/madplay/target-x86/lib
make -j8
make install

但是,这样得到的是动态链接。如果在 madplay-0.15.2b 目录中执行gcc 命令,并且在最后加上-static,运行以后就可以得到静态链接的程序

静态链接
1
gcc -Wall -O2 -fomit-frame-pointer -o madplay madplay.o getopt.o getopt1.o version.o resample.o filter.o tag.o crc.o rgain.o player.o audio.o audio_aiff.o audio_cdda.o audio_hex.o audio_null.o audio_raw.o audio_snd.o audio_wave.o audio_oss.o -L/home/jym/code/linux/qemu/madplay/target-x86/lib /home/jym/code/linux/qemu/madplay/target-x86/lib/libmad.a /home/jym/code/linux/qemu/madplay/target-x86/lib/libid3tag.a -lz -lm -static
1
2
ls /home/jym/code/linux/qemu/madplay/target-x86/bin/
abxtest madplay

使用file查看madplay文件

1
2
3
4
5
jym@jym-virtual-machine:~/code/linux/qemu/madplay/target-x86/bin$ file madplay
madplay: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=16f52c3b88a3bce95f5274f8d6b43d15b03fca25, for GNU/Linux 3.2.0, not stripped

jym@jym-virtual-machine:~/code/linux/qemu/madplay/src-x86/madplay-0.15.2b$ file madplay
madplay: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, BuildID[sha1]=e5deaf02bfed1309a9f82f53f4c89edcd0e45070, for GNU/Linux 3.2.0, not stripped

image-20241016232303443

3.运行测试

移植完成后还有一个问题需要处理,直接运行会报错如下:

1
2
3
jym@jym-virtual-machine:~/code/linux/qemu/madplay/target-x86/bin$ ./madplay shanghaitan.mp3
MPEG Audio Decoder 0.15.2 (beta) - Copyright (C) 2000-2004 Robert Leslie et al.
audio: /dev/dsp: 没有那个文件或目录

原因是madplay使用oss建构,在网上查找了一些资料,可以用alsa支持oss架构,后续我再探索,root 账号下不能用audio,切记!

简单解决方法如下:使用madplay解码再使用aplay播放

1
./madplay -o wav:- shanghaitan.mp3 | aplay

最终效果如图所示

image-20241016233558824

4.构建脚本文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/bin/sh
# This build script is for madplay under PC-Linux
MADPLAY_DIR=/home/jym/code/linux/qemu/madplay
TARBALL_DIR=$MADPLAY_DIR/tarball
SRC_DIR=$MADPLAY_DIR/src-x86
TARGET_DIR=$MADPLAY_DIR/target-x86

tar -zxvf $TARBALL_DIR/zlib-1.2.3.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/libid3tag-0.15.1b.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/libmad-0.15.1b.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/madplay-0.15.2b.tar.gz -C $SRC_DIR
# for f in $(ls $TARBALL_DIR/*.tar.gz); do tar -zxvf $f -C $SRC_DIR; done
cd $SRC_DIR/zlib-1.2.3
./configure --prefix=$TARGET_DIR
make
make install

cd $SRC_DIR/libid3tag-0.15.1b
./configure --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib
make
make install

cd $SRC_DIR/libmad-0.15.1b
./configure --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib

sed 's/-fforce-mem //g' Makefile > Makefile.bak
mv Makefile.bak Makefile

make
make install

cd $SRC_DIR/madplay-0.15.2b
./configure --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib
make
make install

gcc -Wall -O2 -fomit-frame-pointer -o madplay-s madplay.o getopt.o getopt1.o version.o resample.o filter.o tag.o crc.o rgain.o player.o audio.o audio_aiff.o audio_cdda.o audio_hex.o audio_null.o audio_raw.o audio_snd.o audio_wave.o audio_oss.o -L$TARGET_DIR/lib $TARGET_DIR/lib/libmad.a $TARGET_DIR/lib/libid3tag.a -lz -lm -static

mv madplay madplay-d
mv $TARGET_DIR/bin/madplay $TARGET_DIR/bin/madplay-d
cp -a madplay-s $TARGET_DIR/bin

madplay-ARM-Linux

1.构建脚本

移植ARM-Linux版本和上述PC端流程类似,使用交叉编译器即可,这里给出编译脚本,一键运行即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#!/bin/sh
# This build script is for madplay under ARM-Linux
MADPLAY_DIR=/home/jym/code/linux/qemu/madplay
TARBALL_DIR=$MADPLAY_DIR/tarball
SRC_DIR=$MADPLAY_DIR/src-arm
TARGET_DIR=$MADPLAY_DIR/target-arm
tar -zxvf $TARBALL_DIR/zlib-1.2.3.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/libid3tag-0.15.1b.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/libmad-0.15.1b.tar.gz -C $SRC_DIR
tar -zxvf $TARBALL_DIR/madplay-0.15.2b.tar.gz -C $SRC_DIR
# for f in $(ls $TARBALL_DIR/*.tar.gz); do tar -zxvf $f -C $SRC_DIR; done

export CC=arm-linux-gcc
cd $SRC_DIR/zlib-1.2.3
./configure --prefix=$TARGET_DIR
make -j8
make install

cd $SRC_DIR/libid3tag-0.15.1b
./configure --host=arm-linux --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib
make -j8
make install

cd $SRC_DIR/libmad-0.15.1b
./configure --host=arm-linux --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib

sed 's/-fforce-mem //g' Makefile > Makefile.bak
mv Makefile.bak Makefile

make -j8
make install

cd $SRC_DIR/madplay-0.15.2b
./configure --host=arm-linux --prefix=$TARGET_DIR --disable-debugging --disable-shared --enable-static CPPFLAGS=-I$TARGET_DIR/include LDFLAGS=-L$TARGET_DIR/lib
make -j8
make install

arm-linux-gcc -Wall -O2 -fomit-frame-pointer -o madplay-s madplay.o getopt.o getopt1.o version.o resample.o filter.o tag.o crc.o rgain.o player.o audio.o audio_aiff.o audio_cdda.o audio_hex.o audio_null.o audio_raw.o audio_snd.o audio_wave.o audio_oss.o -L$TARGET_DIR/lib $TARGET_DIR/lib/libmad.a $TARGET_DIR/lib/libid3tag.a -lz -lm -static

mv madplay madplay-d
mv $TARGET_DIR/bin/madplay $TARGET_DIR/bin/madplay-d
cp -a madplay-s $TARGET_DIR/bin

这里经过验证,是可以不报错运行完毕的,生成的相关文件信息如下:

image-20241017090810624!image-20241017090719513

2.运行验证

启动虚拟arm平台

image-20241017091028207

音乐成功响起

image-20241017091252686

生成的相关信息如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
MPEG Audio Decoder 0.15.2 (beta) - Copyright (C) 2000-2004 Robert Leslie et al.
Title: ▒Ϻ▒̲
Artist: Ҷ▒▒▒▒
Year: 2000
Genre: Goa
<3>__free_from_pool: freeing wrong coherent size from pool
__free_from_pool: freeing wrong coherent size from pool
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283258>] (snd_pcm_oss_get_format+0x10/0x28)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283258>] (snd_pcm_oss_get_format+0x10/0x28)
[<80283258>] (snd_pcm_oss_get_format+0x10/0x28) from [<80283ed8>] (snd_pcm_oss_ioctl+0x438/0xd2c)
[<80283258>] (snd_pcm_oss_get_format+0x10/0x28) from [<80283ed8>] (snd_pcm_oss_ioctl+0x438/0xd2c)
[<80283ed8>] (snd_pcm_oss_ioctl+0x438/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<80283ed8>] (snd_pcm_oss_ioctl+0x438/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)
<3>__free_from_pool: freeing wrong coherent size from pool
__free_from_pool: freeing wrong coherent size from pool
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283280>] (snd_pcm_oss_get_channels+0x10/0x28)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283280>] (snd_pcm_oss_get_channels+0x10/0x28)
[<80283280>] (snd_pcm_oss_get_channels+0x10/0x28) from [<80283fb4>] (snd_pcm_oss_ioctl+0x514/0xd2c)
[<80283280>] (snd_pcm_oss_get_channels+0x10/0x28) from [<80283fb4>] (snd_pcm_oss_ioctl+0x514/0xd2c)
[<80283fb4>] (snd_pcm_oss_ioctl+0x514/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<80283fb4>] (snd_pcm_oss_ioctl+0x514/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)
<3>__free_from_pool: freeing wrong coherent size from pool
__free_from_pool: freeing wrong coherent size from pool
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800151e8>] (unwind_backtrace+0x0/0xf0) from [<800189c4>] (arm_dma_free+0x188/0x1b0)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<800189c4>] (arm_dma_free+0x188/0x1b0) from [<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027dfa4>] (snd_dma_free_pages+0xb4/0xc8) from [<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<8027d4ac>] (snd_pcm_lib_free_pages+0x2c/0x54) from [<80292144>] (aaci_pcm_hw_free+0x40/0x5c)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292144>] (aaci_pcm_hw_free+0x40/0x5c) from [<80292180>] (aaci_pcm_hw_params+0x20/0xd4)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80292180>] (aaci_pcm_hw_params+0x20/0xd4) from [<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80276ce4>] (snd_pcm_hw_params+0x114/0x3e0) from [<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80277984>] (snd_pcm_common_ioctl1+0x5a0/0x10a4) from [<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278990>] (snd_pcm_playback_ioctl1+0x38/0x4c0) from [<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80278e84>] (snd_pcm_kernel_ioctl+0x40/0x6c) from [<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80282730>] (snd_pcm_oss_change_params+0x818/0xcc0) from [<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283230>] (snd_pcm_oss_get_rate+0x10/0x28)
[<80283214>] (snd_pcm_oss_get_active_substream+0x68/0x74) from [<80283230>] (snd_pcm_oss_get_rate+0x10/0x28)
[<80283230>] (snd_pcm_oss_get_rate+0x10/0x28) from [<80284658>] (snd_pcm_oss_ioctl+0xbb8/0xd2c)
[<80283230>] (snd_pcm_oss_get_rate+0x10/0x28) from [<80284658>] (snd_pcm_oss_ioctl+0xbb8/0xd2c)
[<80284658>] (snd_pcm_oss_ioctl+0xbb8/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<80284658>] (snd_pcm_oss_ioctl+0xbb8/0xd2c) from [<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c6c9c>] (do_vfs_ioctl+0x80/0x5b0) from [<800c7204>] (sys_ioctl+0x38/0x60)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)
[<800c7204>] (sys_ioctl+0x38/0x60) from [<8000e2c0>] (ret_fast_syscall+0x0/0x30)

iconv

后续移植

iconv概述

iconv是字符编码转换库

iconv应用

FrameBuffer

后续移植

FrameBuffer概述

framebuffer简介

image-20241015225141191

FrameBuffer编程模型

image-20241015225312473

对framebuffer设备操作

image-20241015225538409

操作framebuffer步骤

image-20241015225711248

framebuffer信息结构

framebuffer实战

CSAPP

第一章

程序运行流程:
首先我们按下./hello ,字符串从键盘被读取 shell会将读取到的字符串逐一加载到寄存器,处理器会把hello字符串放到内存中。(注意此处不可以DMA直接读取到内存,内存只是存储空间,不放在寄存器里面program count无法指向程序)按下回车键,指令结束,shell会通过一系列指令加载可执行文件hello。这些指令会将hello中的数据和指令从磁盘督导内存而不经过CPU,这既是DMA技术。当hello中的数据和指令到达内存时,处理器就开始执行指令。CPU会将hello world\n 这个字符串复制到寄存器文件,然后再从寄存器文件复制到显示设备。

image-20240701095309869

image-20240701095600280

image-20240702105444786

image-20240702111734630

image-20240702105905118

image-20240702110710375

image-20240702111326538

image-20240702111503634

image-20240702111802383

image-20240702111905488

image-20240702112016418

image-20240702112105886

第二章 信息的表示和存储

2.1 信息存储

gcc -m32 -o hello32 hello.c #编译32位程序
gcc -m64 -o hello64 hello.c #编译64位程序
if(a && 5/a) //先判断a是否为0 如果是0,则不再执行后续

image-20240704100006629image-20240704100206456

算术右移与逻辑右移不同之处:算术右移操作数最高位为1时,左端补1 逻辑右移直接补0
一般有符号数算术右移 无符号数逻辑右移

image-20240704100617873

2.2 整数的表示

二进制补码 补码是首位是1则为负,首位为0即是正数

  • 正数的补码与原码相同。
  • 负数的补码是对原码(符号位除外)按位取反后加1得到的。

image-20240716155215275

image-20240716160847167

image-20240716161547701

image-20240716161033328

image-20240716161706090

image-20240716161738855

image-20240716161431728

2.3 整数的运算-remain

正溢出实际上是一种假溢出,因为没有舍弃位,而是最高位由0变为1,产生了负权重
负溢出是真正意义的溢出,最高位产生溢出,而被丢弃,使得负权重丢失,让结果变为正数

image-20240716163831757

image-20240716165402940

加入偏置后再右移 偏置为((1<<k )-1)接口得到向0舍入的结果

image-20240716165824328

image-20240716170116721

2.4 浮点数

image-20240716175926183

image-20240716175215470

阶码(Exponent)

阶码用于表示浮点数中指数部分的值。它决定了尾数的小数点应该向左或向右移动多少位,从而表示出不同的数值范围。在IEEE 754标准中,阶码通常采用偏移(或称为偏置)的方式来表示,这是为了使得所有的指数都有一个正的表示,包括负数。偏移值通常是2e−1−1,其中e是阶码的位数。

例如,在一个单精度浮点数(32位)中,阶码占据了其中的8位(包括一个符号位),因此偏移值为27−1=127。如果一个单精度浮点数的阶码字段为128(二进制为10000000),那么实际的指数值为128−127=1,表示小数点向右移动1位。

尾数(Mantissa 或 Significand)

尾数(或称为有效数字)是浮点数中表示数值精度(或大小)的部分。它紧跟在阶码之后,包含了浮点数中除去隐含的整数位(在IEEE 754标准中,对于非零的规格化浮点数,尾数前会隐含一个1,这个1不会存储在尾数字段中)之外的所有位。尾数可以是整数也可以是分数,但在计算机中,它们通常以二进制形式存储。

在IEEE 754标准中,尾数部分还包括了一个隐含的二进制点,这意味着尾数实际上是一个二进制小数,而不仅仅是整数或二进制数的集合。这种设计允许浮点数以更高的精度来表示小数。

image-20240716180332384

image-20240716180809606

浮点数结果不具有整数相关性image-20240716181450431

第三章 程序的机器级表示

freetype移植

类型匹配

1
2
CvScalar s = {0, 255, 0}; //!这里有一个类型不匹配问题:cv::Scalar 不能隐式转换为 CvScalar。
text.putText(img, msg, pos, s); // 绘制文本

中文字体使用宽字符

1
const wchar_t *msg = L"偏振图像"; // 使用宽字符

CvxText

  • 参考源码地址OpenCV 4.X 使用CvxText在图片显示汉字 | Learning BigBook (bigbookplus.github.io)
  • 注意linux引用opencv 使用/ 而在Windows使用\
  • 这份源文件有问题,如下图,注意临时对象不能右值引用,Visual Studio中会将其延长生命周期 linux-g++编译不会优化编译
    1
    2
    3
    4
    5
    6
    7
    8
    void CvxText::putWChar(cv::Mat &frame, wchar_t wc, CvPoint &pos, CvScalar color)
    {
    // IplImage* img = NULL;
    // img = &(cvIplImage(frame));
    //!临时对象非const不能右值引用
    IplImage temp_img = cvIplImage(frame); // 创建IplImage的副本
    IplImage* img = &temp_img; // 取得指针
    }

Makefile

  • 只需要freetype2.pc
  • 注意看系统中是否有freetype的动态库 避免多个库复用导致vim出现问题 我的Ubuntu使用的freetype动态链接库版本是libfreetype.so.6.17.1
  • 之前在Makefile中必须要加上LDFLAGS += -L/usr/lib/x86_64-linux-gnu/这句话是因为在移植freetype时执行sudo make install所以将另一个版本的pc文件或者动态/静态库安装在/usr/local/lib中 删除即可

msyh.ttf

  • 微软雅黑字体msyh.ttf需放到运行目录下

CvxText.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224

#include "CvxText.h"
#include <wchar.h>
#include <assert.h>
#include <locale.h>
#include <ctype.h>
#include "opencv2/core/core.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

using namespace cv;

CvxText::CvxText(const char *freeType)
{
assert(freeType != NULL);

// ���ֿ��ļ�, ����һ������

if (FT_Init_FreeType(&m_library)) throw;
int result = 100;
//result = FT_New_Face(m_library, freeType, 0, &m_face);
if (result = FT_New_Face(m_library, freeType, 0, &m_face)) throw;


// ���������������

restoreFont();

// ����C���Ե��ַ�������

setlocale(LC_ALL, "");
}

// �ͷ�FreeType��Դ

CvxText::~CvxText()
{
FT_Done_Face(m_face);
FT_Done_FreeType(m_library);
}

// �����������:
//
// font - ��������, Ŀǰ��֧��
// size - �����С/�հױ���/�������/��ת�Ƕ�
// underline - �»���
// diaphaneity - ͸����

void CvxText::getFont(int *type, CvScalar *size, bool *underline, float *diaphaneity)
{
if (type) *type = m_fontType;
if (size) *size = m_fontSize;
if (underline) *underline = m_fontUnderline;
if (diaphaneity) *diaphaneity = m_fontDiaphaneity;
}

void CvxText::setFont(int *type, CvScalar *size, bool *underline, float *diaphaneity)
{
// �����Ϸ��Լ��

if (type)
{
if (type >= 0) m_fontType = *type;
}
if (size)
{
m_fontSize.val[0] = fabs(size->val[0]);
m_fontSize.val[1] = fabs(size->val[1]);
m_fontSize.val[2] = fabs(size->val[2]);
m_fontSize.val[3] = fabs(size->val[3]);
}
if (underline)
{
m_fontUnderline = *underline;
}
if (diaphaneity)
{
m_fontDiaphaneity = *diaphaneity;
}
}

// �ָ�ԭʼ����������

void CvxText::restoreFont()
{
m_fontType = 0; // ��������(��֧��)

m_fontSize.val[0] = 40; // �����С
m_fontSize.val[1] = 0.5; // �հ��ַ���С����
m_fontSize.val[2] = 0.1; // ���������
m_fontSize.val[3] = 0; // ��ת�Ƕ�(��֧��)

m_fontUnderline = false; // �»���(��֧��)

m_fontDiaphaneity = 1.0; // ɫ�ʱ���(�ɲ���͸��Ч��)

// �����ַ���С

FT_Set_Pixel_Sizes(m_face, (int)m_fontSize.val[0], 0);
}

// �������(��ɫĬ��Ϊ��ɫ)

int CvxText::putText(cv::Mat &frame, const char *text, CvPoint pos)
{
//return putText(frame, text, pos, CV_RGB(255, 255, 255));
CvScalar s = {255, 255, 255};
return putText(frame, text, pos, s);
}
int CvxText::putText(cv::Mat &frame, const wchar_t *text, CvPoint pos)
{
//return putText(frame, text, pos, CV_RGB(255, 255, 255));
CvScalar s = {255, 255, 255};
return putText(frame, text, pos, s);
}

//

int CvxText::putText(cv::Mat &frame, const char *text, CvPoint pos, CvScalar color)
{
if (frame.empty()) return -1;
if (text == NULL) return -1;

//

int i;
for (i = 0; text[i] != '\0'; ++i)
{
wchar_t wc = text[i];

// ����˫�ֽڷ���

if (!isascii(wc)) mbtowc(&wc, &text[i++], 2);

// �����ǰ���ַ�

putWChar(frame, wc, pos, color);
}
return i;
}
int CvxText::putText(cv::Mat &frame, const wchar_t *text, CvPoint pos, CvScalar color)
{
if (frame.empty()) return -1;
if (text == NULL) return -1;

//

int i;
for (i = 0; text[i] != '\0'; ++i)
{
// �����ǰ���ַ�

putWChar(frame, text[i], pos, color);
}
return i;
}

// �����ǰ�ַ�, ����m_posλ��

void CvxText::putWChar(cv::Mat &frame, wchar_t wc, CvPoint &pos, CvScalar color)
{
// IplImage* img = NULL;
// img = &(cvIplImage(frame));
//!临时对象非const不能右值引用
IplImage temp_img = cvIplImage(frame); // 创建IplImage的副本
IplImage* img = &temp_img; // 取得指针

// ����unicode��������Ķ�ֵλͼ

FT_UInt glyph_index = FT_Get_Char_Index(m_face, wc);
FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT);
FT_Render_Glyph(m_face->glyph, FT_RENDER_MODE_MONO);

//

FT_GlyphSlot slot = m_face->glyph;

// ������

int rows = slot->bitmap.rows;
int cols = slot->bitmap.width;

//

for (int i = 0; i < rows; ++i)
{
for (int j = 0; j < cols; ++j)
{
int off = ((img->origin == 0) ? i : (rows - 1 - i))
* slot->bitmap.pitch + j / 8;

if (slot->bitmap.buffer[off] & (0xC0 >> (j % 8)))
{
int r = (img->origin == 0) ? pos.y - (rows - 1 - i) : pos.y + i;;
int c = pos.x + j;

if (r >= 0 && r < img->height
&& c >= 0 && c < img->width)
{
CvScalar scalar = cvGet2D(img, r, c);

// ����ɫ���ں�

float p = m_fontDiaphaneity;
for (int k = 0; k < 4; ++k)
{
scalar.val[k] = scalar.val[k] * (1 - p) + color.val[k] * p;
}

cvSet2D(img, r, c, scalar);
}
}
} // end for
} // end for

// �޸���һ���ֵ����λ��

double space = m_fontSize.val[0] * m_fontSize.val[1];
double sep = m_fontSize.val[0] * m_fontSize.val[2];

pos.x += (int)((cols ? cols : space) + sep);
}


CvxText.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#ifndef CVX_TEXT_H
#define CVX_TEXT_H

#include <ft2build.h>
#include FT_FREETYPE_H
#include "opencv2/core/core.hpp"
#include "opencv2/core/core_c.h"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
class CvxText
{
// ��ֹcopy
CvxText& operator=(const CvxText&);
public:
CvxText(const char *freeType);
virtual ~CvxText();

/**
* ��ȡ���塣Ŀǰ��Щ�����в�֧�֡�
*
* \param font ��������, Ŀǰ��֧��
* \param size �����С/�հױ���/�������/��ת�Ƕ�
* \param underline �»���
* \param diaphaneity ͸����
*
* \sa setFont, restoreFont
*/

void getFont(int *type,
CvScalar *size = NULL, bool *underline = NULL, float *diaphaneity = NULL);

/**
* �������塣Ŀǰ��Щ�����в�֧�֡�
*
* \param font ��������, Ŀǰ��֧��
* \param size �����С/�հױ���/�������/��ת�Ƕ�
* \param underline �»���
* \param diaphaneity ͸����
*
* \sa getFont, restoreFont
*/

void setFont(int *type,
CvScalar *size = NULL, bool *underline = NULL, float *diaphaneity = NULL);

/**
* �ָ�ԭʼ���������á�
*
* \sa getFont, setFont
*/

void restoreFont();

//================================================================
//================================================================

/**
* �������(��ɫĬ��Ϊ��ɫ)����������������ַ���ֹͣ��
*
* \param img �����Ӱ��
* \param text �ı�����
* \param pos �ı�λ��
*
* \return ���سɹ�������ַ����ȣ�ʧ�ܷ���-1��
*/

int putText(cv::Mat &frame, const char *text, CvPoint pos);

/**
* �������(��ɫĬ��Ϊ��ɫ)����������������ַ���ֹͣ��
*
* \param img �����Ӱ��
* \param text �ı�����
* \param pos �ı�λ��
*
* \return ���سɹ�������ַ����ȣ�ʧ�ܷ���-1��
*/

int putText(cv::Mat &frame, const wchar_t *text, CvPoint pos);

/**
* ������֡���������������ַ���ֹͣ��
*
* \param img �����Ӱ��
* \param text �ı�����
* \param pos �ı�λ��
* \param color �ı���ɫ
*
* \return ���سɹ�������ַ����ȣ�ʧ�ܷ���-1��
*/

int putText(cv::Mat &frame, const char *text, CvPoint pos, CvScalar color);

/**
* ������֡���������������ַ���ֹͣ��
*
* \param img �����Ӱ��
* \param text �ı�����
* \param pos �ı�λ��
* \param color �ı���ɫ
*
* \return ���سɹ�������ַ����ȣ�ʧ�ܷ���-1��
*/
int putText(cv::Mat &frame, const wchar_t *text, CvPoint pos, CvScalar color);

//================================================================
//================================================================

private:

// �����ǰ�ַ�, ����m_posλ��

void putWChar(cv::Mat &frame, wchar_t wc, CvPoint &pos, CvScalar color);

//================================================================
//================================================================

private:

FT_Library m_library; // �ֿ�
FT_Face m_face; // ����

//================================================================
//================================================================

// Ĭ�ϵ������������

int m_fontType;
CvScalar m_fontSize;
bool m_fontUnderline;
float m_fontDiaphaneity;
};

#endif


cjson源码学习

本篇博客用于记录学习cjson源码历程

用途

cjson用于生成/解析json格式数据的轻量级源码

代码分析

cjson结构体

对于cjson结构体的数据结构类似于树的的结构对于array/object类型

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct cJSON {
struct cJSON *next,*prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */
struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */

int type; /* The type of the item, as above. */

char *valuestring; /* The item's string, if type==cJSON_String */
int valueint; /* The item's number, if type==cJSON_Number */
double valuedouble; /* The item's number, if type==cJSON_Number */

char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */
} cJSON;

json格式

1
2
3
4
5
6
7
8
root (cJSON_Object)
├── "name" : "Jack (\"Bee\") Nimble" (cJSON_String)
└── "format" : fmt (cJSON_Object)
├── "type" : "rect" (cJSON_String)
├── "width" : 1920 (cJSON_Number)
├── "height" : 1080 (cJSON_Number)
├── "interlace" : false (cJSON_False)
└── "frame rate" : 24 (cJSON_Number)

数组解析

对于数组格式的json数据,如下代码展示如何通过child, next, prev指针构建链表进行解析。

这里新建了一个item,并赋值给child,先检测第一个数据并将其放在数组第一个位置上,这样保证链表头即是数组首个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// +----------------+     +----------------+     +----------------+
// | cJSON object | --> | cJSON object | --> | cJSON object |
// | (array head) | | (next element) | | (next element) |
// +----------------+ +----------------+ +----------------+
// item->child child->next child->next
// / | \ / | \ |
// ... ... ... ... ... ... ...
// /* Build an array from input text. */
static const char *parse_array(cJSON *item,const char *value)
{
cJSON *child;
if (*value!='[') {ep=value;return 0;} /* not an array! */

item->type=cJSON_Array;
value=skip(value+1);
if (*value==']') return value+1; /* empty array. */

item->child=child=cJSON_New_Item();
if (!item->child) return 0; /* memory fail */
value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */
if (!value) return 0;

while (*value==',')
{
cJSON *new_item;
if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */
child->next=new_item;new_item->prev=child;child=new_item;
value=skip(parse_value(child,skip(value+1)));
if (!value) return 0; /* memory fail */
}

if (*value==']') return value+1; /* end of array */
ep=value;return 0; /* malformed. */
}

本次博客用于记录协作开发基于socket传图过程中所学内容。

客户端代码

代码框架

接收回调 菜单

创建线程

零拷贝机制

字节序

大小端字节序/结构体对齐/gdb调试

socket

fsync刷新缓冲区 read阻塞

文件描述符

C语言读写文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include<stdio.h>
#include<stdlib.h>
int main()
{
FILE* fp = fopen("./log.txt", "w");
if(fp == NULL){
perror("fopen failed ! \n");
return -1;
}

// int cnt = 10;
// while(cnt --){
// const char * msg = "hello file ! \n";
// fputs(msg, fp);
// }
fprintf(fp, "FILE pointer write pattern ! This is a test file . \n");
fclose(fp);

FILE* fp_read = fopen("./log.txt", "r");
if(fp_read == NULL) {
perror("fopen read failed ! \n");
return -1;
}
char buffer[1024];
while(fgets(buffer, sizeof(buffer), fp_read)){
printf("%s \n", buffer);
}
// if(feof(fp_read)){
// printf("fgets quit normal! \n");
// }
// else{
// printf("fgets quit not normal ! \n");
// }
fclose(fp_read);
return 0;

}

Cpp读取文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <fstream>
#include <string>
int main()
{
std::ofstream out("./log.txt", std::ios::out|std::ios::binary);
if(!out.is_open()){
std::cerr << "open failed ! \n" << std::endl;
return -1;
}
std::string msg = "Hello, this is cpp file descriptor ! \n";
int cnt = 10;

while(cnt --){
/* 函数原型 const char* c_str();
由于c语言没有string类型 使用c_str()将string转化为const char*
*/
out.write(msg.c_str(), msg.size());
}
out.close();
return 0;

}

校验 MD5 CRC

调试

git

  • 合作开发:先git pull 再 git push
  • 在gitignore文件中写*png此时远程仓库若没有任何png文件,中再次上传时会自动忽略
  • git pull遇到conflict 自动合并出现问题需要手动解决冲突,再次add后commit即可pull

磁盘损坏

粘包

gdb生成调试core文件

服务器代码

代码框架

函数原型

在 C 语言中,nanosleep 函数定义在 <time.h> 头文件中,其原型如下:

1
2
3
#include <time.h>

int nanosleep(const struct timespec *reqtp, struct timespec *remtp);

参数

  • **reqtp**:指向 timespec 结构的指针,该结构指定了要延迟的时间。timespec 结构包含两个成员:tv_sec(秒数)和 tv_nsec(纳秒数)。
  • **remtp**:指向 timespec 结构的指针,如果函数被信号中断,该结构将被用来存储未休眠的时间。

返回值

  • 如果函数成功执行,nanosleep 返回 0
  • 如果函数被信号中断,返回一个错误,并将剩余的睡眠时间存储在 remtp 指向的 timespec 结构中。