嵌入式系统原理与应用设计-实验指导书.doc
【精品文档】如有侵权,请联系网站删除,仅供学习与交流嵌入式系统原理与应用设计-实验指导书.精品文档.嵌入式系统原理与应用设计实验指导书山东科技大学电气信息系二00八年十月实验指导书编写说明嵌入式系统原理与应用设计-实验指导书是为配合嵌入式系统原理与设计课程而编写的实验指导书,主要是依据博创公司的ARM9实验箱UP-NETARM2410-S为硬件平台,LINUX操作系统和C基础语言编程为基础工具进行编写。指导书中含有13个实验,主要用来证明嵌入式系统的应用。使用与具有C语言基础和了解LINUX操作系统的学生。实验一 嵌入式实验平台的认识1、实验目的使初学者了解嵌入式教学实验平台UP-NETARM2410-S 的硬件资源,学习演示linux操作系统移植的demo程序,了解嵌入式系统的硬件资源LCD显示、声音驱动、步进电机驱动。重点掌握交叉编译环境下的终端调试方法。2、实验仪器博创科技嵌入式教学实验箱UP-NETARM2410-S和电脑。3、实验内容UP-NETARM2410-S实验箱采用基于ARM9架构的嵌入式芯片S3C2410,主频202MHz ,64MB SDRAM,64MB FLASH,UP-NETARM2410-S主板资源上有 8寸640*480TFT真彩LCD 、触摸屏、4个主USB口、1个从USB口 、2个JTAG接口、一个100M 网卡,预留一个100M网卡 、两个串口、一个485接口 、 CAN总线接口 、红外通信收发器 、8通道10位AD转换模块 、2通道10位DA转换模块等PCMCIA接口 、SD/MMC 接口 、 IDE硬盘接口 、笔记本硬盘接口 、CF卡接口、IC卡接口 、直流电机、步进电机 * 8个用户自定义LED数码管 * 17键键盘 * PS2鼠标、键盘接口。提供软件资源:完整的Linux、WinCE、C/OS-II操作系统移植、bootloader:vivi 、操作系统:linux 2.4.x 。4、实验原理4.1windows下的超级终端方式运行演示程序建立超级终端:运行Windows系统下(以WindowsXP为例)开始所有程序附件通讯超级终端(HyperTerminal)。 在windows xp操作系统下,当初次建立超级终端的时候,会出现如下对话框,请在中打上,并选择“否”。新建一个通信终端: 区号、电话号码等信息随意输入,出现如下所示对话框时,为所建超级终端取名为arm,可以为其选一个图标。单击“确定”按钮。ARM开发平台实际连接的PC机串口选择COM1.端口设置: 波特率为115200,数据位8,无奇偶校验,停止位1,无数据流控制。完成新建超级终端的设置以后,可以选择超级终端文件菜单中的另存为,把设置好的超级终端保存在桌面上,以备后用。用串口线将PC机串口和平台UART0正确连接后,就可以在超级终端上看到程序输出的信息了。 启动实验箱电源打开电源开关,系统会由VIVI开始引导。正常启动时会显示启动信息到“Press Return to start the LINUX now, any other key for vivi”,不进行任何操作等待30S或按回车则启动进入linux系统,按除回车键外的其它键则进入vivi控制台,如图5.3.1。在这里输入“boot”,进入LINUX系统。 是在应用程序目录下,可以通过“ls”查看,如图演示程序都是经过编译后生成的二进制文件,用来测试开发板端口和演示开发板功能演示内容1-播放视频:/mnt/yaffs/iccardcd /mnt/yaffs/mplayer/-带“/”表示绝对路径,但必须从/mnt 开始/mnt/yaffs/mplayerlsmplayer mplayer2 test.avi/mnt/yaffs/mplayermplayer test.avi 看到原始电影/mnt/yaffs/mplayermplayer2 test.avi 看到全频电影根据说明书,实验直流电机工作方式。电机的演示: 打开电机红色按钮开关(1)直流电机:/mnt/yaffscd motor/mnt/yaffs/motorlsDC STEP/mnt/yaffs/motorcd DC/ 注意大小写/mnt/yaffs/motor/DClsdc-motor.o dcm_main/mnt/yaffs/motor/DCinsmod dc-motor.oUsing dc-motor.o s3c2410_dcm_init(159): s3c2410-dc-motordevice initialized/mnt/yaffs/motor/DC./dcm_main 启动直流电机(2)步进电机:/mnt/yaffscd motor/mnt/yaffs/motorlsDC STEP/mnt/yaffs/motorcd STEP/mnt/yaffs/motor/STEPlsexio.o stepmotor/mnt/yaffs/motor/STEPinsmod exio.oUsing exio.o/mnt/yaffs/motor/STEP./stepmotor 启动步进电机(9)播放mp3:/mnt/yaffscd sound/mnt/yaffs/soundls1.wav madplay ttdl.mp3 wavplay wavrec/mnt/yaffs/soundmadplay ttdl.mp3MPEG Audio Decoder 0.14.2 (beta) - Copyright (C) 2000-2001Robert Leslie5、实验思考1)实验过程中目标机、主机、终端机各指什么设备。2)终端显示结果的程序在哪个设备中执行实验二 交叉编译环境设置与终端调试1实验目的:熟悉Linux开发环境,学会基于S3C2410的Linux开发环境的配置和使用,使用Linux的armv4l-unknown-linux gcc编译,使用基于NFS方式的下载调试,了解嵌入式开发的基本过程。2实验仪器硬件:博创科技嵌入式教学实验箱UP-NETARM2410-S、PC机Pentium 500 以上,硬盘10G以上的电脑。软件:WINDOWS XP操作系统、LINUX 9.0操作系统、MINICOM终端调试器、ARM-LINUX开发环境。3、实验原理绝大多数Linux 软件开发都是以native 方式进行的,即本机(HOST)开发、调试,本机运行的方式。这种方式通常不适合于嵌入式系统的软件开发,因为对于嵌入式系统的开发,没有足够的资源在本机(即板子上系统)运行开发工具和调试工具。通常的嵌入式系统的软件开发采用一种交叉编译调试的方式。交叉编译调试环境建立在宿主机(即一台PC 机)上,对应的开发板叫做目标板。运行Linux 的PC【宿主机】开发时使用宿主机上的交叉编译、汇编及连接工具形成可执行的二进制代码(这种可执行代码并不能在宿主机上执行,而只能在目标板上执行),然后把可执行文件下载到目标机上运行。调试时的方法很多,可以使用串口,以太网口等,具体使用哪种调试方法可以根据目标机处理器提供的支持作出选择。宿主机和目标板的处理器一般不相同,宿主机为Intel 处理器,而目标板如UP-NetARM2410-S 开发板为三星S3c2410.GNU 编译器提供这样的功能,在编译器编译时可以选择开发所需的宿主机和目标嵌入式linux 开发,根据应用需求的不同有不同的配置开发方法,但是一般都要经过以下过程:建立开发环境:操作系统一般使用REDHATLINUX,版本7 到9 都可以,选择定制安装或全部安装,通过网络下载相应的GCC 交叉编译器进行安装(比如arm-linux-gcc、arm-uclibc-gcc),或者安装产品厂家提供的交叉编译器。配置开发主机:配置MINICOM,一般参数为波特率115200,数据位8 位,停止位1,无奇偶校验,软硬件控制流设为无。在WINDOWS 下的超级终端的配置也是这样。MINICOM 软件的作用是作为调试嵌入式开发板信息输出的监视器和键盘输入的工具;配置网络,主要是配置NFS 网络文件系统,需要关闭防火墙,简化嵌入式网络调试环境设置过程。建立引导装载程序BOOTLOADER,从网络上下载一些公开源代码的BOOTLOADER,如U-BOOT、BLOB、VIVI、LILO、ARM-BOOT、RED-BOOT 等,根据自己具体芯片进行移植修改。有些芯片没有内置引导装载程序,比如三星的ARM7、ARM9 系列芯片,这样就需要编写烧写开发板上flash 的烧写程序,网络上有免费下载的WINDOWS 下通过JTAG 并口简易仿真器烧写ARM 外围flash 芯片的程序。也有LINUX 下公开源代码的J-FLASH 程序。如果不能烧写自己的开发板,就需要根据自己的具体电路进行源代码修改。这是让系统可以正常运行的第一步。如果你购买了厂家的仿真器当然比较容易烧写flash 了,但是其中的核心技术是无法了解的。这对于需要迅速开发应用的人来说可以极大地提高开发速度。下载别人已经移植好的LINUX 操作系统,如UCLINUX、ARM-LINUX 、PPC-LINUX 等,如果有专门针对你所使用的CPU 移植好的LINUX 操作系统那是再好不过,下载后再添加自己的特定硬件的驱动程序,进行调试修改,对于带MMU 的CPU 可以使用模块方式调试驱动,对于UCLINUX 这样的系统好像只能编译进内核进行调试。建立根文件系统,从 下载使用BUSYBOX 软件进行功能裁减,产生一个最基本的根文件系统,再根据自己的应用需要添加其他的程序。默认的启动脚本一般都不会符合应用的需要,所以就要修改根文件系统中的启动脚本,它的存放位置位于/etc 目录下,包括:/etc/init.d/rc.S、/etc/profile、/etc/.profile 等,自动挂装文件系统的配置文件/etc/fstab,具体情况会随系统不同而不同。根文件系统在嵌入式系统中一般设为只读,需要使用mkcramfs 、genromfs 等工具产生烧写映象文件。建立应用程序的flash 磁盘分区,一般使用JFFS2 或YAFFS 文件系统,这需要在内核中提供这些文件系统的驱动,有的系统使用一个线性flash(NOR 型)512K32M,有的系统使用非线性flash(NAND 型)8512M,有的两个同时使用,需要根据应用规划flash 的分区方案。开发应用程序,可以下载到根文件系统中,也可以放入YAFFS、JFFS2 文件系统中,有的应用程序不使用根文件系统,而是直接将应用程序和内核设计在一起,这有点类似于UCOS-II的方式。烧写内核、根文件系统、应用程序。发布产品。4、实验内容4.1开发环境的设置配置网络,包括配置IP 地址、NFS 服务、防火墙。网络配置主要是要安装好以太网卡,对于一般常见的RTL8139 网卡,REDHAT9.0 可以自动识别并自动安装好,完全不要用户参与,因此建议使用该网卡。然后配置宿主机IP为192.168.0.121。如果是在有多台计算机使用的局域网环境使用此开发设备,IP地址可以根据具体情况设置。如图2.1所示: 图2.1 网络配置双击设备eth0的蓝色区域,进入以太网设置界面,如图2.2,2.3所示:图2.2 以太网常规设置界面图2.3 以太网路由设置界面对于REDHAT9.0,它默认的是打开了防火墙,因此对于外来的IP访问它全部拒绝,这样其它网络设备根本无法访问它,即无法用NFS mount 它,许多网络功能都将无法使用。因此网络安装完毕后,应立即关闭防火墙。操作如下:点击红帽子开始菜单,选择安全级别设置,选中无防火墙。如图2.4 所示:图2.4 安全级别设置在系统设置菜单中选择服务器设置菜单,再选中服务菜单,将iptables服务的勾去掉,并确保nfs选项选中。配置NFS:点击主菜单运行系统设置->服务器设置->NFS 服务器(英文为:SETUP->SYSTEMSERVICE->NFS),点击增加出现如下在界面,在目录(Drictory):中填入需要共享的路径,在主机(Hosts):中填入允许进行连接的主机IP 地址。并选择允许客户对共享目录的操作为只读(Readonly)或读写(Read/write)。如图2.5 所示:图2.5 NFS基本设置图2.6 是对客户端存取服务器的一些其他设置,一般不需要设置,取默认值。图2.6 NFS用户访问设置当将远程根用户当作本地根用户时, 对于操作比较方便,但是安全性较差。最后退出时则完成NFS 配置。配置好后,界面应显示如图2.7 所示:图2.7 远程根用户当作本地根用户图2.8 配置好的NFS *我们也可以手工编写/etc/exports 文件,其格式如下:共享目录 可以连接的主机(读写权限,其他参数)例如:/arm2410s 192.168.0.*(rw,sync)表示将本机的/arm2410s 目录共享给ip 地址为192.168.0.1192.168.0.254 的所有计算机,可以读取和写入。*配置完成后,可用如下办法简单测试一下NFS 是否配置好了:在宿主机上自己mount 自己,看是否成功就可以判断NFS 是否配好了。例如在宿主机/目录下执行:mount 192.168.0.10:/arm2410s /mnt其中192.168.0.10 应为主机的IP 地址。然后到/mnt/目录下看是否可以列出/arm2410s 目录下的所有文件和目录,则可以说明mount 成功,NFS 配置成功。4.2 终端调试1) 新建终端与编写程序新建终端有三种方式:(1) 开始菜单系统设置终端;(2) 在桌面空白位置单击鼠标右键,选择”新建终端”;(3) 单击桌面下面的快速启动栏的新建终端图标. 新建一个终端,在终端的命令行状态下编写程序,与Linux操作环境相同。2)启动NFS服务功能新建一个终端,在新建的终端命令行输入boot/bc/bootservice nfs start启动NFS服务功能3) 建立终端调试环境再新建一个终端,输入minicom.之后打开实验箱.,可以调试实验箱上的程序,程序运行于ARM9 的核心处理器下,编写的程序可以使用。4.3 交叉编译调试环境编程调试实验1)建立工作目录rootroot/arm2410s/exp/basic/# mkdir hellorootarm2410s/exp/basic# cd hello2)编写程序源代码在Linux 下的文本编辑器有许多,常用的是vim 和Xwindow 界面下的gedit 等,在开发过程中推荐使用vim,如果学习vim 的操作方法,请参考相关书籍中的关于vim 的操作指南。 Kdevelope、anjuta 软件的界面与vc6.0 类似,使用它们对于熟悉windows环境下开发的用户更容易上手。hello.c 源代码 如下:include <stdio.h>main()printf(“hello world n”);/这里输入自己实验需要显示的内容我们可以是用下面的命令来编写hello.c 的源代码,进入hello 目录使用vi 命令来编辑代码:root hello# vi hello.c按“i”或者“a”进入编辑模式,将上面的代码录入进去,完成后按Esc 键进入命令状态,再用命令“:wq”保存并退出。这样我们便在当前目录下建立了一个名为hello.c 的文件。3)编写Makefile要使上面的hello.c 程序能够运行,必须要编写一个Makefile 文件,Makefile 文件定义了一系列的规则,它指明了哪些文件需要编译,哪些文件需要先编译,哪些文件需要重新编译等等更为复杂的命令。使用它带来的好处就是自动编译,你只需要敲一个“make”命令整个工程就可以实现自动编译,本次实验只有一个文件,它还不能体现出使用Makefile 的优越性,但当工程比较大文件比较多时,不使用Makefile 几乎是不可能的。下面介绍本次实验用到的Makefile 文件。下面介绍这个Makefile 文件的几个主要部分: CC 指明编译器 EXEC 表示编译后生成的执行文件名称 OBJS 目标文件列表 CFLAGS 编译参数 LDFLAGS 连接参数 all: 编译主入口 clean: 清除编译结果与上面编写hello.c 的过程类似,用vi 来创建一个Makefile 文件并将代码录入其中:root hello# vi Makefile4)编译应用程序在上面的步骤完成后,可以在hello 目录下运行“make”来编译程序了。如果进行了修改,重新编译则运行:root hello# make cleanroot hello# make注意:编译、修改程序都是在宿主机(本地PC 机)上进行,不能在MINICOM 下进行。5)下载调试在宿主PC 计算机上启动NFS 服务,并设置好共享的目录,(前面已经配置完成) 。在建立好NFS 共享目录以后,可以进入MINICOM 中建立开发板与宿主PC 机之间的通讯了。root root# minicom/mnt/yaffs mount -t nfs -o nolock 192.168.0.121:/arm2410s /host注意: IP 地址需要根据宿主PC 机的实际情况修改成功挂接宿主机的arm2410s 目录后,在开发板上进入/host 目录便相应进入宿主机的/arm2410s 目录, 已经给出了编辑好的hello.c 和Makefile 文件, 它们在/arm2410s/exp/basic/01_hello 目录下。用户可以直接在宿主PC 上编译生成可执行文件,并通过上面的命令挂载到开发板上,运行程序察看结果。再进入/host 目录运行刚刚编译好的hello 程序,查看运行结果。/mnt/yaffs cd /host/host ./hellohello world注意:开发板挂接宿主计算机目录只需要挂接一次便可,只要开发板没有重启,就可以一直保持连接。这样可以反复修改、编译、调试,不需要下载到开发板。五、思考题1)Makefile 是如何工作的?其中的宏定义分别是什么意思?实验三 多线程应用程序设计1实验目的:进一步熟悉Linux开发环境,了解多线程程序设计的基本原理,掌握Linux多任务管理程序的开发;学习pthread库函数的使用。2实验仪器硬件:博创科技嵌入式教学实验箱UP-NETARM2410-S、PC机Pentium 500 以上,硬盘10G以上的电脑。软件:WINDOWS XP操作系统、LINUX 9.0操作系统、MINICOM终端调试器、ARM-LINUX开发环境。3、实验原理31 多线程程序的优缺点多线程程序作为一种多任务、并发的工作方式,有以下的优点:1) 提高应用程序响应。这对图形界面的程序尤其有意义,当一个操作耗时很长时,整个系统都会等待这个操作,此时程序不会响应键盘、鼠标、菜单的操作,而使用多线程技术,将耗时长的操作(time consuming)置于一个新的线程,可以避免这种尴尬的情况。2) 使多CPU 系统更加有效。操作系统会保证当线程数不大于CPU 数目时,不同的线程运行于不同的CPU 上。3) 改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独立的运行部分,这样的程序会利于理解和修改。LIBC 中的pthread 库提供了大量的API 函数,为用户编写应用程序提供支持。32 实验源代码与结构流程图本实验为著名的生产者消费者问题模型的实现,主程序中分别启动生产者线程和消费者线程。生产者线程不断顺序地将0 到1000 的数字写入共享的循环缓冲区,同时消费者线程不断地从共享的循环缓冲区读取数据。流程图如图2.2.1 所示:图2.2.1 生产者-消费者实验源代码结构流程图本实验具体代码如下:* The classic producer-consumer example.* Illustrates mutexes and conditions.* by Zou jian guo <ah_zou>* 2003-12-22#include <stdio.h>#include <stdlib.h>#include <time.h>#include "pthread.h"#define BUFFER_SIZE 16/* 设置一个整数的圆形缓冲区 */struct prodcons int bufferBUFFER_SIZE; /* 缓冲区数组 */pthread_mutex_t lock; /* 互斥锁 */int readpos, writepos; /* 读写的位置*/pthread_cond_t notempty; /* 缓冲区非空信号 */pthread_cond_t notfull; /*缓冲区非满信号 */*初始化缓冲区*/void init(struct prodcons * b)pthread_mutex_init(&b->lock, NULL);pthread_cond_init(&b->notempty, NULL);pthread_cond_init(&b->notfull, NULL);b->readpos = 0;b->writepos = 0;/* 向缓冲区中写入一个整数*/void put(struct prodcons * b, int data)pthread_mutex_lock(&b->lock);/*等待缓冲区非满*/while (b->writepos + 1) % BUFFER_SIZE = b->readpos) printf("wait for not fulln");pthread_cond_wait(&b->notfull, &b->lock);/*写数据并且指针前移*/b->bufferb->writepos = data;b->writepos+;if (b->writepos >= BUFFER_SIZE) b->writepos = 0;/*设置缓冲区非空信号*/pthread_cond_signal(&b->notempty);pthread_mutex_unlock(&b->lock);/*从缓冲区中读出一个整数 */int get(struct prodcons * b)int data;pthread_mutex_lock(&b->lock);/* 等待缓冲区非空*/while (b->writepos = b->readpos) printf("wait for not emptyn");pthread_cond_wait(&b->notempty, &b->lock);/* 读数据并且指针前移 */data = b->bufferb->readpos;b->readpos+;if (b->readpos >= BUFFER_SIZE) b->readpos = 0;/* 设置缓冲区非满信号*/pthread_cond_signal(&b->notfull);pthread_mutex_unlock(&b->lock);return data;#define OVER (-1)struct prodcons buffer;void * producer(void * data)int n;for (n = 0; n < 1000; n+) printf(" put->%dn", n);put(&buffer, n);put(&buffer, OVER);printf("producer stopped!n");return NULL;void * consumer(void * data)int d;while (1) d = get(&buffer);if (d = OVER ) break;printf(" %d->getn", d);printf("consumer stopped!n");return NULL;int main(void)pthread_t th_a, th_b;void * retval;init(&buffer);pthread_create(&th_a, NULL, producer, 0);pthread_create(&th_b, NULL, consumer, 0);/* 等待生产者和消费者结束 */pthread_join(th_a, &retval);pthread_join(th_b, &retval);return 0;33 主要函数分析:生产者写入缓冲区和消费者从缓冲区读数的具体流程,生产者首先要获得互斥锁,并且判断写指针+1 后是否等于读指针,如果相等则进入等待状态,等候条件变量notfull;如果不等则向缓冲区中写一个整数,并且设置条件变量为notempty,最后释放互斥锁。消费者线程与生产者线程类似,这里就不再过多介绍了。流程图如下:图2.2.2 生产消费流程图 生产者写入共享的循环缓冲区函数PUTvoid put(struct prodcons * b, int data)pthread_mutex_lock(&b->lock); /获取互斥锁while (b->writepos + 1) % BUFFER_SIZE = b->readpos) /如果读写位置相同pthread_cond_wait(&b->notfull, &b->lock);/等待状态变量b->notfull,不满则跳出阻塞b->bufferb->writepos = data; /写入数据b->writepos+;if (b->writepos >= BUFFER_SIZE) b->writepos = 0;pthread_cond_signal(&b->notempty); /设置状态变量pthread_mutex_unlock(&b->lock); /释放互斥锁 消费者读取共享的循环缓冲区函数GETint get(struct prodcons * b)int data;pthread_mutex_lock(&b->lock); /获取互斥锁while (b->writepos = b->readpos) /如果读写位置相同pthread_cond_wait(&b->notempty, &b->lock);/等待状态变量b->notempty,不空则跳出阻塞。否则无数据可读。data = b->bufferb->readpos; /读取数据b->readpos+;if (b->readpos >= BUFFER_SIZE) b->readpos = 0;pthread_cond_signal(&b->notfull); /设置状态变量pthread_mutex_unlock(&b->lock); /释放互斥锁return data;34主要的多线程API在本程序的代码中大量的使用了线程函数,如pthread_cond_signal、pthread_mutex_init、pthread_mutex_lock 等等,这些函数的作用是什么,在哪里定义的,将在下面的内容中为大家做一个简单的介绍,并且为其中比较重要的函数做一些详细的说明。 线程创建函数:int pthread_create (pthread_t * thread_id, _const pthread_attr_t * _attr,void *(*_start_routine) (void *),void *_restrict _arg) 获得父进程ID:pthread_t pthread_self (void) 测试两个线程号是否相同:int pthread_equal (pthread_t _thread1, pthread_t _thread2) 线程退出:void pthread_exit (void *_retval) 等待指定的线程结束:int pthread_join (pthread_t _th, void *_thread_return) 互斥量初始化:pthread_mutex_init (pthread_mutex_t *,_const pthread_mutexattr_t *) 销毁互斥量:int pthread_mutex_destroy (pthread_mutex_t *_mutex) 再试一次获得对互斥量的锁定(非阻塞):int pthread_mutex_trylock (pthread_mutex_t *_mutex) 锁定互斥量(阻塞):int pthread_mutex_lock (pthread_mutex_t *_mutex) 解锁互斥量:int pthread_mutex_unlock (pthread_mutex_t *_mutex) 条件变量初始化:int pthread_cond_init (pthread_cond_t *_restrict _cond,_const pthread_condattr_t *_restrict _cond_attr) 销毁条件变量COND:int pthread_cond_destroy (pthread_cond_t *_cond) 唤醒线程等待条件变量:int pthread_cond_signal (pthread_cond_t *_cond) 等待条件变量(阻塞):int pthread_cond_wait (pthread_cond_t *_restrict _cond, pthread_mutex_t *_restrict _mutex) 在指定的时间到达前等待条件变量:int pthread_cond_timedwait (pthread_cond_t *_restrict _cond,pthread_mutex_t *_restrict _mutex, _const struct timespec *_restrict _abstime)PTHREAD 库中还有大量的API 函数,用户可以参考其他相关书籍。下面我们对几个比较重要的函数做一下详细的说明:pthread_create 线程创建函数int pthread_create (pthread_t * thread_id,_const pthread_attr_t * _attr,void *(*_start_routine) (void *),void *_restrict _arg)线程创建函数第一个参数为指向线程标识符的指针,第二个参数用来设置线程属性,第三个参数是线程运行函数的起始地址,最后一个参数是运行函数的参数。这里,我们的函数thread 不需要参数,所以最后一个参数设为空指针。第二个参数我们也设为空指针,这样将生成默认属性的线程。当创建线程成功时,函数返回0,若不为0 则说明创建线程失败,常见的错误返回代码为EAGAIN 和EINVAL。前者表示系统限制创建新的线程,例如线程数目过多了;后者表示第二个参数代表的线程属性值非法。创建线程成功后,新创建的线程则运行参数三和参数四确定的函数,原来的线程则继续运行下一行代码。pthread_join 函数 用来等待一个线程的结束。函数原型为:int pthread_join (pthread_t _th, void *_thread_return)第一个参数为被等待的线程标识符,第二个参数为一个用户定义的指针,它可以用来存储被等待线程的返回值。这个函数是一个线程阻塞的函数,调用它的函数将一直等待到被等待的线程结束为止,当函数返回时,被等待线程的资源被收回。pthread_exit 函数一个线程的结束有两种途径,一种是象我们上面的例子一样,函数结束了,调用它的线程也就结束了;另一种方式是通过函数pthread_exit 来实现。它的函数原型为:void pthread_exit (void *_retval)唯一的参数是函数的返回代码,只要pthread_join 中的第二个参数thread_return 不是NULL,这个值将被传递给thread_return。最后要说明的是,一个线程不能被多个线程等待,否则第一个接收到信号的线程成功返回,其余调用pthread_join 的线程则返回错误代码ESRCH。下面我们来介绍有关条件变量的内容。使用互斥锁来可实现线程间数据的共享和通信,互斥锁一个明显的缺点是它只有两种状态:锁定和非锁定。而条件变量通过允许线程阻塞和等待另一个线程发送信号的方法弥补了互斥锁的不足,它常和互斥锁一起使用。使用时,条件变量被用来阻塞一个线程,当条件不满足时,线程往往解开相应的互斥锁并等待条件发生变化。一旦其它的某个线程改变了条件变量,它将通知相应的条件变量唤醒一个或多个正被此条件变量阻塞的线程。这些线程将重新锁定互斥锁并重新测试条件是否满足。一般说来,条件变量被用来进行线线程间的同步。pthread_cond_init 函数条件变量的结构为pthread_cond_t,函数pthread_cond_init()被用来初始化一个条件变量。它的原型为:int pthread_cond_init (pthread_cond_t * cond, _const pthread_condattr_t * cond_attr)其中cond 是一个指向结构pthread_cond_t 的指针,cond_attr 是一个指向结构pthread_condattr_t 的指针。结构pthread_condattr_t 是条件变量的属性结构,和互斥锁一样我们可以用它来设置条件变量是进程内可用还是进程间可用,默认值是PTHREAD_PROCESS_PRIVATE,即此条件变量被同一进程内的各