开放性实验报告题目: 基于ARM的多线程应用程序设计院系名称:电气工程学院专业班级:自动1302学生姓名:张鹏涛学号:201323020219指导教师:张晓东目录1 系统概述与设计要求 (2)1.1 系统概述 (2)1.2 设计要求 (2)2 方案论证 (2)2.1 实现方法 (2)2.2 线程优势 (2)3 硬件设计 (3)3.1 树莓派接口驱动LED电路设计 (3)4 软件设计 (4)4.1 驱动三色LED灯 (4)4.1.1 驱动实现方法 (4)4.1.2 wiringPi库安装和软件编程 (5)4.2 服务器和客户端 (5)4.2.1 服务器设计方法 (5)4.2.2 客户端设计方法 (6)5 系统调试 (6)设计心得 (8)参考文献 (9)附录1(LED驱动程序) (10)附录2(服务器程序) (10)附录3(客户端程序代码) (14)1 系统概述与设计要求1.1 系统概述本系统设计是基于树莓派开发板上实现的,树莓派由注册于英国的慈善组织“Raspberry Pi 基金会”开发,Eben·Upton/埃·厄普顿为项目带头人。
2012年3月,英国剑桥大学埃本·阿普顿(Eben Epton)正式发售世界上最小的台式机,又称卡片式电脑,外形只有信用卡大小,却具有电脑的所有基本功能,这就是Raspberry Pi电脑板,中文译名"树莓派"。
它是一款基于ARM的微型电脑主板,以SD/MicroSD 卡为内存硬盘,卡片主板周围有1/2/4个USB接口和一个10/100 以太网接口(A型没有网口),可连接键盘、鼠标和网线,同时拥有视频模拟信号的电视输出接口和HDMI高清视频输出接口,以上部件全部整合在一张仅比信用卡稍大的主板上,具备所有PC的基本功能。
而树莓派2具有900MHz内核频率,4核ARM Cortex-A7,1GB 内存,带Micro SD 卡插槽(支持通过它启动Linux 操作系统,如Fedora),40PIN接口(可以增加驱动外设)。
本系统设计正式在树莓派2环境下开发实现多线程设计,设计的主要功能就是两个客户端通过服务器互相收发信息。
1.2 设计要求要求多个客户端能够同时连接服务器,而服务器需要创建线程来管理这多个客户端,并且能够把一个客户端发来的数据进行解析,发给另一个客户端,实现两个甚至多个客户端互相收发信息。
能够通过驱动三色灯来发现系统运行的状态,红色说明有错误发生,绿色说明正在正常运行,蓝色说明有用户连接,绿色说明有客户端互相收发信息。
2 方案论证2.1 实现方法要实现服务器同时管理两个甚至多个客户端,就必须引入进程或线程。
2.2 线程优势一是和进程相比,它是一种非常"节俭"的多任务操作方式。
进程是系统中程序执行和资源分配的基本单位。
我们知道,在Linux系统下,启动一个新的进程必须分配给它独立的地址空间,建立众多的数据表来维护它的代码段、堆栈段和数据段,这就导致了进程在进行切换等操作起到了现场保护作用, 这是一种"昂贵"的多任务工作方式。
但是为了进一步减少处理机的空转时间支持多处理器和减少上下文切换开销,进程演化中出现了另外一个概念,这就是线程,也被人称为轻量级的进程。
它是一个进程内的基本调度单位。
线程是在共享的内存空间中并发的多道执行路径,它们共享一个进程的资源,比如文件描述符和信号处理等。
因此,大大减少了上下文切换的开销。
二是线程间方便的通信机制。
对不同进程来说,它们具有独立的数据空间,要进行数据的传递只能通过通信的方式进行,这种方式不仅费时,而且很不方便。
线程则不然,由于同一进程下的线程之间共享数据空间,所以一个线程的数据可以直接为其它线程所用,这不仅快捷,而且方便。
当然,数据的共享也带来其他一些问题,有的变量不能同时被两个线程所修改,有的子程序中声明为static的数据更有可能给多线程程序带来灾难性的打击,这些正是编写多线程程序时最需要注意的地方。
3 硬件设计3.1 树莓派接口驱动LED电路设计图 3.1从图3.1可以知道,要想让三色灯的红色亮起来,首先控制树莓派GPIO.27引脚输出低电平,同理可以控制GPIO.28、GPIO29引脚电瓶来控制绿、蓝LED的启动和关闭。
树莓派开发板上的相关引脚如图3.2。
图 3.24 软件设计4.1 驱动三色LED灯4.1.1 驱动实现方法控制引脚电瓶的高低就能实现控制LED灯的变化,实际上三色LED可以显示无数种颜色,要想超过三种颜色的显示实现,就必须引入PWM。
PWM是模拟脉宽调制来控制输出引脚的实际输出电瓶大小,此系统可以控制引脚从0~3.3V变化来显示不同的颜色。
wiringPi适合那些具有C语言基础,在接触树莓派之前已经接触过单片机或者嵌入式开发的人群。
wiringPi的API函数和arduino非常相似,这也使得它广受欢迎。
作者给出了大量的说明和示例代码,这些示例代码也包括UART设备,I2C设备和SPI设备等。
4.1.2 wiringPi库安装和软件编程首先需要在树莓派上安装wiringPi库,我们选择直接在网上下载安装源码,输入命令:cd ~ 进入根目录下,输入命令:git clone git:///wiringPi 从网上下载源码包,输入命令cd wiringPi 进入安装包目录下,依次输入命令:configure make make install来配置、编译和安装,最后输入命令sudo ./build 来执行编译之后生成的可执行文件,完成安装。
最后输入命令:gpio readall会出来引脚图来确定已经安装成功。
接下来就需要运用库的软件编程来驱动led灯啦,在写C文件时首先要加入库的头文件:#include <wiringPi.h>和C语言必要的头文件:#include <stdio.h>,然后还需要加入实现软件PWM的头文件:#include <softPwm.h>。
接下来我们就需要运用库的API函数wiringPiSetup()初始化wiringPi,若初始化失败会返回-1;然后运用库的API函数softPwmCreate()创建软件PWM,此函数有3个输入参数,分别是控制引脚号,PWM最小值,PWM最大值;运用库的API函数softPwmWrite()写PWM 的值,此函数有2个输入参数,分别是控制那个引脚号,写入的PWM的值,此值需要在最小最大值之间。
完整代码如附录1。
4.2 服务器和客户端4.2.1 服务器设计方法为了方便起见,我们把服务器和客户端都定义在本地IP上进行测试,服务器端的话,首先我们需要把主函数传入的端口号记录下来,并且利用C语言标准函数atoi 转换成整型值。
接着我们定义两个整型数组来存放两个客户端的套接字,然后我们根据端口去创建服务器,创建服务器需要几个函数来实现,第一个就是socket()函数来创建一个服务器的套接字,此函数有3个输入参数,我们选择ipv4协议族,流式Socket,TCP协议类型。
然后根据端口号和本地IP配置服务器,之后就是调用绑定bind(),监听listen()函数来完成服务器的创建。
之后就是根据创建的套接字来进行循环,如果有客户端连接,就保存客户端套接字创建一个线程去处理,此处我们以两个客户端为例来进行操作。
若是套接字编号0发来消息,我们就转发给套接字编号1,若是超过2个的客户端连接进来我们直接关闭创建的线程就行,之后要是有客户端掉线,就把线程和客户端一块销毁。
具体代码实现见附录24.2.2 客户端设计方法首先我们封装三个函数,分别是连接、读数据、写数据,开始就调用连接函数,在连接函数里我们创建客户端并通过输入的服务器ip和端口去连接服务器,然后我们创建两个线程分别是读和写,在读函数里我们不间断读键盘数据并发送给服务器,在写函数里不间断的读服务器发来的数据并显示在屏幕上。
具体代码实现见附录3。
5 系统调试此系统的调试,我们选择Linux虚拟机模拟调试,首先运行服务器和两个客户端,然后客户端连接服务器,之后两个客户端互相收发数据。
为了方便起见,我在PC机上运行和仿真,首先安装虚拟机VMware8.0,然后安装VMware Tools,虚拟机的工具是为了共享电脑上的文件,这样可以在电脑上编写代码,在虚拟机上编译运行。
编译服务器的代码,输入命令gcc server.c -o server.exe -lpthread,回车之后会生成server.exe可执行文件,之后我们运行服务器,输入命令./server.exe 6789,运行服务器,其中6789为输入的端口号。
之后编译客户端代码,输入命令gcc client.c -o client.exe -lpthread,回车之后会生成client.exe可执行文件,我们事先查看虚拟机ip,输入命令ifconfig回车就能看到虚拟机本机ip,为我们客户端连接服务器所用。
之后我们运行客户端一,输入命令./client.exe 192.168.20.109 6789,回车,然后用同样的命令运行客户端二,之后我们在一个客户端输入信息回车,在另一个客户端就能接收到,具体运行结果如图5.1,5.2,5.3。
图 5.1图5.2图5.3设计心得此次开发性试验设计让我收获甚多。
一是要有一个积极的心态,独立解决问题的意识,培养扎实基础的认识。
不要什么东西都感觉跟简单(很多东西可能是看似简单)就不去做了或者不屑一做,以至于性网上搜搜就可以了,这样很不好。
有自己的东西有自己的付出才会有程序运行成功时的喜悦和小自豪,这样也有助于培养自己的兴趣。
要时刻牢记态度决定一切。
其次是兴趣,感觉学习工作中兴趣很关键,只是一个引发人积极性的问题,有了兴趣就自觉了,效率自然就高了。
再次要敢于尝试和挑战。
不要安于现成的程序,而且不要害怕失败,在程序调试的过程中这点尤为重要,“发现出问题然后解决问题”是一个积累经验的过程,而且很高效。
最后要不懈追求。
对于源代码进行不断的完善,要尽可能的实现课题所要求的功能。
对于初学者或者开发较少的人来说,大量大写程序还是有必要的,但同时要注意思考,理解其实现的内在意义。
还可以自己添加一些有意义的功能来实现。
当看到自己编写的程序正常运行时,兴趣也会随之而来,乐此不疲,形成一个良性循环。
短短一周的开放性ARM多线程设计很快结束了,我发现我对嵌入式这个方向、对嵌入式技术、对Linux都有了新的认识。
通过这次的编程,我了解到,要真真正正的掌握计算机程序还不是一件简单容易的事儿,但真正掌握后,它带给我们的将是无穷的便捷与科技,我喜欢高端便捷的生活。
我希望我能做计算机这个万能机器人的主人而不是奴隶,我会努力加油的!参考文献[1]徐千洋.Linux C函数库参考手册.[M]中国青年出版社.2002[2]马忠梅,马广云,徐英慧,田译.ARM嵌入式处理结构与应用基础[M].北京航空航天大学出版社.2002[3]邹思铁.嵌入式Linux设计与应用[M].北京清华大学出版社.2002[4]杜春雷.ARM体系结构与编程[M].清华大学出版社.2003[5]田泽.嵌入式系统开发与应用[M].北京航空航天大学出版社.2005 [11]陈鑫.嵌入式软件技术的现状与发展动向[M].软件世界.2001[6]田泽.嵌入式系统开发与应用实验教程[M].北京航空航天大学出版社.2004[7]Alessandro Rubini,Jonathan Corbet.Linux设备驱动程序[M].中国电力出版社.2002附录1(LED驱动程序)#include <wiringPi.h>#include <softPwm.h>#include <stdio.h>#define uchar unsigned char#define LedPinRed 27#define LedPinGreen 28#define LedPinBlue 29void ledInit(void){softPwmCreate(LedPinRed, 0, 100); softPwmCreate(LedPinGreen,0, 100); softPwmCreate(LedPinBlue, 0, 100);}void ledColorSet(uchar r_val, uchar g_val, uchar b_val){ softPwmWrite(LedPinRed, r_val); softPwmWrite(LedPinGreen, g_val); softPwmWrite(LedPinBlue, b_val);}int main(void){int i;if(wiringPiSetup() == -1){printf("setup wiringPi failed !");return 1;}ledInit();while(1){ledColorSet(0xff,0x00,0x00); //reddelay(500);ledColorSet(0x00,0xff,0x00); //greendelay(500);ledColorSet(0x00,0x00,0xff); //bluedelay(500);}return 0;}附录2(服务器程序)#include <stdio.h>#include <stdlib.h>#include <string.h>#include <signal.h>#include <unistd.h>#include <error.h>#include <errno.h>#include <fcntl.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/epoll.h>#include <netinet/in.h>#include <arpa/inet.h>#include <pthread.h>#define BUFSIZE 1024int socket_client[2];int socket_create(int port) {int st = socket(AF_INET, SOCK_STREAM, 0);int on = 1;if (setsockopt(st, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) == -1){ printf("setsockopt is failed %s\n", strerror(errno));return 0;}struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(port);addr.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(st, (struct sockaddr *) &addr, sizeof(addr)) == -1){printf("zhang.pt:bind is failed %s\n", strerror(errno));return 0;}if (listen(st, 300) == -1){printf("zhang.pt:listen is failed %s\n", strerror(errno));return 0;}return st;}void deliver(int index, const char *buf, ssize_t len){ssize_t rc = 0;if(index == 0){if (socket_client[1] == 0){printf("%d:user not online\n", index);}else{rc = send(socket_client[1], buf, len, 0);printf("send:%s,send:%u bytes\n", buf, rc);if (rc <= 0){if (rc == 0)printf("zhang.pt:send failed, disconnection,\n");elseprintf("zhang.pt:send failed, %s\n", strerror(errno));}}}if(index == 1){if(socket_client[0] == 0){printf("%d:user not online\n", index);}else{rc = send(socket_client[0], buf, len, 0);printf("send:%s,send:%u bytes\n", buf, rc);if(rc <= 0){if (rc == 0)printf("zhang.pt:send failed, disconnection,\n");elseprintf("zhang.pt:send failed, %s\n", strerror(errno));}}}}void socket_work(int index){char buf[BUFSIZE];ssize_t rc = 0;while(1){memset(buf, 0, sizeof(buf));rc = recv(socket_client[index], buf, sizeof(buf), 0);if (rc <= 0){if (rc == 0)printf("%d:recv disconnection\n", index);elseprintf("%d:recv failed, %s\n", index, strerror(errno));close(socket_client[index]);socket_client[index] = 0;break;}else{printf("%d:recv:%s,recv:%u bytes\n", index, buf, rc);deliver(index, buf, rc);}}}void *socket_handle(void *arg){int client_st = *(int *)arg;free((int *)arg);printf("zhang.pt:handle_thread is begin\n");int index = 0;if (socket_client[0] == 0){socket_client[0] = client_st;}else{if (socket_client[1] == 0){socket_client[1] = client_st;index = 1;}else{close(client_st);return NULL;}}socket_work(index);printf("zhang.pt:handle_thread is end\n");return NULL;}void sockaddr_toa(const struct sockaddr_in *addr, char *IPAddr) { unsigned char *p = (unsigned char *)&(addr->sin_addr.s_addr);sprintf(IPAddr, "%u.%u.%u.%u", p[0], p[1], p[2], p[3]);}void socket_accept(int st) {pthread_t thr_d;pthread_attr_t attr;pthread_attr_init(&attr);pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);struct sockaddr_in client_addr;socklen_t len = sizeof(client_addr);while(1){memset(&client_addr, 0, sizeof(client_addr));int client_st = accept(st, (void *)&client_addr, &len);if(client_st == -1){printf("zhang.pt:accept failed %s\n", strerror(errno));break;}else{char sIP[32];memset(sIP, 0, sizeof(sIP));sockaddr_toa(&client_addr, sIP);printf("zhang.pt:accept by %s\n", sIP);int *tmp = malloc(sizeof(int));*tmp = client_st;pthread_create(&thr_d, &attr, socket_handle, tmp);}}pthread_attr_destroy(&attr);}int main(int arg, char *args[]) {if(arg < 2){printf("zhang.pt:server port error!\n");return 0;}int iport = atoi(args[1]);if(iport == 0){printf("zhang.pt:port %d is invalid!\n", iport);return 0;}printf("zhang.pt:server is begin*v*!\n");memset(socket_client, 0, sizeof(socket_client));int st = socket_create(iport);if(st == 0) return 0;socket_accept(st);close(st);printf("zhang.pt:server is end\n");return 0;}附录3(客户端程序代码)#include <stdio.h>#include <stdlib.h>#include <string.h>#include <errno.h>#include <unistd.h>#include <arpa/inet.h>#include <sys/types.h>#include <sys/socket.h>#include <pthread.h>#define BUFSIZE 1024void *socket_read(void *arg){int st = *(int *)arg;char buf[BUFSIZE];while(1){memset(buf, 0, sizeof(buf));ssize_t rc = recv(st, buf, sizeof(buf), 0);if(rc <= 0){printf("zhang.pt:recv failed, %s\n", strerror(errno));break;}else{printf("zhang.pt:recv:%s,recv:%u byte\n", buf, rc);}}return NULL;}void *socket_write(void *arg){int st = *(int *)arg;char buf[BUFSIZE];while(1){memset(buf, 0, sizeof(buf));read(STDIN_FILENO, buf, sizeof(buf));int ilen = strlen(buf);if(buf[ilen - 1] == '\n'){buf[ilen - 1] = 0;}ssize_t rc = send(st, buf, sizeof(buf), 0);printf("zhang.pt:send:%s,send:%u byte\n", buf, rc);if(rc <= 0){printf("zhang.pt:send failed, %s\n", strerror(errno));}}return NULL;}int socket_connect(const char *hostname, int iport){int st = socket(AF_INET, SOCK_STREAM, 0);struct sockaddr_in addr;memset(&addr, 0, sizeof(addr));addr.sin_family = AF_INET;addr.sin_port = htons(iport);addr.sin_addr.s_addr = inet_addr(hostname);if(connect(st, (struct sockaddr *)&addr, sizeof(addr)) == -1){ printf("zhang.pt:connect failed %s\n", strerror(errno));}else{printf("zhang.pt:connect success\n");}return st;}int main(int arg, char *args[]){if(arg < 3){printf("zhang.pt:server ip & port error!\n");return 0;}int iport = atoi(args[2]);if(iport == 0){printf("zhang.pt:port %d is invalid\n", iport);return 0;}printf("zhang.pt:client is begin\n");int st = socket_connect(args[1], iport);if(st == 0) return 0;pthread_t thr_read, thr_write;pthread_create(&thr_read, NULL, socket_read, &st);pthread_create(&thr_write, NULL, socket_write, &st);pthread_join(thr_read, NULL);close(st);printf("zhang.pt:client is end\n");return 0;}。