Embedded

methods of handling multiple connections as lwip

这里分享一些一个服务handle多client连接的经验。 (1) 多线程/进程方法 每accept并创建一个新连接之后,就create一个task来处理这个连接的事务。linux使用fork()来创建分支线程。freertos 不支持fork方法,只能创建新的线程来管理连接。此方法内存开销较大。 (2) non-blocking socket 和 select 此方法由单线程处理并发事务,即最原始的轮询方式。在系统不支持fork()的情况下经常用到。首先,accpet/recv函数都必须的non-blocking的,需立即返回,否则其它任务就得不到轮询。这种模式在跑单片机裸机程序时经常是这样的,另外在labview里面也经常是这样的轮询的方式。这个例程可以在lwip 1.4.1 contrib下app demo:chargen 里可以学习到。这种方式会占用很多cpu资源,这样的任务优先级要放低,并设置一定的轮询间隔(类比在labview里,while循环通常会插入一个等待时间)。否则 freertos 中低优先级任务就得不到运行,同优先级的任务也只能分时间片轮流运行。 这里简单总结下 select方法。 协议栈内核在每次有accept 或 recv事件到达后都会调用 event_callback(); 增加一个事件记录。在上层接收后都会减掉一个记录。select 函数通过查询socket的对应event来置位fd_set对应的比特位。本质就类似查询一个计数信号量。 (3) 使用 raw api, 基于事件触发(基于回调)方式。 此方式适合编写只进行简单处理的应用,每次接收到包就会调用相应的回调函数。多连接是由协议栈的active_pcb_lists直接管理,每个连接都创建属于自己的state argument。如果需要运算量比较大占用时间较长的服务则不适合此方式,会影响的协议栈对其它服务的响应速度。 后记,从事务的角度来看,一个tcp连接代表一个session,一句udp 请求也代表一个session。一个session从接收到返回就是一条事务线。若不能一气呵成,则就必须用一条线程去管理它,或者说维护一个工作现场,等待它的后台任务完成再回来应答这个事务。一条线程就对应一个事务现场,多任务操作系统就是在不同的工作现场的中交替工作,模拟多个人工作的情况,每一个工作现场都保存的对应工作事务的进展情况和所有需要用到的工具。 或者还有一种做法,就是一件事做完一部分后丢给后台,让它处理完之后调用我给它的办法帮我把剩下的事处理完,节省了任务之间信息传递和任务切换带来的开销。 其实一个session对应一个任务确实有点浪费,我想一般handle几百上千万个连接的服务应该不会这样做的,每个连接只需将它当前的状态变量连同数据包传给每个部门来做就可以了,毕竟每个连接的状态的数据量不是很多,没必要开辟一个专门的任务去Handle.

Lwip Netconn

从Lwip Netconn接口的代码中,我们可以学到与Lwip 协议栈规范的交互方式和这种接口的封装方法。 这里总结出一个看代码的技巧,第一步先是只看正常处理的流程,忽略异常处理的部分,这样代码就可以非常简洁,可以很快把代码流程搞清楚,第二步再看重要的异常情况的处理过程。 连接的建立过程前面已经简单总结过,这里就只说我们比较关心的recv_xxx()过程。 recv_xxx() 函数是在accept_func里注册的,并且pcb->callback_arg = new_netconn。 其回调原型: [c] #define TCP_EVENT_RECV(pcb,p,err,ret) \ do { \ if((pcb)->recv != NULL) { \ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\ } else { \ … \ } \ } while (0) [/c] 函数原型: recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err) { struct netconn *conn; u16_t len; conn = (struct netconn *)arg; if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) { … } 这里从tcpip_thread抛出的是pbuf。 netconn 接收部分如下: [c] netconn_recv_data(struct netconn *conn, void **new_buf) { void *buf = NULL; if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) … } [/c] 这里buf 是void 类型的指针,因为上头可能会用pbuf类型或者netbuf类型来接收邮箱里的pbuf。 netbuf的封装方式:

Lwip 里的信号量、邮箱、线程

这里简单分析总结一下Lwip sys_arch.c 中的信号量、邮箱、进程相关的内容。系统用的是FreeRTOS。 [c] “sys_arch.c” /* An array to hold the memory for the available semaphores. */ static sem_t sems[SYS_SEM_MAX]; /* An array to hold the memory for the available mailboxes. */ static mbox_t mboxes[SYS_MBOX_MAX]; “sys_arch.h” ///* A structure to hold the variables for a sys_sem_t. */ typedef struct { xQueueHandle queue; signed char buffer[sizeof(void *) + portQUEUE_OVERHEAD_BYTES]; } sem_t; /* A structure to hold the variables for a sys_mbox_t. */ typedef struct { xQueueHandle queue; signed char buffer[(sizeof(void *) * MBOX_MAX) + portQUEUE_OVERHEAD_BYTES]; } mbox_t; [/c] 这里创建了两个全局结构体数组来管理semaphores and mailboxes.

