第一篇:2-嵌入式操作系统实验指导书(2018年5月更新)
《嵌入式操作系统》实验指导书
实验1 Linux编程基础
实验序号:1 适用专业:计算机科学与技术
一、实验目的
1、熟悉Vim的工作模式,熟练使用vim中的常见操作。
2、熟练掌握gcc编译命令及gdb的调试命令,通过对有问题程序的跟踪调试,进一步提高发现问题和解决问题的能力。
3、熟悉多文件的makefile的编写,熟悉各种形式的makefile,并且进一步加深对makefile中用户自定义变量、自动变量的理解。
4、使用autotools生成多文件的makefile,进一步掌握autotools的使用方法。
二、实验内容
1、vim使用练习
(1)在“/root”目录下建一个名为“vim”的目录。(2)进入“vim”目录。
(3)将文件“/etc/inittab”复制到“vim”目录下。(4)使用vim打开“vim”目录下的inittab.(5)设定行号,指出设定initdefault(类似于“id:5:initdefault”)的所在行号。(6)将光标移到该行。(7)复制该行内容。
(8)将光标移到最后一行行首。(9)粘贴复制行的内容。(10)撤销第9步的动作。
(11)将光标移动到最后一行的行尾。(12)粘贴复制行的内容。
(13)光标移到“si::sysinit:/etc/rc.d/rc.sysinit”。(14)删除该行。(15)存盘但不退出。(16)将光标移到首行。
(17)插入模式下输入“Hello,this is vi world!”.(18)返回命令行模式。
(19)向下查找字符串“0:wait”。(20)再向上查找字符串“halt”。
实验名称:Linux编程基础
学 时 数:4学时
(21)强制退出vim,不存盘。
2、用gdb调试程序的bug(1)使用vi编辑器,将以下代码输入到名为greet.c的文件中。此代码的原意为输出倒序main函数中定义的字符串,但结果显示没有输出,代码如下所示。
#include
int main(){
} int display1(char *string){
} int display2(char *string1){
char *string2;int size,i;size=strlen(string1);string2=(char *)malloc(size+1);for(i=0,i (4)使用gdb调试程序,通过设置断点、单步跟踪,一步步找出错误所在。 (5)纠正错误,更改源程序并得到正确的结果。 3、编写包含多文件的makefile(1)用vi在同一目录下编辑两个简单的hello程序,如下所示。#hello.c #include“hello.h” int main(){ } #hello.h #include (3)将上述makefile使用变量替换实现。同样用make验证所编写的makefile是否正确。 (4)编辑另一个makefile,取名为makefile1,不使用变量替换,但用两个目标体实现(也就是首先将hello.c和hello.h编译为hello.o,再将hello.o编译为hello),再用make的“-f”选项验证这个makefile1的正确性。 (5)将上述makefile1使用变量替换实现。 三、实验步骤 根据实验内容要求完成实验。printf(“Hello everyone!n”); 实验2 进程管理实验 实验序号:2 实验名称:进程管理实验 适用专业:计算机科学与技术(嵌入式系统)学 时 数:4学时 一、实验目的 1、通过编写多进程程序,使读者熟练掌握fork()、exec()、wait()和waitpid()等函数的使用,进一步理解在Linux中多进程编程的步骤。 2、通过编写经典的“生产者—消费者”问题的实验,进一步熟悉Linux中的多线程编程,掌握用信号量处理线程间的同步和互斥问题。 二、实验内容 1、编写多进程程序 该实验有3个进程,其中一个为父进程,其余两个是该父进程创建的子进程,其中一个子进程运行“ls-l”指令,另一个子进程在暂停5s之后异常退出,父进程先用阻塞方式等待第一个子进程的结束,然后用非阻塞方式等待另一个子进程的退出,等待收集到第二个子进程结束的信息,父进程就返回。参考流程图如图1所示。 开始 fork()创建两个子进程子进程1父进程阻塞式等待子进程1的结束等待1s子进程2是否结束?运行(调用execlp()执行“ls-l”命令)是子进程2运行(调用sleep函数)结束 图1 多进程实验流程图 2、多线程实验 编写“生产者—消费者”问题的实验,熟悉Linux中的多线程编程。 “生产者—消费者”问题描述如下。有一个有限缓冲区和两个线程:生产者和消费者。他们分别不停地把产品放入缓冲区和从缓冲区中拿走产品。一个生产者在缓冲区满的时候必须等待,一个消费者在 缓冲区空的时候也必须等待。另外,因为缓冲区是临界资源,所以生产者和消费者之间必须互斥执行。 要求使用有名管道来模拟有限缓冲区,并且使用信号量来解决“生产者—消费者”问题中的同步和互斥问题。 使用3 个信号量,其中两个信号量 avail 和 full 分别用于解决生产者和消费者线程 之间的同步问题,mutex 是用于这两个线程之间的互斥问题。其中 avail 表示有界缓冲区中 的空单元数,初始值为 N;full 表示有界缓冲区中非空单元数,初始值为 0;mutex是互斥 信号量,初始值为 1。参考流程图如图2所示。 开始 建立有名管道 打开有名管道 初始化三个信号量 创建消费者和生 产者两个线程 生产者线程 P操作(avail)P操作(mutex)读管道消费者线程 P操作(full)P操作(mutex)写管道 V操作(full)V操作(mutex)结束 V操作(avail)V操作)(mutex 图2 多线程实验流程图 三、实验步骤 1、根据参考流程图编写程序; 2、编译和运行程序代码,并观察运行结果。 实验3 模块编程实验 实验序号:3 实验名称:模块编程实验 适用专业:计算机科学与技术(嵌入式系统)学 时 数:4学时 一、实验目的 1、熟悉模块添加和删除的方法。 2、熟悉字符设备驱动的编写流程。 二、实验内容 要求实现到虚拟设备(一段内存)的打开、关闭、读写的操作,并通过编写测试程序来测试虚拟设备及其驱动运行是否正常。 三、实验步骤 (1)编写代码。 这个简单的驱动程序的源代码如下所示: /*test_drv.c*/ #include { int len;if(count<0) } { } len=strlen(data);count=(len>count)?count:len;if *(copy_to_user(buf,data,count))/*将内核缓冲的数据复制到用户空间*/ { } return count;return –EFAULT;return –EINVAL;/*写函数*/ static ssize_t test_write(struct file *file,const char *buffer,size_t count,loff_t *f_pos){ if(count<0){ } memset(data,0,BUFF_SZ); } /*打开函数*/ static int test_open(struct inode *inode,struct file *file){ printk(“This is open operationn”);/*分配并初始化缓冲区*/ data=(char*)kmalloc(sizeof(char)* BUFF_SZ,GFP_KERNEL);if(!data)count=(BUFF_SZ>count)?count:BUFF_SZ;if(copy_from_user(data,buffer,count))/*将用户缓冲的数据复制到内核空间*/ { } return count;return –EFAULT;return –EINVAL; } { } memset(data,0,BUFF_SZ);return 0;return –ENOMEM;/*关闭函数*/ static int test_release(struct inode *inode,struct file *file){ } /* 创建、初始化字符设备,并且注册到系统*/ static void test_setup_cdev(struct cdev *dev, int minor, struct file_operations *fops) { int err, devno = MKDEV(major, minor);cdev_init(dev, fops);dev->owner = THIS_MODULE;dev->ops = fops; err = cdev_add(dev, devno, 1); if(err){ printk(KERN_NOTICE “Error %d adding test %d”, err, minor);} } /* 虚拟设备的 file_operations 结构 */ static struct file_operations test_fops = { .owner = THIS_MODULE,.read.open }; /*模块注册入口*/ = test_read, = test_open,.write = test_write,.release = test_release, printk(“This is release operationn”);if(data){ } return 0;kfree(data);data=NULL; /*释放缓冲区*/ /*防止出现野指针*/ int init_module(void){ int result;dev_t dev = MKDEV(major, 0);if(major) {/* 静态注册一个设备,设备号先前指定好,并设定设备名,用 cat /proc/devices 来查看*/ } else { } if(result<0){ printk(KERN_WARNING“Test device:unable to get major %dn”,major);return result; } test_setup_cdev(&test_dev, 0, &test_fops);printk(“The major of the test device is %dn”, major);return 0;} /*卸载模块*/ void cleanup_module(void){ cdev_del(&test_dev);unregister_chrdev_region(MKDEV(major, 0), 1);printk(“Test device uninstalledn”);} result=alloc_chrdev_region(&dev,0,1,TEST_DEVICE_NAME);result=register_chrdev_region(dev,1,TEST_DEVICE_NAME); (2)编译代码。 虚拟设备的驱动程序的 Makefile 如下所示: ifeq($(KERNELRELEASE),) KERNELDIR ?= /lib/modules/$(shell uname-r)/build /*内核代码编译路径*/ PWD := $(shell pwd)modules: $(MAKE)-C $(KERNELDIR)M=$(PWD)modules modules_install: $(MAKE)-C $(KERNELDIR)M=$(PWD)modules_install clean: rm-rf *.o *~ core.depend.*.cmd *.ko *.mod.c.tmp_versions.PHONY: modules modules_install clean else obj-m := test_drv.o /* 将生成的模块为 test_drv.ko*/ endif (3)加载和卸载模块。通过下面两个脚本代码分别实现驱动模块的加载和卸载。加载脚本 test_drv_load 如下所示: #!/bin/sh # 驱动模块名称 module=“test_drv” # 设备名称。在/proc/devices 中出现 device=“test_dev” # 设备文件的属性 mode=“664” group=“david” # 删除已存在的设备节点 rm-f /dev/${device} # 加载驱动模块 /sbin/insmod-f./$module.ko $* || exit 1 # 查到创建设备的主设备号 major=`cat /proc/devices | awk “$2==”$device“ {print $1}”` # 创建设备文件节点 mknod /dev/${device} c $major 0 # 设置设备文件属性 chgrp $group /dev/${device} chmod $mode /dev/${device} 卸载脚本 test_drv_unload 如下所示: #!/bin/sh module=“test_drv” device=“test_dev” # 卸载驱动模块 /sbin/rmmod $module $* || exit 1 # 删除设备文件 rm-f /dev/${device} exit 0(4)编写测试代码。 最后一步是编写测试代码,也就是用户空间的程序,该程序调用设备驱动来测试驱动的运行是否正常。以下实例只实现了简单的读写功能,测试代码如下所示: /* test.c */ #include #include int fd, nwrite, nread;char buff[BUFF_SZ];/* 打开设备文件 */ fd = open(TEST_DEVICE_FILENAME, O_RDWR);if(fd < 0){ perror(“open”);exit(1);} do { printf(“Input some words to kernel(enter 'quit' to exit):”);memset(buff, 0, BUFF_SZ);if(fgets(buff, BUFF_SZ, stdin)== NULL){ perror(“fgets”);break;} buff[strlen(buff)-1] = ' ';if(write(fd, buff, strlen(buff))< 0)/* 向设备写入数据 */ { perror(“write”);break;} if(read(fd, buff, BUFF_SZ)< 0){ perror(“read”);break;} else { /*缓冲区*/ /* 从设备读取数据 */ printf(“The read string is from kernel:%sn”, buff);} } while(strncmp(buff, “quit”, 4));close(fd);exit(0);} 四、实验结果 首先在虚拟设备驱动源码目录下编译并加载驱动模块。 $ make clean;make $./test_drv_load $ gcc –o test test.c $./test ;加载模块直接运行脚本文件test_drv_load 接下来,编译并运行测试程序 测试程序运行效果如下: Input some words to kernel(enter 'quit' to exit):Hello, everybody!The read string is from kernel:Hello, everybody!/* 从内核读取的数据 */ Input some words to kernel(enter 'quit' to exit):This is a simple driver The read string is from kernel: This is a simple driver Input some words to kernel(enter 'quit' to exit):quit The read string is from kernel:quit 最后,卸载驱动程序 $./test_drv_unload 卸载模块直接运行脚本文件test_drv_unload 通过 dmesg 命令可以查看内核打印的信息: $ dmesg|tail –n 10 …… The major of the test device is 250 /* 当加载模块时打印 */ This is open operation This is release operation Test device uninstalled /* 当打开设备时打印 */ /* 关闭设备时打印 */ /* 当卸载设备时打印 */ 《操作系统》实验指导书 (适用于计科、网络工程、软件工程、信计专业) 计算机科学与技术学院 2010-5 目录 前言..................................................................................................................................................3 实验 一、进程管理与进程同步.......................................................................................................4 实验 二、存储器管理.......................................................................................................................6 实验 三、磁盘调度算法的设计.......................................................................................................7 实验 四、文件系统原理与模拟实现...............................................................................................8 前言 本课程将系统学习操作系统的基本概念和常用算法以及其发展情况和应用情况。通过本课程的学习,学生应达到如下要求: 1、加深理解操作系统原理。 2、熟悉操作系统的常用算法并完成算法的程序设计。 3、理解当前操作系统的应用前景和新的进展。 本课程主要讲解操作系统的实现原理,如进程管理、进程同步、存储器管理、设备管理和文件系统等。要求学生理解操作系统的基本原理并完成其中多种典型的操作系统的算法的模拟序设计。 学生可以采用任何一种自己熟悉的编程语言完成算法的程序设计,如C/C++、Delphi、VB、VC、C#等。 实验 一、进程管理 实验目的: 理解和掌握进程管理中死锁处理和进程同步的方法。 实验内容: 实现银行家算法、进程调度过程的模拟、读者-写者问题的写者优先算法。 实验步骤: 理解安全性算法和银行家算法的核心机制: 针对3类资源、5个进程的情况,设计相应的数据结构,分别表示每个进程占用各类资源的情况; 编程实现安全性算法函数,编制主函数,动态输入资源的占用情况,进程的资源申请,调用安全性函数,实现银行家算法; 测试:输入可分配和不可分配的请求,测试系统的正确性。 理解进程的三状态调度过程,及各状态间的转换关系; 模拟若干个进程的运行过程,将其存入进程文件中。如:进程1:运行5秒后有3秒的I/O操作,之后有10秒的运行,结束。可以写成:”p1:r5,io3,r3 e;” ; 编程实现调度算法函数,定义时间片大小和并发进程个数,不断从进程文件中读出进程信息,模拟进程的运行及调度过程; 测试:针对进程文件里面的数据为正常、缺项、格式不正确等各种情况,检测程序的执行结果。 设计读者--写者问题的写者优先算法; 学习Windows平台下信号量的API函数的使用;编制读写进程的模拟信息文件,里面包含多个读写进程的运行描述:编制读者--写者问题的写者优先算法,从进程模拟信息文件中取出进程信息,按要求启动对应的进程模拟程序,决定出读者/写者进程的运行次序。 实验结果: 银行家算法程序提供一个用户界面,可以在上边发出资源申请命令,系统应能给出是否可以接受申请,并且有结论输出; 进程调度模拟程序根据一个进程调度文件,模拟进程的各种调度过程,用适合的表达方式表示出来。 写者优先同步控制程序根据一个读写进程模拟信息文件,按照写者优先同步控制过程,用适合的表达方式表示出各读写进程的执行次序。 实验 二、存储器管理 实验目的: 理解各类置换算法的原理和虚拟存储器管理的方法。 实验内容: 编程实现LRU算法或CLOCK/改进算法等置换算法(二选一),模拟实现虚拟存储器的地址变换过程。 实验步骤: 理解LRU或CLOCK改进算法等置换算法; 设计与算法相关的数据结构,如:LRU的堆栈或CLOCK改进算法中的循环结构; 按照最多5块的内存分配情况,编程实现所选算法,动态输入访问内存的块号序列,输出置换结果; 测试:输入合法、非法的访问序列数据,检查程序的正确性和健壮性。 理解虚拟存储器的地址变换过程; 设计用于模拟快表、页表、地址变换所用的寄存器的数据结构; 编制页表的初始信息文件,举例说明文件中具有的信息:共有5块,每块的状态、在内存和外存的起始地址等。 编程实现虚拟存储器地址变换算法程序,动态输入所要访问的逻辑地址,变换过程文字描述以及变换后的物理地址; 测试:输入有效、无效地址,测试程序的正确性和错误处理能力。 实验结果: 置换算法程序提供内存访问序列的输入界面,输出正确的置换过程描述和置换结果; 虚拟地址变换程序提供逻辑地址输入界面,形象地表示出变换成物理地址的过程与最后变换成的物理地址。 实验 三、磁盘调度算法的设计 实验目的: 通过对磁盘调度算法的设计,深入理解提高磁盘访问速度的原理。 实验内容: 模拟实现磁盘调度算法:最短寻道时间优先(SSTF)和扫描(SCAN)算法。 要求: 可以对给出的任意的磁盘请求序列、计算平均寻道长度; 要求可定制磁盘请求序列长度、磁头起始位置、磁头移动方向。 测试:假设磁盘访问序列:98,183,37,122,14,124,65,67;读写头起始位置:53,方向:磁道增加的方向。 实验 四、文件系统原理与模拟实现 实验目的: 了解操作系统中文件系统的结构和管理过程,掌握经典的算法:混合索引与成组链接法等方法。 实验内容: 编程模拟实现混合索引和成组链接法算法; 实验步骤: 模拟混合索引的原理; 假设每个盘块16字节大小,每个盘块号占2字节: 设计支持混合索引算法的索引节点的数据结构;编程模拟实现混合索引算法。 测试:输入一个文件的长度,给出模拟分配占用的磁盘块的情况;输入一个需要访问的地址,计算该地址所在的盘块号。 模拟成组链接法的原理; 设系统具有7个可用磁盘块,每组3块。 编程模拟实现成组链接法。输入请求的磁盘块数,模拟成组链接分配;输入回收的磁盘块号,模拟成组链接回收。 测试:输入请求的磁盘块数,给出分配后的链接情况。输入回收的磁盘块号,给出回收后的链接情况。 实验一 嵌入式开发环境的建立 一、实验目的 通过此实验系统,读者可以了解嵌入式实时操作系统 uC/OS-II 的内核机制和运行原理。本实验系统展示了 uC/OS-II 各方面的管理功能,包括信号量、队列、内存、时钟等。在各个实验中具体介绍了 uC/OS-II 的相关函数。读者在做实验的同时能够结合理论知识加以分析,了解各个函数的作用和嵌入式应用程序的设计方法,最终对整个 uC/OS-II 和嵌入式操作系统的应用有较为清楚的认识。 二、实验步骤 1.安装集成开发环境LambdaEDU 集成开发环境LambdaEDU 的安装文件夹为 LambdaEDU,其中有一个名为“Setup.exe” 的文件,直接双击该文件便可启动安装过程。具体的安装指导请看“LambdaEDU 安装手 册.doc”文件。 当 LambdaEDU 安装完毕之后,我们看到的是一个空的界面,现在就开始一步一步地将 我们的实验项目建立并运行起来。 2.建立项目 为了我们的实验运行起来,需要建立1 个项目基于x86 虚拟机的标准应用项目。通过点 击“文件”、“新建”、“项目”开始根据向导创建一个项目。 在随后出现的对话框中选择“Tool/标准应用项目”,点击下一步,开始创建一个标准的 可执行的应用程序项目。 在随后出现的对话框中填入项目名称“ucos_x86_demo”。点击“下一步”。 选择“pc386 uC/OS-II 应用(x86)”作为该项目的应用框架。点击“下一步” 选择“pc386_elf_tra_debug”作为该项目的基本配置。点击“完成”。 新创建的项目“ucos_x86_demo”将会被添加到项目列表。src 文件夹下保存了该项目中 包含的源文件。ucos2 文件夹中包含了移植到x86 虚拟机的全部代码。init.c 文件是基于ucos2 和本虚拟机的一个应用程序。在进行ucos2 内核实验中,只需要替换init.c 文件,即可。文 件名不限,但是文件名中最好不要使用英文符号和数字以外的其他字符,3.构建项目 到这里,项目配置全部完成。接下来就可以进行构建项目了。 第一次构建本项目,在此项目上点击右键,选择“重建BSP 及项目”。即可开始构建。 之后弹出的对话框显示了构建的进度。可以点击“在后台运行”,以隐藏该对话框 在构建的同时,在右下角的“构建信息”视图输出构建过程中的详细信息: 注:“重新构建”将本项目中的全部源代码进行一次完全的编译和连接,花费时间较多。“构建项目”则仅仅将新修改过的源代码进行编译和连接,花费时间最少。“重建BSP及项 目”,不但要完成“重新构建”的全部工作,另外还要编译与该项目有关的的LambdaEDU 中内置的部分代码,花费时间最多。但是在项目刚建立后,第一次构建时需要选择“重建 BSP 及项目”。以后的构建中选择“重新构建”或“构建项目”即可。另外,在替换了源代 码中的文件后,需要选择“重新构建”来完成该项目的构建。 4.配置虚拟机和目标机代理 (1)制作X86启动盘 在 LambdaEDU 中依次点击“工具”、“Bochs”、“制作虚拟机启动映象”。对启动盘进行一些参数设置后(如下图所示),系统将自动为你生成一个PC 虚拟机的 启动盘映像。 (2)配置虚拟机 选择使用的网络适配器(网卡)后,点击“确定”完成配置。 注意:如果计算机上有多网卡,请将其他网卡停用(包括 VMware 虚拟机添加的虚拟 网卡)。 (3)创建目标机代理 配置好虚拟机后,创建目标机代理:点击LambdaEDU 左下方窗口中绿色的十字符号,在弹出的窗口中选择“基于TA 的连接方式”,并点击“下一步”。 在弹出的“新目标机连接配置中”的这些参数,应该与之前制作启动盘时设置的参数一致。 注意: 名字:输入目标机的名字(缺省是 default),注意如果和现有目标机重名的话,改个名 字。 连接类型:默认选择 UDP IP地址:这里输入目标机(在本实验系统中是虚拟机)的 IP地址; 最后点击“确定”,在目标机管理窗口中,可以看到新增加了一个名为default 的目标机 节点 (4)调试应用 启动虚拟机。 虚拟机启动后的画面如下(其中显示的IP 地址创建虚拟机启动盘时填入的IP 地址)中设置的IP 地址): 在成功完成构建的项目ucos_x86_demo 中的“pc386_elf_tra_debug”上点击鼠标右键,在弹出的菜单中选择“调试”,启动调试器调试生成的程序: 第一次进行调试/运行,需要选择目标机,如下图,选择“Default”,点击“确定”,开 始向目标机(虚拟机)下载应用程序。程序下载完成后,会弹出一个“确认透视图切换”对话框,选择“是”,切换到调试透 视图。 调试的界面如下: 点击绿色的按钮,全速运行。 注意:全速运行后,程序不能够被暂停和停止。 三、实验过程中遇到的问题及体会 在设置IP地址时,要求该IP地址与本计算机在同一个子网中,同时要求该 IP地址没有被网络上其他计算机使用。此外,通过构建开发环境,处次体验到了嵌入式开发工作的乐趣。 实验二 任务的基本管理 一、实验目的 1.理解任务管理的基本原理,了解任务的各个基本状态及其变迁过程; 2.掌握 uC/OS-II 中任务管理的基本方法(创建、启动、挂起、解挂任务); 3.熟练使用 uC/OS-II 任务管理的基本系统调用。 二、实验原理及程序结构 1.实验设计 为了展现任务的各种基本状态及其变迁过程,本实验设计了 Task0、Task1 两个任务: 任务 Task0 不断地挂起自己,再被任务 Task1 解挂,两个任务不断地切换执行。通过本实验,读者可以清晰地了解到任务在各个时刻的状态以及状态变迁的原因。2.运行流程 描述如下: (1)系统经历一系列的初始化过程后进入 boot_card()函数,在其中调用 ucBsp_init()进 行板级初始化后,调用 main()函数; (2)main()函数调用 OSInit()函数对 uC/OS-II 内核进行初始化,调用 OSTaskCreate 创 建起始任务 TaskStart; (3)main()函数调用函数 OSStart()启动 uC/OS-II 内核的运行,开始多任务的调度,执 行当前优先级最高的就绪任务 TaskStart;(4)TaskStart 完成如下工作: a、安装时钟中断并初始化时钟,创建 2 个应用任务; b、挂起自己(不再被其它任务唤醒),系统切换到当前优先级最高的就绪任务Task0。之后整个系统的运行流程如下: t1 时刻,Task0 开始执行,它运行到 t2 时刻挂起自己; t2 时刻,系统调度处于就绪状态的优先级最高任务 Task1 执行,它在 t3 时刻唤醒Task0,后者由于优先级较高而抢占 CPU; Task0 执行到 t4 时刻又挂起自己,内核调度 Task1 执行; Task1 运行至 t5 时刻再度唤醒 Task0; …… 3.µC/OS-Ⅱ中的任务描述 一个任务通常是一个无限的循环,由于任务的执行是由操作系统内核调度的,因此任务是绝不会返回的,其返回参数必须定义成 void。在μC/OS-Ⅱ中,当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的 CPU 使用权就会被抢占,高优先级任务会立刻得到 CPU 的控制权(在系统允许调度和任务切换的前提下)。μC/OS-Ⅱ可以管理多达 64 个任务,但目前版本的μC/OS-Ⅱ有两个任务已经被系统占用了(即空闲任务和统计任务)。必须给每个任务赋以不同的优先级,任务的优先级号就是任务编号(ID),优先级可以从 0 到 OS_LOWEST_PR10-2。优先级号越低,任务的优先级越高。μC/OS-Ⅱ总是运行进入就绪态的优先级最高的任务。4.源程序说明(1)TaskStart任务 TaskStart 任务负责安装操作系统的时钟中断服务例程、初始化操作系统时钟,并创建所 有的应用任务: UCOS_CPU_INIT();/* Install uC/OS-II's clock tick ISR */ UCOS_TIMER_START();/*Timer 初始化*/ TaskStartCreateTasks();/* Create all the application tasks */ OSTaskSuspend(OS_PRIO_SELF); 具体负责应用任务创建的 TaskStartCreateTasks 函数代码如下,它创建了两个应用任务 Task0 和 Task1: void TaskStartCreateTasks(void){ INT8U i; for(i = 0;i < N_TASKS;i++)// Create tasks { TaskData[i] = i;// Each task will display itsown information } OSTaskCreate(Task0,(void *)&TaskData[0], &TaskStk[0][TASK_STK_SIZE1], 6);} TaskStart 任务完成上述操作后将自己挂起,操作系统将调度当前优先级最高的应用任务Task0 运行。(2)应用任务 应用任务 Task0 运行后将自己挂起,之后操作系统就会调度处于就绪状态的优先级最高的任务,具体代码如下: void Task0(void *pdata){ INT8U i;INT8U err;i=*(int *)pdata;for(;;){ printf(“Application tasks switched %d times!nr”,++count); printf(“TASK_0 IS RUNNING..............................................................nr”);printf(“task_1 is suspended!nr”); printf(“**************************************************nr”);err=OSTaskSuspend(5);// suspend itself } } 应用任务 Task1 运行后将 Task0 唤醒,使其进入到就绪队列中: void Task1(void *pdata){ INT8U i;INT8U err;i=*(int *)pdata;for(;;){ OSTimeDly(150); printf(“Application tasks switched %d times!nr”,++count);printf(“task_0 is suspended!nr”);printf(“TASK_1 IS RUNNING..............................................................nr”);printf(“**************************************************nr”);OSTimeDly(150); err=OSTaskResume(5);/* resume task0 */ } } 三、运行及观察应用输出信息 按照本实验手册第一部分所描述的方法建立应用项目并完成构建,当我们在 LambdaEDU 调试器的控制下运行构建好的程序后,将看到在μC/OS-Ⅱ内核的调度管理下,两个应用任务不断切换执行的情形: 四、本实验中用到的µC/OS-Ⅱ相关函数 4.1 OSTaskCreate() OSTaskCreate()建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在 正在运行的任务中建立。中断处理程序中不能建立任务。一个任务必须为无限循环结构,且 不能有返回点。 OSTaskCreate()是为与先前的μC/OS 版本保持兼容,新增的特性在 OSTaskCreateExt()函数中。 无论用户程序中是否产生中断,在初始化任务堆栈时,堆栈的结构必须与 CPU 中断后 寄存器入栈的顺序结构相同。详细说明请参考所用处理器的手册。函数原型: INT8U OSTaskCreate(void(*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio); 参数说明: task 是指向任务代码首地址的指针。 pdata 指向一个数据结构,该结构用来在建立任务时向任务传递参数。 ptos 为指向任务堆栈栈顶的指针。任务堆栈用来保存局部变量,函数参数,返回地址 以及任务被中断时的 CPU 寄存器内容。任务堆栈的大小决定于任务的需要及预计的中断嵌 套层数。计算堆栈的大小,需要知道任务的局部变量所占的空间,可能产生嵌套调用的函数,及中断嵌套所需空间。如果初始化常量 OS_STK_GROWTH 设为 1,堆栈被设为从内存高地址 向 低 地 址 增 长,此时 ptos 应 该 指 向任 务堆 栈 空 间 的 最 高 地 址。反 之,如 果OS_STK_GROWTH 设为 0,堆栈将从内存的低地址向高地址增长。prio 为任务的优先级。每个任务必须有一个唯一的优先级作为标识。数字越小,优先级越高。返回值: OSTaskCreate()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_PRIO_EXIST:具有该优先级的任务已经存在。 OS_PRIO_INVALID:参数指定的优先级大于 OS_LOWEST_PRIO。 OS_NO_MORE_TCB:系统中没有 OS_TCB 可以分配给任务了。注意: 任务堆栈必须声明为 OS_STK 类型。 在任务中必须调用μC/OS 提供的下述过程之一:延时等待、任务挂起、等待事件发生(等待信号量,消息邮箱、消息队列),以使其他任务得到 CPU。用 户 程 序 中 不 能 使 用 优 先 级 0,1,2,3,以 及 OS_LOWEST_PRIO-3, OS_LOWEST_PRIO-2, OS_LOWEST_PRIO-1, OS_LOWEST_PRIO。这些优先级μC/OS 系统 保留,其余的 56 个优先级提供给应用程序。4.2 OSTaskSuspend() OSTaskSuspend()无条件挂起一个任务。调用此函数的任务也可以传递参数 OS_PRIO_SELF,挂起调用任务本身。当前任务挂起后,只有其他任务才能唤醒。任务挂起 后,系统会重新进行任务调度,运行下一个优先级最高的就绪任务。唤醒挂起任务需要调用 函数 OSTaskResume()。 任务的挂起是可以叠加到其他操作上的。例如,任务被挂起时正在进行延时操作,那么 任务的唤醒就需要两个条件:延时的结束以及其他任务的唤醒操作。又如,任务被挂起时正 在等待信号量,当任务从信号量的等待对列中清除后也不能立即运行,而必须等到被唤醒后。函数原型: INT8U OSTaskSuspend(INT8U prio);参数说明: prio 为指定要获取挂起的任务优先级,也可以指定参数 OS_PRIO_SELF,挂起任务本 身。此时,下一个优先级最高的就绪任务将运行。返回值: OSTaskSuspend()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_TASK_ SUSPEND_IDLE:试图挂起 µC/OS-II 中的空闲任务(Idle task)。此为非法操作。 OS_PRIO_INVALID :参数指定的优先级大于 OS_LOWEST_PRIO 或没有设定 OS_PRIO_SELF 的值。 OS_TASK_ SUSPEND _PRIO:要挂起的任务不存在。注意: 在程序中 OSTaskSuspend()和 OSTaskResume()应该成对使用。用 OSTaskSuspend()挂起的任务只能用 OSTaskResume()唤醒。4.3 OSTaskResume() OSTaskResume()唤醒一个用 OSTaskSuspend()函数挂起的任务。OSTaskResume()也是唯一能“解挂”挂起任务的函数。函数原型: INT8UOSTaskResume(INT8U prio);参数说明: prio 指定要唤醒任务的优先级。返回值: OSTaskResume()的返回值为下述之一: OS_NO_ERR:函数调用成功。 OS_TASK_RESUME_PRIO:要唤醒的任务不存在。 OS_TASK_NOT_SUSPENDED:要唤醒的任务不在挂起状态。 OS_PRIO_INVALID:参数指定的优先级大于或等于 OS_LOWEST_PRIO。 五、实验过程中遇到的问题及体会 实验过程中体会到了嵌入式开发的乐趣,对上课老师所讲的内容有了进一步的认识与理解。17 实验三 信号量:哲学家就餐问题的实现 一、实验目的 掌握在基于嵌入式实时操作系统 uC/OS-II 的应用中,任务使用信号量的一般原理。通 过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问。 二、实验原理及程序结构 1.实验设计 掌握在基于嵌入式实时操作系统 uC/OS-II 的应用中,任务使用信号量的一般原理。通 过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问。2.源程序说明 五个哲学家任务(ph1、ph2、ph3、ph4、ph5)主要有两种过程:思考(即睡眠一段时 间)和就餐。每个哲学家任务在就餐前必须申请并获得一左一右两支筷子,就餐完毕后释放 这两支筷子。五个哲学家围成一圈,每两人之间有一支筷子。一共有五支筷子,在该实验中 用了五个互斥信号量来代表。每个任务的代码都一样,如下所示: void Task(void *pdata){ INT8U err;INT8U i;INT8U j; i=*(int *)pdata;j=(i+1)% 5; uC/OS-II 实验指导书 for(;;){ TaskThinking2Hungry(i);OSSemPend(fork[i], 0, &err); OSSemPend(fork[j], 0, &err);/* Acquire semaphores to eat */ TaskEat(i); OSSemPost(fork[j]); OSSemPost(fork[i]);/* Release semaphore */ OSTimeDly(200);/* Delay 10 clock tick */ } } 操作系统配置 修改 uC_OS-II/OS_CFG.h: :: : #define OS_MAX_EVENTS 10 /*最多可以有 10 个事件*/ #define OS_MAX_FLAGS 5 /*最多可以有 5 个事件标志*/ #define OS_MAX_MEM_PART 5 /*最多可以划分 5 个内存块*/ #define OS_MAX_QS 2 /*最多可以使用 2 个队列*/ #define OS_MAX_TASKS 8 /*最多可以创建 8 个任务*/ #define OS_LOWEST_PRIO 14 /*任务优先级不可以大于 14*/ #define OS_TASK_IDLE_STK_SIZE 1024 /*空闲任务堆栈大小*/ #define OS_TASK_STAT_EN 1 /*是否允许使用统计任务*/ #define OS_TASK_STAT_STK_SIZE 1024 /*统计任务堆栈大小*/ #define OS_FLAG_EN 1 /*是否允许使用事件标志功能*/ #define OS_FLAG_WAIT_CLR_EN 1 /*是否允许等待清除事件标志*/ #define OS_FLAG_ACCEPT_EN 1 /*是否允许使用 OSFlagAccept()*/ #define OS_FLAG_DEL_EN 1 /*是否允许使用 OSFlagDel()*/ #define OS_FLAG_QUERY_EN 1 /*是否允许使用 OSFlagQuery()*/ #define OS_MBOX_EN 0 /*是否允许使用邮箱功能*/ #define OS_MEM_EN 0 /*是否允许使用内存管理的功能*/ #define OS_MUTEX_EN 0 /*是否允许使用互斥信号量的功能*/ #define OS_Q_EN 0 /*是否允许使用队列功能*/ #define OS_SEM_EN 1 /*是否允许使用信号量功能*/ #define OS_SEM_ACCEPT_EN 1 /*是否允许使用 OSSemAccept()*/ #define OS_SEM_DEL_EN 1 /*是否允许使用OSSemDel()*/ #define OS_SEM_QUERY_EN 1 /*是否允许使用OSSemQuery()*/ #define OS_TASK_CHANGE_PRIO_EN 1 /* 是 否 允 许 使 用 OSTaskChangePrio()*/ #define OS_TASK_CREATE_EN 1 /*是否允许使用 OSTaskCreate()*/ #define OS_TASK_CREATE_EXT_EN 1 /*是否允许使用 OSTaskCreateExt()*/ #define OS_TASK_DEL_EN 1 /*是否允许使用 OSTaskDel()*/ #define OS_TASK_SUSPEND_EN 1 /* 是 否 允 许 使 用 OSTaskSuspend()and OSTaskResume()*/ #define OS_TASK_QUERY_EN 1 /*是否允许使用 OSTaskQuery()*/ #define OS_TIME_DLY_HMSM_EN 1 /* 是 否 允 许 使 用 OSTimeDlyHMSM()*/ #define OS_TIME_DLY_RESUME_EN 1 /* 是 否 允 许 使 用 OSTimeDlyResume()*/ #define OS_TIME_GET_SET_EN 1 /* 是否允许使用 OSTimeGet()和 OSTimeSet()*/ #define OS_SCHED_LOCK_EN 1 /* 是 否 允 许 使 用 OSSchedLock()和 OSSchedUnlock()*/ #define OS_TICKS_PER_SEC 200 /*设置每秒之内的时钟节拍数目*/ 三、运行及观察应用输出信息 开始,所有的哲学家先处于 thinking 状态,然后都进入 hungry 状态: 后首先获得两个信号量的 1、3 号哲学家开始 eating,待他们释放相关信号量之后,哲 学家 2、5、4 获得所需的信号量并 eating: 应用如此这般地循环执行程序下去„„ 四、本实验中用到的µC/OS-Ⅱ相关函数 4.1 OSSemCreate() OSSemCreate()函数建立并初始化一个信号量。信号量的作用如下: 允许一个任务和其他任务或者中断同步 取得设备的使用权 标志事件的发生 函数原型: OS_EVENT *OSSemCreate((((WORD value))))参数说明: value 参数是所建立的信号量的初始值,可以取 0 到 65535 之间的任何值。返回值: OSSemCreate()函数返回指向分配给所建立的信号量的控制块的指针。如果没有可用的 控制块,OSSemCreate()函数返回空指针。注意: 必须先建立信号量,然后使用。4.2 OSSemPend() OSSemPend()函数用于任务试图取得设备的使用权,任务需要和其他任务或中断同 步,任务需要等待特定事件的发生的场合。如果任务调用 OSSemPend()函数时,信号量 的值大于零,OSSemPend()函数递减该值并返回该值。如果调用时信号量等于零,OSSemPend()函数函数将任务加入该信号量的等待队列。OSSemPend()函数挂起当前 任务直到其他的任务或中断置起信号量或超出等待的预期时间。如果在预期的时钟节拍内信 号量被置起,μC/OS-Ⅱ默认最高优先级的任务取得信号量恢复执行。一个被 OSTaskSuspend()函数挂起的任务也可以接受信号量,但这个任务将一直保持挂起状态直到通过调用 OSTaskResume()函数恢复任务的运行。函数原型: :: : Void OSSemPend(OS_EVNNT *pevent, INT16U timeout, int8u *err);参数说明: :: : pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考 OSSemCreate()函数)。 Timeout 允许一个任务在经过了指定数目的时钟节拍后还没有得到需要的信号量时 恢复就绪状态。如果该值为零表示任务将持续地等待信号量,最大的等待时间为 65535 个时 钟节拍。这个时间长度并不是非常严格的,可能存在一个时钟节拍的误差。 Err 是指向包含错误码的变量的指针。OSSemPend()函数返回的错误码可能为下述几 种: OS_NO_ERR :信号量不为零。 OS_TIMEOUT :信号量没有在指定数目的时钟周期内被设置。 OS_ERR_PEND_ISR :从中断调用该函数。虽然规定了不允许从中断调用该函数,但 µC/OS-Ⅱ仍然包含了检测这种情况的功能。 OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针。返回值: 无 注意: 必须先建立信号量,然后使用。不允许从中断调用该函数。 4.3 OSSemPost() OSSemPost()函数置起指定的信号量。如果指定的信号量是零或大于零,OSSemPost()函数递增该信号量并返回。如果有任何任务在等待信号量,最高优先级的任务将得到信 号量并进入就绪状态。任务调度函数将进行任务调度,决定当前运行的任务是否仍然为最高 优先级的就绪状态的任务。函数原型: INT8U OSSemPost(OS_EVENT *pevent);参数说明: pevent 是指向信号量的指针。该指针的值在建立该信号量时可以得到。(参考 OSSemCreate()函数)。返回值: OSSemPost()函数的返回值为下述之一: OS_NO_ERR :信号量被成功地设置 OS_SEM_OVF :信号量的值溢出 OS_ERR_EVENT_TYPE :pevent 不是指向信号量的指针 注意: 必须先建立信号量,然后使用。4.4 OSTimeDly() OSTimeDly()将一个任务延时若干个时钟节拍。如果延时时间大于 0,系统将立即进 行任务调度。延时时间的长度可从 0 到 65535 个时钟节拍。延时时间 0 表示不进行延时,函 数将立即返回调用者。延时的具体时间依赖于系统每秒钟有多少时钟节拍(由文件 SO_CFG.H 中的常量 OS_TICKS_PER_SEC 设定)。函数原型: void OSTimeDly(INT16U ticks);参数说明: ticks 为要延时的时钟节拍数。返回值: 无 注意: 注意到延时时间 0 表示不进行延时操作,而立即返回调用者。为了确保设定的延时时间,建议用户设定的时钟节拍数加 1。例如,希望延时 10 个时钟节拍,可设定参数为 11。 五、实验过程中遇到的问题及体会 在实验前要对该问题进行深入的理解,即五个哲学家任务(ph1、ph2、ph3、ph4、ph5)主要有两种过程:思考(即睡眠一段时间)和就餐。每个哲学家任务在就餐前必须申请并获得一左一右两支筷子,就餐完毕后释放这两支筷子。五个哲学家围成一圈,每两人之间有一支筷子。只有理解了,才能更好的进行实验。 华北水利水电大学 North China University of Water Resources and Electric Power 嵌入式操作系统课程报告 题目 嵌入式系统课程综和论述 学 院 物理与电子学院 专 业 电子信息工程 姓 名 李天泽 学 号 201816516 组 员 完成时间 2020.12.22 目 录 一、嵌入式系统的介绍 (1)、嵌入式系统的概念……………………………………3 (2)、嵌入式系统的特点……………………………………4 二、嵌入式系统的发展和应用……………………………5 三、总结和心得……………………………………………7 参考文献……………………………………………7 附录…………………………………………………8 摘要: 如今,嵌入式系统经过半个多实际的发展和革新,在各个产业都可以看见它的身影。在电子消费领域,它已经广泛应用于手机、VCD、数字电视和路由器等常见家用电器和电子产品,或许在你的家里有着几十甚至几百个微型嵌入式计算机无时无刻地不在为你服务。 在工业控制方面,一辆豪华轿车的控制系统就包含着至少50个嵌入式微处理器,它们分布于火花塞、传动轴和安全气囊等等。而一架先进的飞机,一台人造卫星就可能包含着几十套嵌入式系统和上百台微型嵌入式计算机,没有这些装载,飞机和卫星的控制系统就不能有效地工作,它们的导航系统就不能满足严格的要求。 在通信领域也有着数不胜数的嵌入式系统的应用,由于带宽网络的发展,交换机、路由器和各种传输设备等都逐渐需要更多的嵌入式系统来满足它们互联的需求,而这些基于32位的嵌入式系统品种多样,绝大多数都价格低廉,能够为企业和家庭的网络选择提供更加廉价而多样的方案。 一、嵌入式系统的介绍 1、嵌入式系统的概念: 上世纪的40年代人类社会诞生了当时最伟大的发明之一——计算机。1946年宾夕法尼亚大学研制出了世界上第一台计算机“ENIAC”,吹响了人类向信息时代进发的号角。如今半个多世纪过去了,总体来看,计算机已经经历了两个大的发展阶段:大型计算机阶段和个人中小型计算机阶段。而今后,计算机技术将迈入下一个充满机遇和挑战的新阶段—— “无处不在的计算机”阶段,即“后PC发展阶段”。“无处不在的计算机”是指在数以千计乃至万计的计算机之间彼此相互关联,其与使用者的比例高达100%,这些计算机中包括有传统的通用式计算机和嵌入式计算机,而后者占绝大多数,可以达到95%的比例。 施乐公司研究中心的主任Mark Weiser 认为:“以长远的发展来看,PC和计算机工作站将逐渐衰落,因为计算机将会变得无处不在,它们会在墙上,在手腕上,在口袋里等等,计算机将会像手写纸一样,随用随取,伸手可得。” 目前全世界范围的计算机研究者都在逐步形成一种共识,那就是在计算机将来的发展中,它必然不会成为像科幻电影中的那种会背叛人类,伤害人类的机械怪物,恰恰相反它们将变得小巧玲珑而且无处不在。它们会出现在任何你能看的见、听得到、摸得着的地方,功能强大而且随处可用,这就是“无处不在的计算机”。 嵌入式计算机系统就是所谓的“看不见的计算机”,一般情况下它只是运行平台,并不能作为独立的开发平台来使用。而且它不能够被用户编程,对用户的I/O接口是专用的。所以不严谨地说:任意包含可编程计算机的设备而且这种设备不是作为通用计算机而设计的都可以称作嵌入式系统。 时至今日嵌入式系统已经逐渐渗透到人们的日常生活中,但因为其不同的应用形式和相异的名称,目前对嵌入式系统还没有一个统一的定义。但一般认为,它有以下概念: (1)嵌入式系统的中心是应用功能,基础是计算机技术,其软件和硬件可以裁剪,对应用系统的功能、可靠性、成本、体积大小和功率损耗都有十分严格的要求和指标。 (2)国际电气和电子工程师协会认为嵌入式系统的定义是“Device used to control, monitor, or assist the operation of equipment , machinery or plants.” (3)嵌入式系统是计算机技术、半导体技术、电子技术等与各个行业的具体应用相结合后的产物,是一个技术集中、资源集中、应用高度分散、技术不断革新的集成系统。 2、嵌入式系统的特点: 1)嵌入式系统通常都是多样的有特定应用功能的软硬件综合体,用于特定的任务,其硬件和软件设计都是高效而简洁的。其中嵌入式软件的应用程序和操作程序是一体化的,不同于传统的通用计算机操作系统和应用程序有着分明的界限。 2)嵌入式系统能够受到多个处理器和体系结构的支持,不同于通用的计算机只能够使用少数的处理器类型和体系结构。目前已经生产有上千种嵌人式微处理器和几十种微处理器的体系结构,其中比较主流常见的有ARM,MIPS, PowerPC,X86和SuperH等。 4)嵌入式系统有实时性和可靠性的特点,其主要表现在:目前绝大多数实时操作系统都是嵌人式系统;嵌人式系统都有实时性的要求,其软件通常都是固化或直接加载到内存中运行的,启动十分快速 另外,嵌人式系统通常都有处错能力和自动复位的功能,目前在绝大多数嵌式系统中都包含着用于保证系统运行可靠性的软硬件处理机制,比如看门狗定时器和内存保护重启机制等。 5)嵌入式系统通常都使用可以适应多种类型处理器、可裁剪量轻、实时性和可靠性高以及可以固化的。同嵌入式微处理器,嵌入式的操作系统也是多种多样的,不仅可以支持多种处理器,还可以进行裁剪量轻来匹配应用的功能,而且规模较小,能够节省资源等等。 二、嵌入式系统的发展和应用 第一代电子计算机体积大,耗电快,而且可靠性和实时性都无法满足嵌入式计算的要求。到了20世纪60年代,由晶体管、磁芯存储制造的第二代计算机开始用于航海航空等领域,它的CPU能够处理从电子系统传来的信号,具有了数据总线的一些基本特性。而与此同时,嵌人式计算机也逐步应用于工业和制造等方面。 至60年代末,采用集成电路的第三代计算机问世,1965年发射Gemini3号是人们第一次使用机载数字计算机。而后的阿波罗探测飞船则使用了嵌人式计算机系统来提供和保障人机的交互功能来用于引导飞行。1963年DEC公司推广了第一台商用小型机,它具有嵌入式系统的结构,具备单总线结构、高速寄存器和实时性、可靠性强的中断系统以及交叉存取功能,标志着嵌入式系统的兴起。 1971 年,英特尔公司成功推出了世界上第一片微处理器Intel 4004。它的体积小、质量轻、价格实惠、使用方便,在当时销量很好,Intel公司将它进一步改进后推出了4位的微处理器4040和8位的8008。 1973-1977 年短短四年之间全球许多厂家推出了各种各样的8位微处理器,其中比较流行的有英特尔公司的8080/ 8085系列,摩托罗拉公司的6800/6802系列,齐洛格公司的Z80和罗克韦尔公司的6502等。这些微处理器的广泛应用为嵌入式系统开辟了广阔的市场,促成了嵌入式系统的快速发展。计算机厂商开始以插件的形式为用户提供所需的OEM产品,并构成符合用户要求的微型控制计算机,嵌入到系统设备中。 嵌入式系统的大发展还要归功于20世纪80年代软件技术的进步。最初的嵌入式计算机的软件都是十分专用的,其程序也只能用汇编语言来编写,因此嵌入式系统的开发周期过长,效率太低,不利于广泛地推广和应用。得益于微电子技术的进步,嵌入式计算机的软件开发不再局限于汇编语言,可以使用C或PL等高级语言,是编程更加多样和简洁化,加快了嵌入式系统的开发效率。时间步入20世纪90年代,当时对分布控制、柔性制造和数字通信电等技术有着巨大需求,而这种需求也刺激着嵌人式系统的软硬技术的革新和发展,促进了嵌入式系统的应用扩大化。 如今,嵌入式系统经过半个多实际的发展和革新,在各个产业都可以看见它的身影。在电子消费领域,它已经广泛应用于手机、VCD、数字电视和路由器等常见家用电器和电子产品,或许在你的家里有着几十甚至几百个微型嵌入式计算机无时无刻地不在为你服务。 在工业控制方面,一辆豪华轿车的控制系统就包含着至少50个嵌入式微处理器,它们分布于火花塞、传动轴和安全气囊等等。而一架先进的飞机,一台人造卫星就可能包含着几十套嵌入式系统和上百台微型嵌入式计算机,没有这些装载,飞机和卫星的控制系统就不能有效地工作,它们的导航系统就不能满足严格的要求。 在通信领域也有着数不胜数的嵌入式系统的应用,由于带宽网络的发展,交换机、路由器和各种传输设备等都逐渐需要更多的嵌入式系统来满足它们互联的需求,而这些基于32位的嵌入式系统品种多样,绝大多数都价格低廉,能够为企业和家庭的网络选择提供更加廉价而多样的方案。 时至今日,嵌入式系统的应用已经从微至著,广泛分布。但它还在不断地更新发展,其构成的计算机也会变得更小巧,更灵敏,更高效,更智能,相信在未来的某一天,会如Mark Weiser所说的那样:“它们会在墙上,在手腕上,在口袋里,就像手写纸一样,随用随取,伸手可得。” 三、总结和心得 嵌入式系统作为一门计算机开发的学科,有着不同于传统通用计算机系统的特点和概念,具有独特长处。同时它的应用遍布于电子消费、通信工程、工业控制和军事国防等多种领域,其发展前景是非常广阔的,是一门十分值得深入学习和研究的学科。 通过一个学期的学习,我了解了许多嵌入式实时操作系统的应用知识,比如“任务的管理和调度”、“同步、互斥与通信”以及“中断和时间管理”等全新的理论知识,同时也通过多次的实验操作理解了对嵌入式系统软硬件工作方式和应用。所谓温故而知新,通过撰写课程报告,我对嵌入式系统的各个方面有了新的了解,大大提高了对这门学科的兴趣,在今后的学习中,我也会更加深入地去学习这门课程的相关内容,丰富自己的知识领域,开阔眼界,掌握更多的技能,为自己将来的发展做好铺垫。 参考文献: 甄鹏------《嵌入式实时Linux的移植及应用技术的研究》2008,(02) 郭军------《基于Petri网的嵌入式系统高层级设计方法与技术研究》2007,(04) 吴敏------《基于嵌入式的家庭网关控制平台的研究与设计》2007,(05) 刘青云;焦铬-------《嵌入式Web Service模型实现及应用》2019,(01) 田婧---------《嵌入式μCOSⅡ在DSP中的移植与应用研究》2007,(04) 附录 附查重报告: 操作系统实验 实验一 Linux常用命令实验 一.目的和要求 本实验的目的是熟悉Linux操作系统的命令接口、图形接口和程序接口;了解Linux操作系统的启动过程;了解Linux操作系统的目录结构;用vi编辑器编写简单的C语言程序,并用gcc编译器编译、运行。 二.实验内容 1、实现开机、登录、退出与关机: (1)如果以root用户登录,则命令窗口的提示符为#;如果以普通用户登录,则命令窗口的提示符为$;登陆用户名:user 密码:123456(2)修改口令(修改口令操作不做):成功进入系统后,在命令提示符后输入“passwd”并键入回车键 (3)退出帐号:命令方式下:logout(4)关机或重启: 命令方式下:halt或reboot 窗口方式下:“桌面”->“注销” 2、掌握的基本常用命令列表 (1)关于目录的操作命令:cd、ls、mkdir、rmdir、pwd等; (2)关于文件的操作命令:cat、find、man/help、vi/vim、cp、rm、mv、dd、du、df、chmod、ln等; (3)关于进程管理的操作命令:ps、kill、top、free 等; (4)关于系统管理的操作命令:whoami、passwd、adduser/useradd、addgroup、userdel、groupdel、su、who、Ctrl+Alt+Fn(n=1、2、3、4、5、6)(在X-Window界面下切换到字符界面,重新登录,Ctrl+Alt+F7返回图形界面)、Alt+Fn(n=1、2、3、4、5、6)(在6个虚拟终端之间切换)等; (5)安装和卸载文件系统:mount、umount等; (6)显示有关计算机系统信息的命令:uname(显示操作系统的名称)、uname –n(显示系统域名)、uname –p(显示系统的CPU名称) (7)其它命令:time、date、cal 等。 3、阅读/etc/inittab 文本文件,思考问题:如果要求启动Linux系统之后进入字符 1 操作系统实验 界面,应如何修改/etc/inittab文件?用户应具有什么权限? 4、切换到不同的虚拟终端,登录到Linux系统 5、vi 编辑器的使用(1)进入和退出vi(2)利用文本插入方式建立一个文件(3)在新建的文本文件上移动光标。 (4)对文本文件执行删除、复原、修改、替换操作。 6、熟悉gcc编译环境:编写一个C语言程序myfile1.c,求1~100中偶数的和,编译并运行。 (1)编译 gcc myfile1.c 运行./a.out(2)编译 gcc –o myfile1 myfile1.c 运行./myfile1 7、编写一个C语言程序myfile2.c,显示字符串“Hello, Linux!”,并将其反向输出。 8、熟悉Linux系统的目录结构,使用命令或者编写C语言程序报告Linux内核的行为。 报告以下内容: CPU类型和型号 内核版本 从系统最后一次启动以来经历了多长时间?形式为dd:hh:mm:ss 当前配置的内存数量 当前可用内存数量 自系统启动以来,发生的所有的中断的次数 从系统启动开始创建的进程数 内核执行的上下文转换的次数 三.实验提示 1、Linux安装 (1)安装前的准备工作 <1>.基本的硬件配置 由于安装涉及到各种硬件的设置,所以在安装前必须了解机器各种硬件的型号,硬盘的使用情况,内存的大小,鼠标的类型及接口,声卡,网卡,显卡,显示器的型号。 操作系统实验 <2>.有关网络的信息 IP地址,掩码,网关IP地址,域名服务器IP地址,域名,宿主机名。<3>.安装方式的选择 •从CD-ROM安装 •从FTP站点安装 •从NFS服务器安装 •从硬盘安装 硬盘分区 硬盘空间必须和计算机上安装的其他操作系统所使用的硬盘空间分开。特别要注意,如果硬盘空间很大,切忌不能将Linux装在8G以后。安装Red Hat Linux至少需要两个硬盘分区:一个或多个“Linux native”类型的分区,一个“Linux swap”类型的分区 分区命名设计Linux 通过字母和数字的组合来表示硬盘分区。 前两个字母-----分区名的前两个字母表明分区所在设备的类型。hd指IDE硬盘,sd指SCSI硬盘。 下一个字母-----分区在哪个设备。例如,/dev/hda(第一个IDE硬盘),/dev/sdb(第二个SCSI硬盘)。 数字-----代表分区。前四个分区(主分区或扩展分区)用数字1到4表示。逻辑分区从5开始。例如, 若IDE硬盘在安装Linux前安装了Windows系统并划分了C盘和逻辑分区D盘,那么D盘就是/dev/hda5, /dev/hda5表示第一个硬盘的第一个逻辑分区。 对于Linux初学者来说,为Linux分两个区(根分区和交换分区)是比较简单方便的。 一个交换分区:用来支持虚拟内存。一个根分区:根分区是/(根目录)的所在地,其中包含启动系统所需的文件和系统配置文件。这个分区要足够大。 一个/usr分区: /usr是Linux系统许多软件所在的地方。一个/home分区:这是用户的主目录所在地。(2)开始安装 注意点:我们一般选择的是图形化的安装方式。它的主要部分是相同的。 可能会在安装完成后第一次启动时才进行网卡的检测。 操作系统实验 在选择图形化界面时,有两种方式gnome和kde;它们各有优缺点。 系统会让你选择启动时是图形化方式,还是字符方式。请大家选择字符方式。 在选择防火墙的时候,在安装时请先不用防火墙。 图形化安装方式下,不能选择启动时的开启服务。可在系统安装完成后用setup命令进行修改。 2、进入Linux(1)登录 第一次登录系统,必须作为“root”登录。这个帐号对系统的一切都有完全的访问权限。 在login:提示符处输入root。按[Enter](或[Return]键).会出现Password提示。输入口令,应该看到类似以下的信息: [root@localhost /root] #(2)退出 输入[Ctrl]-[D](3)帐号和口令 <1>.帐号 创建新的帐号有几种方法,最基本的方法:useradd命令.[root @ localhost / root] # useradd Tom [root @ localhost / root] # <2>.口令 passwd 命令可以用来: 为新创建的用户分配口令。 修改已存在的用户的口令。 修改登录的用户的口令。此时必须以root登录。如: [root @ localhost / root]# passwd Tom New UNIX password: Retype new UNIX password: passwd:all authentication tokens updated successfully 4 操作系统实验 [root @ localhost / root]# 用新帐户登录: Red Hat Linux release 7.1(Manhattan) Kernel 2.0.34 on an i586 login: Tom Password: [Tom@ localhost Tom] $ <3>.su 命令 用su,当前的登录段能变成root(或其他用户)的登录段。如: [Tom@ localhost Tom] $ su Password: [root@ localhost Tom] # 也可以用su变成其他用户。这时,必须作为root运行su,给出用户名。<4>.关闭系统 关闭系统时,必须告诉所有的进程结束运行,使用shutdown命令。且只能由root 运行,格式是: shutdown -h-------在关闭完成后(Halt)停止系统。 -r--------在关闭完成后重启动(Reboot)系统。 3、vi 编辑器的使用(1)进入和退出vi <1>进入vi 在系统提示符($)下输入命令vi和想要编辑(建立)的文件名(如example),便可进入vi。 <2>退出vi 在命令方式下可有几种方法退出vi编辑器: :wq 把编辑缓冲区的内容写到正在编辑的文件中,退出编辑器,回到Linux shell下。 :ZZ 仅当作过修改时才将缓冲区内容写到文件上。 操作系统实验 :x 与 :ZZ 相同。 :q!强行退出vi。感叹号(!)告诉vi,无条件退出,丢弃缓冲区内容。这样,先前对该文件所做的修改或输入都被抛弃。(2)新建文件 <1>在Linux提示符$之后,输入命令 :vi myfile,然后按〈Enter〉键。<2>输入插入命令i(屏幕上看不到字符i)。<3>然后,输入以下文本行: To the only book tht I, For mang year you have been my favourite book <4>发现这两行有错,进行改正: 按〈Esc〉键,从插入方式回到命令方式。按光标上移键,使光标移到第一行。 按光标左移键,使光标移到“tht”的第二个“t”处。 输入i(这是插入命令),然后输入a。该行变成如下形式: To the only book that I, 按光标右移键,使光标移到“I”上。 我们想在“I”之后输入一个空格和单词“like”。为此,输入附加命令“a”。结果屏幕显示为: To the only book that a I,没有出现预期的效果......原来是:我们先前使用了插入命令i,至今并未用〈Esc〉键返回命令方式。所以,输入的所有字符都作为输入文本予以显示。<5>按〈Esc〉键,返回命令方式。 利用x命令删除错误字符。然后,进入插入方式,输入正确字符。<6>最后输入如下所示的文本: To the only book that I like, For many year you have been my favourite book I liveeyou all the time and could not have picked much better.<7>将编辑的文本文件存盘。(利用“:wq”命令,或者“:x”命令)<8>重新进入vi编辑程序,编辑上面的文件。(如:$ vi myfile) 操作系统实验 <9>在屏幕上见到myfile文件的内容。在屏幕底边一行显示出该文件的名称、行数和字符个数:“myfile”4 lines,130 characters 它仍然有错,需进一步修改。 <10>将光标移到第二行的year的r处。输入a命令,添加字符s。 <11>按〈Esc〉,回到命令方式。输入命令10〈Space〉,光标移至何处?---光标右移10个字符位置。 <12>利用取代命令r将liveeyou改为live you。 <13>将光标移至第三行。输入新行命令O(大写字母),屏幕上有什么变化?---光标移至上一行(新加空行)的开头。<14>输入新行的内容: We've been through much together 此时,vi处于哪种工作方式? <15>按〈Esc〉,回到命令方式。将光标移到第四行的live的v字母处。利用替换命令s将v改为k。 <16>在第四行的you之后添加单词very much。<17>修改后的文本是以下内容: To the only book that I like, For many years you have been my favourite book We've been through much together I like you very much all the the time and could not have picked much better.将该文件存盘,退出vi。 <18>重新编辑该文件。并将光标移到最后一行的have的v字母处,使用d$命令将v至行尾的字符都删除。 <19>现在想恢复17步的原状,怎么办?(使用复原命令u) <20>使用dd命令删除第一行;将光标移至through的u字母处,使用C(大写字母)命令进行修改,随便输入一串字符。将光标移到下一行的开头,执行5x命令;然后执行重复命令(.)。 <21>屏幕内容乱了!现在想恢复17步的原状,怎么办?(不写盘,强行退出vi) 4、Linux内核 操作系统实验 Linux 内核源程序目录结构(/usr/src/redhat/SOURCES)如下: /document :保存帮助文档 /arch :包含多个子目录,每个存放与特定体系结构相关的代码。如arch/i386(intel 386 体系结构),arch/sparc,arch/alpha等。每个子目录下至少又包含三个子目录: kernel(存放支持该体系结构特有的诸如信号处理和SMP之类特征的实现); lib(存放该体系结构特有的诸如Strlen和memcpy之类的高效率函数); mm(存放该体系结构特有的诸如内存管理程序的实现) /drivers :该目录占内核代码一半以上,包括显卡、网卡、SCSI适配器、软驱、PCI设备和其他外设的软件驱动程序。/fs:包含linux支持的文件系统。 /include :包含源程序中大部分包含(.h)文件。/init: 包含main.c,保存大部分协调内核初始化的代码。/ipc:实现了SYSTEM V的进程间通讯IPC。 /kernel:包含了linux最重要的部分:实现平台独立的基本功能,包括Sched.c、fork.c、exit.c。 /lib :存放字符串和内存操作函数。 /mm:包含与体系结构无关的内存管理代码。/net:包含了linux应用的网络协议代码。/script :包含用来配置内核的脚本。 5、报告Linux状态(/proc 中的信息) 在终端窗口提示符下,可以使用cat命令显示相关文件的内容,如: cat /proc/cpuinfo 通过编写程序,显示相关文件内容:应用文件操作,将相关 /proc中的文件读入到缓冲区中,可用fgets()函数按行取文件中数据,通过strstr()检验包含所需数据字符串。如存在,用printf()函数输出。(1)CPU类型和型号 /proc/cpuinfo文件提供了有关CPU的多种信息,这些信息是从内核里对CPU的测试代码中得到的。文件列出了CPU个数:processor;CPU制造商:vendor_id;CPU架构:model;CPU名称:model name;CPU时钟频率:cpu MHz;CPU缓存大小: 8 操作系统实验 cache size;CPU包含的指令集:flags。文件还包含了以bogomips表示的处理机速度,而且如果检测到CPU的多种特性或bug,文件还会包含相应的标志。该文件的格式为:文件由多行构成,每行包括一个域名称、一个冒号和一个值。 通过fopen()函数打开包含CPU类型和型号的文件cpuinfo,把内容读入字符数组char_all,然后通过strstr()函数查找CPU类型和型号所在的位置,用strncpy()函数拷贝到字符数组中,通过printf()标准输出函数输出。(2)存储器信息 /proc/meminfo 文件给出了内存状态的信息。它显示出系统中物理内存的总量:MenTotal;未使用的物理内存的总量:MemFree;用做文件缓冲的物理内存的总量:buffers;用做缓冲的物理内存的总量:Cached;活跃的内存大小:Active;不活跃的内存大小:Inactive;交换分区的总量:SwapTotal;交换分区未使用的总量:SwapFree等信息。(3)内核版本 文件/proc/version显示了正在运行的内核版本、编译此内核的gcc版本以及该内核的编译时间。 (4)从系统最后一次启动以来的时间,形式为dd:hh:mm:ss uptime读出的时间是以秒计的,所以根据要求要转换为天:小时:分钟:秒。1天为86400秒,1小时为3600秒,1分钟为60秒。通过两个运算符就可以很好的转换:“/”做除法取整运算,“%”做除法取余运算。举例:86800秒,(86800/86400)=1(天),(86800%86400)=400(余400秒);400秒,(400/3600)=0小时,(400%3600)=400(余400秒);400秒,(400/60)=6分钟,(400%60)=40(余40秒)。所以最后结果为:1:0:6:40。(5)其他信息的读取 从/proc/stat中读取信息 CPU花费在用户态、系统态和空闲态的时间——cpu 自系统启动以来,发生的所有的中断的次数——intr 内核执行的上下文转换的次数----ctxt 系统最后启动的时间----btime 从系统启动开始创建的进程数----processes 6、Linux的目录结构 操作系统实验 对于Linux来讲它的树型结构与Windows不同,Windows可以有多个分区,每个分区都有根,但Linux 只有一个根,其他的所有文件、目录或硬盘分区、软盘、光盘、U 盘都必须mount(挂载)到Linux 根下的一个目录中才能被访问和使用。下面列出根目录下的常见系统目录及其用途。 /bin bin是binary的缩写。这个目录沿袭了UNIX系统的结构,存放着使用者最经常使用的命令。例如cp、ls、cat,等等。 /boot 这里存放的是启动Linux时使用的一些核心文件。 /dev dev是device(设备)的缩写。这个目录下是所有Linux的外部设备,其功能类似DOS下的.sys和Win下的.vxd。在Linux中设备和文件是用同种方法访问的。例如:/dev/hda代表第一个物理IDE硬盘。 /etc 这个目录用来存放系统管理所需要的配置文件(例如配置文件inittab)和子目录。 /home 用户的主目录,比如说有个用户叫wang,那他的主目录就是/home/wang,也可以用~wang表示。 /lib 这个目录里存放着系统最基本的动态链接共享库,其作用类似于Windows里的.dll文件。几乎所有的应用程序都需要用到这些共享库。 /lost+found 这个目录平时是空的,当系统不正常关机后,这里就成了一些无家可归的文件的避难所,有点类似于DOS下的.chk文件。 /media 用来挂载光盘、U盘等文件系统的目录。/misc 用来挂载NFS 共享目录。 /mnt 用于挂载其他硬盘分区系统的目录(如挂载xp分区)。 /opt 某些第三方软件商软件的安装地点,如国产红旗office就存放于此。/proc 这个目录是一个虚拟的目录,它是系统内存的映射,可以通过直接访问这个目录来获取系统信息。也就是说,这个目录的内容不在硬盘上而是在内存里。 /root 系统管理员(也叫超级用户)的主目录。作为系统的拥有者,总要有些特权,比如单独拥有一个目录。 /sbin s就是Super User的意思,也就是说这里存放的是系统管理员使用的管理程序。 /tmp 这个目录是用来存放一些临时文件的地方。 /usr 这是最庞大的目录,要用到的应用程序和文件几乎都存放在这个目录 10 操作系统实验 下。其中包含以下子目录: /usr/X11R6 存放X-Window的目录; /usr/bin 存放着许多应用程序; /usr/sbin 给超级用户使用的一些管理程序就放在这里; /usr/include Linux下开发和编译应用程序需要的头文件,在这里查找; /usr/lib 存放一些常用的动态链接共享库和静态档案库; /usr/local 这是提供给一般用户的/usr目录,在这里安装软件最适合; /usr/src Linux开放的源代码就存在这个目录。 /var 这个目录中存放着那些不断在扩充着的东西,为了保持usr的相对稳定,那些经常被修改的目录可以放在这个目录下,实际上许多系统管理员都是这样做的。另外,系统的日志文件就在/var/log目录中。 我们一般日常能经常访问的目录有/home 目录、/mnt目录、/media 目录、/usr 目录。第二篇:《操作系统》实验指导书
第三篇:嵌入式操作系统实验报告
第四篇:嵌入式操作系统课程报告
第五篇:操作系统实验