因此,驱动程序的设计,要求开发人员熟悉Linux的内核机制、驱动程序和用户级应用程序的接口关系、系统中对设备的并发操作,同时要求开发人员非常熟悉所开发硬件的工作原理。嵌入式Linux中的设备驱动程序有以下三个方面的特点。......
2023-11-19
互联网(Internet)是目前世界上应用最广泛的网络,最早从美国军方的科研项目ARPA(Advanced Research Projects Agency)发展而来。互联网采用TCP/IP协议并不是ISO规定的标准协议,但是作为应用最广泛的协议已经成为大规模网络通信的事实标准。
TCP/IP协议实际上是由一组协议组成的,通常也称作TCP/IP协议簇。根据ISO/OSI参考模型对网络协议的规定,对网络协议划分为7层,如图10.9所示。
图10.9 TCP/IP协议模型和OSI参考模型对应关系
从图中可以看出,TCP/IP协议簇可以分为4层,和OSI参考模型的对应关系是,TCP/IP的应用层对应OSI的应用层、表示层和会话层;TCP/IP的传输层和网络互联层分别对应OSI参考模型的传输层和网络层;TCP/IP的主机到网络层对应OSI参考模型的数据链路和物理层。
OSI参考模型是一种对网络协议功能划分的一般方法,并不是所有的网络协议都会完全采用这种7层结构,OSI参考模型是为了不同架构网络协议之间的相互转换而设计的。如TCP/IP协议簇和一些电信广域网协议的转换。
TCP/IP协议簇使用了4层结构,对于协议处理的开销相对较小。图10.9中主机到网络层包含了数据链路层信息和物理层信息,这部分在PC上对应的网络接口卡以及驱动;主机到网络层以上的三层协议都是Linux内核实现的。网络互联层也叫做路由层,负责数据包的路径管理,常见的网络设备路由器就工作在这一层;传输层负责控制数据包的传输管理,常见的协议有TCP和UDP协议;应用层是用户最关心的,也是用户数据存放的地方,常见的有HTTP协议、FTP协议等。
1.IP协议
从图10.9中可以看出,IP协议工作在网络层,负责数据包的传输管理。IP协议实现两个基本功能:寻址和分段。寻址是IP协议提供的最基本功能,IP协议根据数据包投中目的地址传送数据报文。在传送数据报文的过程中,IP协议可以根据目的地址选择报文在网络中的传输路径,这个过程叫做路由。
分段是IP协议的一个重要功能。由于不同类型的网络之间传输的网络报文长度是不同的,为了能适应在不同的网络中传输TCP/IP协议报文,IP协议提供分段机制帮助数据包穿过不同类型的网络。IP协议在协议头记录了分段后的报文数据,但是IP协议并不关心数据的内容,如图10.10所示为IPv4协议头部。
图10.10 IPv4协议头部
从图中可以看出这是个复杂的结构,最常用字段是源地址和目的地址,用来寻址和查路由。版本字段永远都是4,表示IPv4协议,生存时间也是一个常用的字段,英文简写为TTL。当发送一个数据包的时候,操作系统会给数据包设置一个TTL值,最大是255。每当数据包经过路由器的时候,路由器会把数据包的TTL值减1,表示经过了一个路由器。如果路由器发现TTL等于0,就把数据包丢弃。使用常用的ping命令测试一个IP是否可达的时候,操作系统会给出一个TTL值,在Linux下系统通常会显示如图10.11所示。
图10.11 Ping命令检测IP到达情况测试图
这里的“ttl=64”就是Linux系统在IPv4协议头部设置的生存时间值。
除了提供寻址和分段外,IP还提供了服务类型、生存时间、选项和报头校验码4种关键业务。服务类型是指希望在IP网络中得到的数据传输服务质量;服务类型是设置服务参数的集合,供网关或者路由器使用;生存时间指定了数据包有效的生存时间,由发送方设置,在路由器中被处理。路由器检查每个数据包的生存时间,如果为0则表示丢弃数据包。IP报文还提供了选项,包括时间戳、安全和特殊路由等设置。此外,还提供了报文头校验码,如果校验码出错表示数据包内容有误,必须丢弃数据包。
需要注意的是,IP协议不提供可靠的传输服务,它不提供端到端的或(路由)结点到(路由)结点的确认,对数据没有差错控制,它只使用报头的校验码,不提供重发和流量控制。如果出错可以通过ICMP报告,ICMP在IP模块中实现。
IP协议最早由于地址大小的限制,只能支持最多232-1个地址。但实际远没有这么多,除掉保留地址和D类E类地址外,供互联网使用的地址很有限。随着接入互联网的设备越来越多,目前已经出现了IP地址危机。
早在20多年前,网络专家就提出改进IP协议的方案,目前IP协议头部版本号是4,称做IPv4,下一代IP协议版本号为6,通常称做IPv6。IPv6技术最大的特点是解决了地址空间问题,提供了128b的地址空间,最多可以有2128-1个地址,足够满足全世界范围内的计算机等设备对IP地址的需求。IPv6技术不仅扩充了地址,还提供了其他的新特性,并且采用了和IPv4协议不同的处理方式,简化了协议头及处理过程。同时IPv6协议还提供了MIP(Mobile IP)支持,为手机以及其他移动设备上网打下了基础。
2.TCP协议
TCP协议是一个传输层协议。如图10.9所示,TCP协议位于网络互联层后,是IP协议的上层协议。TCP是一个面向连接的可靠传输协议。在一个协议栈处理程序中,如果发现数据包的IP层后携带了TCP头,会把数据包交给TCP协议层处理。TCP协议层对数据包排序并进行错误检查,按照TCP数据包头中的序列号排序,如果发现排序队列中少某个数据包,则启动重传机制重新传送丢失的数据包。
TCP协议层处理完毕后,把其余数据交给应用程序处理,如FTP的服务程序和客户程序。面向连接的应用几乎都使用TCP协议作为传输协议。TCP传输协议有高度可靠性,可以最大程度保证数据在传递过程中不丢失。
3.UDP协议
UDP和TCP一样时传输层协议,但是UDP协议没有控制数据包的顺序和出错重发机制。因此,UDP的数据在传输时是不稳定的。通常UDP被用在对数据要求不是很高的场合,如查询应答服务等。使用UDP作为传输层协议的有NTP(网络时间协议)和DNS(域名服务系统)。
UDP另一个重要问题就是安全性不高。由于UDP没有连接的概念,在一个数据传输过程中,UDP数据包可以很容易地被伪造或者篡改。
4.Socket通信基本概念
Socket常被翻译成套接字或者插口,是一个指向传输提供者的句柄。在Linux系统的网络编程中,就是通过操作该句柄来实现网络通信和管理的。根据性质和作用的不同,套接字可以分为3种,即原始套接字、流式套接字和数据包套接字。原始套接字能够使程序开发人员对底层的网络传输机制进行控制,在原始套接字下接收的数据中含有IP头。流式套接字提供了双向、有序、可靠的数据传输服务,该类型套接字在通信前需要双方建立连接,大家熟悉的TCP协议采用的就是流式套接字。与流式套接字对应的是数据包套接字,数据包套接字提供双向的数据流,但是它不能保证数据传输的可靠性、有序性和无重复性,UDP协议采用的就是数据包套接字。
在套接字编程中,套接字接口定义了很多函数,用于套接字编程的创建、打开、连接、数据传入传出等等。接下来对这些函数在Linux中的定义进行介绍。
(1)套接字建立。为了建立套接字,程序可以调用socket()函数,该函数返回一个类似于文件描述符的句柄。socket()函数原型为:
int socket(int domain,int type,int protocol);
参数domain代表所使用的协议族,通常为AF_INET,表示互联网协议族(TCP/IP协议族);参数type指定套接字的类型,type可取值为:SOCK_STREAM(流式套接字)或SOCK_DGRAM(数据包套接字),socket接口还定义了SOCK_RAW(原始套接字),允许程序使用底层协议;参数protocol通常赋值“0”。
(2)套接字配置。通过socket()函数调用返回一个套接字描述符后,在使用套接字进行网络传输以前,必须配置该套接字。面向连接的套接字客户端通过调用connect()函数在套接字数据结构中保存本地信息和远端信息。无连接套接字的客户端和服务端以及面向连接套接字的服务端通过调用bind()函数来配置本地信息。
bind()函数将套接字与本机上的一个端口相关联,随后就可以在该端口下,监听服务请求。bind()函数的定义形式如下:
int bind(int sockfd,const struct sockaddr*my_addr,socklen_t addrlen);
参数sockfd是调用socket()函数返回的套接字描述符;参数my_addr是一个指向包含有本机IP地址及端口号等信息的sockaddr类型的指针;参数addrlen通常被设置为结构体struct sockaddr的长度,即sizeof(struct sockaddr)。
(3)字节优先顺序。计算机数据存储有两种字节优先顺序:高位字节优先和低位字节优先。在互联网上数据以高位字节优先顺序在网络上传输,所以对于在内部是以低位字节优先方式存储数据的机器,在互联网上传输数据时就需要进行转换,否则就会出现数据不一致。
(4)连接建立。面向连接的客户程序使用connect()函数来配置套接字并与远端服务器建立一个TCP连接,该函数的定义形式如下:
int connect(int sockfd,const struct sockaddr*serv_addr,socklen_t addrlen);
参数sockfd是socket()函数返回的套接字描述符;参数serv_addr是包含远端主机IP地址和端口号的指针;参数addrlen是远端地质结构的长度。
(5)监听模式。函数listen()使套接字处于被动的监听模式,并为该套接字建立一个输入数据队列,将到达的服务请求保存在此队列中,直到程序对它们进行处理它们。
int listen(int sockfd,int backlog);
参数sockfd是socket()函数返回的套接字描述符;参数backlog指定在请求队列中允许的最大请求数,进入的连接请求将在队列中等待accept()等系统调用的操作。参数backlog对队列中等待服务的请求的数目进行了限制,大多数系统缺省值为20。如果一个服务请求到来时,输入队列已满,该套接字将拒绝连接请求,客户将收到一个出错信息。
(6)接收请求。accept()函数让服务器接收客户的连接请求。在建立好输入队列后,服务器就调用accept()函数,然后睡眠并等待客户的连接请求int accept(int sockfd,struct sockaddr∗addr,socklen_t∗addrlen);
参数sockfd是socket()函数返回的套接字描述符;参数addr通常是一个指向sockaddr_in变量的指针,该变量用来存放提出连接请求服务的主机的信息;参数addrten通常为一个指向值为sizeof(struct sockaddr_in)的整型指针变量。
函数调用出错时accept()函数返回-1,并置相应的errno值。
(7)数据传输。函数send()和recv()用于在面向连接的套接字上进行数据传输。(www.chuimin.cn)
ssize_t send(int sockfd,const void*msg,size_t len,int flags);
参数sockfd是socket()函数返回的套接字描述符;参数msg是一个指向要发送数据的指针;参数len是以字节为单位的数据的长度;参数flags一般情况下置为0。
send()函数返回实际上发送出的字节数,可能会少于你希望发送的数据。在程序中应该将send()的返回值与想要发送的字节数进行比较。当send()返回值与len不匹配时,应该对这种情况进行处理。
ssize_t recv(int s,void*buf,size_t len,int flags);
参数sockfd是socket()函数返回的套接字描述符;参数buf是存放接收数据的缓冲区;参数len是缓冲的长度。参数flags也被置为0。
函数recv()返回实际上接收的字节数,当出现错误时,返回-1并置相应的errno值。
函数sendto()和recvfrom()用于在无连接的数据报套接字方式下进行数据传输。由于本地套接字并没有与远端机器建立连接,所以在发送数据时应指明目的地址。
(8)结束传输。数据操作结束后,就可以调用close()函数来释放该套接字,从而停止在该套接字上的任何数据操作,该函数的定义形式如下:
int close(int fd);
int shutdow n(int s,int how);
参数sockfd是需要关闭的套接字的描述符;参数how允许为关闭操作选择以下几种方式:
0:不允许继续接收数据
1:不允许继续发送数据
2:不允许继续发送和接收数据
根据数据传送方式,可以把套接字分成面向连接的数据流通信(即基于TCP协议编程的套接字通信)和无连接的数据报通信(即基于UDP协议编程的套接字通信)。
5.面向连接的套接字通信实现
面向连接的数据流通信在TCP/IP协议簇是使用TCP作为传输层协议通信,按照TCP协议的要求,通信双方需要在传输数据前建立连接,术语上称做“TCP的三次握手”。对应用程序员来说,这个过程是透明的,如图10.12所示为面向连接的socket通信模型。
图10.12 面向连接的socket数据流通信
图10.12给出了客户端和服务器端创建socket、建立连接、进行数据通信,以及关闭连接的全过程,同时给出了不同过程和函数对应的TCP/IP协议的层次关系。所有的面向数据流通信都遵循这个过程。
服务器端工作流程如下:
(1)使用socket()函数创建socket;
(2)通过bind()函数把创建的soket()句柄绑定到指定的TCP端口;
(3)调用listen()函数使socket处于监听状态,并且设置监听队列的大小;
(4)客户端发送连接请求后,调用socket()函数接受客户端请求,与客户端建立连接;
(5)与客户端发送或者接收数据;
(6)通信完毕,调用close()函数关闭socket()函数;
客户端工作流程如下:
(1)使用socket()函数创建socket;
(2)调用connect()函数向服务器端socket发起连接;
(3)连接建立后,进行数据读写;
(4)数据传输完毕,使用close()函数关闭socket。
6.无连接的套接字通信实现
无连接的socket通信相对于建立连接的流socket较为简单,因为在数据传输过程中不能保证能否到达,常用的一些对数据要求不高的地方,如在线视频等。无连接的套接字不需要建立连接,省去了维护连接的开销,所以,同样环境下一般比流套接字传输数据率要快。在实际应用中,一些应用软件会自己维护无连接的套接字数据传输状态。无连接的套接字使用TCP/IP协议簇的UDP协议传输数据。
如图10.13所示为无连接的套接字通信模型,和面向连接的流通信不同,服务端在绑定socket到指定IP和端口后,并没有使用listen()函数监听连接,也没有使用accept()函数对新的请求建立连接,因为没有连接的概念,传输层协议无法区分不同的连接,也就不需要对每个新的请求创建连接。在客户端创建socket之后,可以直接向服务端发送数据或者读取服务端的数据。无连接的套接字通信服务端和客户端的界限相对模糊一些。
图10.13 无连接的数据报通信示意图
无连接的套接字通信,发送和接收数据的函数与面向流套接字通信不同,使用revfrom()函数和sendto()函数,定义如下:
#include<sys/types.h>
#include<sys/socket.h>
int recvfrom(int s,void*buf,size_t len,int flags,struct sockaddr*from,socklen_t*from len);
int sendto(int s,const void*msg,size_t len,int flags,const struct sockaddr*to,socklen_t tolen);
recvfrom()函数来用从指定的IP地址和端口接收数据。参数s是套接字句柄:参数buf是存放接收数据的缓冲首地址,len是接收缓冲大小;参数from是发送数据方的IP和端口号,fromlen是sockaddr结构大小。如果接收到数据,就返回接收到数据的字节数,失败则返回-1。
sendto()函数发送数据到指定的IP和端口号。参数s指定套接字句柄:参数msg是发送数据的缓冲首地址,len是缓冲大小;参数to指定接收数据的IP和端口号,tolen是sockaddr结构大小。如果函数调用成功则返回发送数据的字节数,失败返回-1。
无连接的套接字可以在同一个socket与不同的IP和端口收发数据,可以在服务器端管理不同的连接。
有关ARM嵌入式系统原理与应用的文章
因此,驱动程序的设计,要求开发人员熟悉Linux的内核机制、驱动程序和用户级应用程序的接口关系、系统中对设备的并发操作,同时要求开发人员非常熟悉所开发硬件的工作原理。嵌入式Linux中的设备驱动程序有以下三个方面的特点。......
2023-11-19
2.开发模式嵌入式系统本身不具备自举开发能力,即使设计完成以后用户通常也是不能对其中的程序功能进行修改的,必须有一套开发工具和环境才能进行开发,这些工具和环境一般是基于通用计算机上的软硬件设备以及各种逻辑分析仪、混合信号示波器等。表1.2常用嵌入式开发工具......
2023-11-19
Linux软件开发一直在Internet环境下讲行。Linux开发工作经常是在Linux用户决定共同完成一个项目时开始的。由于这个活跃的开发环境,新的以Linux为基础的软件功能日益强大,而且呈现爆炸式的增长态势。许多流行的Linux实用程序如C编译器、shell和编辑器都是GNU软件应用程序。Linux程序需要首先转化为低级机器语言即所谓的二进制代码以后,才能被操作系统执行。......
2023-11-19
具体来说,嵌入式系统是以应用为中心、计算机技术为基础,软硬件可裁剪,适应应用系统对功能、可靠性、成本、体积、功耗严格要求的专用计算机系统。可以这样认为,嵌入式系统是一种专用的计算机系统,作为装置或设备的一部分。通常,嵌入式系统是一个控制程序存储在ROM中的嵌入式处理器控制板。表1.1对比了通用计算机系统与嵌入式系统。......
2023-11-19
每个GPIO端口至少需要两个寄存器,一个是用于控制的“通用I/O端口控制寄存器”,一个是存放数据的“通用I/O端口数据寄存器”。控制和数据寄存器的每一位和GPIO的硬件引脚相对应,由控制寄存器设置每一个引脚的数据流向,数据寄存器设置引脚输出的高低电平或读取引脚上的电平。S5PV210共有237个GPIO端口,分成15组。GPP1:低功率I2S、PCM。GPIO的15组引脚除了作为输入、输出引脚外,一般都还有其他功能,称为引脚复用。图4.6GPIO端口功能框图......
2023-11-19
文件是Linux系统处理数据的基本单位,实际上,Linux系统所有的数据以及其他实体都是按照文件组织的。shell程序使用文件名通配符可以查询符合指定条件的文件名。......
2023-11-19
每组GPIO端口都有两类控制寄存器,分别工作在正常模式和掉电模式。S5PV210处理器工作在正常模式下时,正常寄存器工作;进入掉电模式时,所有配置和上拉/下拉控制由掉电寄存器控制。用于控制GPIO的引脚功能,向该寄存器写入数据来设置相应引脚是输入/输出,还是其他功能。GPIO数据寄存器GPxnDAT。GPIO上拉/下拉寄存器GPxnPUD,用于控制每个端口上拉/下拉电阻的使能/禁止。......
2023-11-19
S5PV210处理器中采用的是异步串行通信方式。异步串行通信通常以字符为单位组成字符帧传送。在串行通信中,数据位的发送和接收分别由发送时钟脉冲和接收时钟脉冲进行定时控制。......
2023-11-19
相关推荐