LwIP TCP Layer Simple analysis

LwIP Version : 1.4.1; 这里分享一些阅读LwIP协议栈的心得。 有个经验: 源代码才是最权威的资料。 若想用raw api编程,可以参考httpd_raw的代码。若涉及进程间通讯,请参考netconn接口,避免在其它进程内调用非Thead-Safe的函数。 下面说说对一些函数及过程的认识。 (1) xxx_accept(void *arg, struct tcp_pcb *pcb, err_t err); 这个函数的参数注意点: 回调原型:pcb->accept(pcb->callback_arg, pcb, err); @*arg: 这里协议栈传递过来的是lpcb,即监听pcb,不要认为和第二个参数相同。因为在tcp_listen_input()里,当有新连接请求到达时会新建一个npcb并将其加入到tcp_active_list里,在初始化npcb时,继承的是lpcb的callback_arg, 而这个callback_arg在新建监听链接时将其赋为lpcb。 @*pcb: 这里是传递进新创建的pcb。在创建APP过程中,需给其分配新的argument, 指定xxx_recv(),xxx_err(), xxx_poll(), xxx_sent()等函数。 (2) struct tcp_pcb *tcp_listen( struct tcp_pcb *pcb); 这里传入了一个original pcb,返回一个lpcb,因为listening状态的pcb 只需包含更少的信息。函数内新分配了一个 lpcb, 拷贝必要信息后,将original pcb释放。 对于SO_REUSE选项,在此函数内对比已经在监听List 中的pcb, 允许本地端口号相同,但本地IP号不同的连接可再次监听。 (3) 服务端口监听过程分析 TCP_LISTEN_BACKLOG 选项 此选项使能 TCP 对监听列表允许监听的数量控制。在tcp_listen_input()函数内, [c] … else if (flags & TCP_SYN) { LWIP_DEBUGF(TCP_DEBUG, (“TCP connection request %“U16_F” -> %“U16_F”.\n”, tcphdr->src, tcphdr->dest)); #if TCP_LISTEN_BACKLOG if (pcb->accepts_pending >= pcb->backlog) { LWIP_DEBUGF(TCP_DEBUG, (“tcp_listen_input: listen backlog exceeded for port %“U16_F”\n”, tcphdr->dest)); return ERR_ABRT; } #endif /* TCP_LISTEN_BACKLOG */ npcb = tcp_alloc(pcb->prio); … #if TCP_LISTEN_BACKLOG pcb->accepts_pending++; #endif … [/c] 当收到SYN帧时,此处对比并限制已经pending 状态的数量,即未被上层accept的连接数量。接下来新建立一个npcb, 并加入active_pcbs 的list中。 tcp_process() 主要处理 active_pcbs 的状态机。经过三次握手协议之后就进入了ESTABLISHED状态。 这里着重分析下tcp_input(pbuf *p, netif *inp);函数。 函数传入的是一个pbuf和netif,传入的参数并无连接的概念,接着在这里就进入了tcp层。tcp层维护着几个list: [c] /** An array with all (non-temporary) PCB lists, mainly used for smaller code size */ struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.

make工具使用

Makefile 正如其名作为一个制作文件的工具,大大方便了进行大批量文件编译时的工作流程。这里简单的记录一个make 工具的基本使用规则。 1. Makefile 基本规则: target... : prerequisites ... command ... ... ------------------------------------------------------------------------------- target: 为目标文件,即所要生成的对象; prerequisites : 为要生成target所需要的文件或是目标; command: 即make需要执行的命令,(任意的shell命令,即对应编译器或者平台的命令)。 第一行表示依赖关系,第二行是规则。( 第二行必须由Tab键开头 )。 注意: a. 只敲入make命令默认执行第一个target。 b. command 要用Tab键缩进 2. 比如有源文件:main.c api.c api.h 用gcc 作编译的过程: gcc -c main.c gcc -c api.c gcc -o main main.o api.o 写成Makefile的话就是: main: main.o api.o gcc -o main main.o api.o main.o: main.c gcc -c main.c api.o: api.c api.h gcc -c api.c 另外Makefile简化格式:

Eclipse ARM IDE 开发环境搭建

一、Eclipse Eclipse的本身只是一个框架平台,但是众多插件的支持,使得Eclipse拥有较好的灵活性。依托于Java 环境运行,所以必须安装 Jre。 二、CDT CDT是Eclipse用于扩展Eclipse支持C/C++开发的插件。可直接下载带CDT的Eclipse。 三、Zylin CDT 支持Eclipse用于嵌入式C/C++开发和远程调试的插件。 四、Yagarto Yagarto是整合了GNU arm的交叉编译工具链,是一个跨平台的 ARM 架构开发平台。他们说了,由于基于MinGW的ToolChain 的GDB 跟Eclipse 配合不是很好,所以Yagarto 出现了。目前Yagarto 项目已经完结。此外Yagrato 建议使用免费的 emIDE (free Visual Studio Style IDE including GNU Tools for ARM) 。其集成了GNU Tools。 五、J-LINK GDB Server A JTAG GDB Debug agent run on Host 六、IDE整体结构框图 安装: 1、安装Java SE 下载地址:http://www.oracle.com/technetwork/java/javase/downloads/jre-7u3-download-1501631.html 设置环境变量: 如果只安装Jre的话就添加 : JAVA_HOME = C:\Program Files\Java\jre1.8.0_65; 若是安装JDK的话就添加 : JAVA_HOME = C:\Program Files\Java\jdk1.8.0_65; Path = %JAVA_HOME%\bin;%JAVA_HOME%\jre\bin; //安装JDK时,JDK内部有个jre目录,外部也默认安装了一个jre目录。一般配置jre环境为内部jre目录。 CLASSPATH = .

从PC的总线到ARM内部总线

从PC的总线到ARM内部总线 来源: ChinaUnix博客 日期: 2009.07.18 23:45 从PC的总线到ARM内部总线 我在学习《Linux设备驱动程序(第3版)》的 第九章 与硬件通信 时,对 I\O总线的概念 不是很清晰,所以查找了有关资料。现在总结如下: 本文的参考资料: 1、 《AMBA、AHB、APB总线简介》 作者 adamzhao 日期 2006-11-16 19:33:00 转载网址: http://www.openhard.org/myspace/blog/show.php?id=75802 2、 《Introduction to AMBA Bus System 》台湾 工研院 / 系統晶片技術中心工程師 吳欣龍,PDF格式 下载地址: http://tpe-wh3.dwins.net/download/member_file/2002/soc/2002-5-1.pdf 3、 《PC架构系列:CPU/RAM/IO总线的发展历史!》 转载网址: http://blog.csdn.net/xport/archive/2006/11/16/1387928.aspx 4、 《认识物理I/O构件- 主机I/O总线(1)》作者:IT168存储频道 2007-04-10 原文网址: http://storage.it168.com/g/2007-04-10/200704101424234.shtml 总线的演变 首先应该讲讲总线的演变历史,这方面《PC架构系列:CPU/RAM/IO总线的发展历史!》这篇文章写得很好!感谢文章的作者!以下内容大量来自这篇文章,可以说是这篇文章的缩减转载。 [#toggle Title="继续阅读”] 公共总线 早期PC中,CPU/RAM/IO都是挂在一条总线上,所有的部件都必须在同步的模式下工作。这样就带来一个"互锁” (locked to each other )效应:所有设备都被限定在一个通用时钟频率(Clock Frequency)上面,整个系统的速度会被系统中最慢的设备限制,系统的整体性能无法提高。 南桥诞生(I\O总线诞生) 1987年,康柏(Compaq)公司想到一个办法: 将系统总线与I/O总线分开,使得2个不同的总线工作在不同的时钟频率上。CPU和内存工作在系统总线上(the System Bus),独立于所有的I/O设备。这样高速的CPU/RAM组件就摆脱了低速I/O设备的束缚。这里的Bridge,就是现在的南桥(South Bridge)芯片的前身,而它实际起到了降频的作用。 倍频出世 从80486开始,CPU的发展迅猛,频率大幅攀升。内存开始变得跟不上CPU的发展步伐了。Intel 于是决定在80486中引入倍频(Clock Doubler)的概念。内存依旧工作在系统总线上,与系统总线保持同样的工作频率,而CPU的内部工作频率(CPU主频)是: CPU 主频 = 外频(系统总线频率System Bus Frequency)* 倍频 (Clock doubler) 北桥和前端总线的诞生 PC结构的变化趋势是把低速设备与高速设备用隔离总线的方法进行隔离。而发展到后来,就演变出了北桥(North Bridge)芯片。内存与北桥间的总线称为内存总线,把CPU与北桥间连接的这段总线成为前端总线**(Front Side Bus,FSB),**也就是系统总线(System Bus)! **PC中的I\O总线** 通过上面的文章,我们知道的总线的演变过程。现在专门来讲讲I\O总线。 总线:用来传送信号或能量的构造器。 系统 I/O 总线将指令从内存传导至与输入\输出处理器(IOP) [url=http://publib.

keil 5.0: pack installer Reading one or more Pack discriptions failed 报错的问题

http://www.keil.com/support/docs/3646.htm 按照链接所说的是Keil5.0不支持这些新版本的DFP,需要换Keil5.01. http://www.keil.com/dd2/pack/ 有点奇怪这里有V1.0版本的DFP为什么导进去也不成功呢? http://www2.keil.com/mdk5/legacy 后来下载了legacy support,包括Cortex M系列和ARM7和ARM9的在Keil4.73版本里面的两个Device 包,这个是Keil5.0支持的。 然后,果断升级了keil5.1. 发现了一个规律,只要在之前破解版的基础上覆盖安装到V5.01都是不需要再破解的直接就是标准版的了,再加上那两个包就能完全兼容老版本支持的设备。。

嵌入式系统分层(转自电子发烧友)

一、嵌入式系统的分层与专业的分类。 嵌入式系统分为4层,硬件层、驱动层、操作系统层和应用层。 1、硬件层,是整个嵌入式系统的根本,如果现在单片机及接口这块很熟悉,并且能用C和汇编语言来编程的话,从嵌入式系统的硬件层走起来相对容易,硬件 层也是驱动层的基础,一个优秀的驱动工程师是要能够看懂硬件的电路图和自行完成CPLD的逻辑设计的,同时还要对操作系统内核及其调度性相当的熟悉的。但 硬件平台是基础,增值还要靠软件。 硬件层比较适合于,电子、通信、自动化、机电一体、信息工程类专业的人来搞,需要掌握的专业基础知识有,单片机原理及接口技术、微机原理及接口技术、C语言。 2、 驱动层,这部分比较难,驱动工程师不仅要能看懂电路图还要能对操作系统内核十分的精通,以便其所写的驱动程序在系统调用时,不会独占操作系统时间片,而导 至其它任务不能动行,不懂操作系统内核架构和实时调度性,没有良好的驱动编写风格,按大多数书上所说添加的驱动的方式,很多人都能做到,但可能连个初级的 驱动工程师的水平都达不到,这样所写的驱动在应用调用时就如同windows下我们打开一个程序运行后,再打开一个程序时,要不就是中断以前的程序,要不 就是等上一会才能运行后来打开的程序。想做个好的驱动人员没有三、四年功底,操作系统内核不研究上几编,不是太容易成功的,但其工资在嵌入式系统四层中可 是最高的。 驱动层比较适合于电子、通信、自动化、机电一体、信息工程类专业尤其是计算机偏体系结构类专业的人来搞,除硬件层所具备的基础学科外,还要对数据结构与算法、操作系统原理、编译原理都要十分精通了解。 3、操作系统层,对于操作系统层目前可能只能说是简单的移植,而很少有人来自已写操作系统,或者写出缺胳膊少腿的操作系统来,这部分工作大都由驱动工 程师来完成。操作系统是负责系统任务的调试、磁盘和文件的管理,而嵌入式系统的实时性十分重要。据说,XP操作系统是微软投入300人用两年时间才搞定 的,总时工时是600人年,中科院软件所自己的女娲Hopen操作系统估计也得花遇几百人年才能搞定。因此这部分工作相对来讲没有太大意义。 4、应用层,相对来讲较为容易的,如果会在windows下如何进行编程接口函数调用,到操作系统下只是编译和开发环 境有相应的变化而已。如果涉及Jave方面的编程也是如此的。嵌入式系统中涉及算法的由专业算法的人来处理的,不必归结到嵌入式系统范畴内。但如果涉及嵌 入式系统下面嵌入式数据库、基于嵌入式系统的网络编程和基于某此应用层面的协议应用开发(比如基于SIP、H.323、Astrisk)方面又较为复杂, 并且有难度了。

S3C2440外围设备(peripherals)

1.它与单片机有啥区别呢? 比我们常见的单片机多了这些东西: External memory controller, LCD controller, AC‘97 CODEC interface, USB Host, Camera interface. 图1 S3C2440外围 图 ARM920T内核