嵌入式Linux实时操作系统及应用编程代码及答案.doc
附录A 习题参考答案嵌入式Linux实时操作系统及应用编程第1章1嵌入式系统是指操作系统和功能软件集成于计算机硬件系统之中。简单的说就是系统的应用软件与系统的硬件一体化,类似与BIOS的工作方式。具有软件代码小,高度自动化,响应速度快等特点。特别适合于要时的和多任务的体系。根据IEEE国际电气和电子工程师协会的定义:嵌入式系统是"用于控制、监视或者辅助操作机器和设备的装置原文为devices used to control, monitor, or assist the operation of equipment, machinery or plants。简单地讲就是嵌入到对象体中的专用计算机系统。嵌入式系统一般有3个主要的组成局部:硬件、实时操作系统以及应用软件。图1.1 嵌入式系统三个组成局部硬件:包括处理器、存储器ROM、RAM、输入输出设备、其他局部辅助系统等。实时操作系统Real-Time Operating System,RTOS:用于管理应用软件,并提供一种机制,使得处理器分时地执行各个任务并完成一定的时限要求。应用软件:实现具体业务逻辑功能。2嵌入式系统的三要素是嵌入、专用、计算机;其中嵌入性指的是嵌入到对象体系中,有对象环境要求;专用性是指软、硬件按对象要求裁减;计算机指实现对象的智能化功能。广义地说一个嵌入式系统就是一个具有特定功能或用途的计算机软硬件集合体。即以应用为中心、以计算机技术为根底、软件硬件可裁剪、适应应用系统对功能、可靠性、本钱、体积、功耗严格要求的专用计算机系统 。3嵌入式实时操作系统Real-Time Operating System,RTOS是指操作系统本身要能在一个固定时限对程序调用或外部事件做出正确的反响,亦即对时序与稳定性的要求十分严格。目前国际较为知名的有:VxWorks、NeutrinoRTOS、Nucleus Plus、 OS/9、VRTX、LynuxOS,RTLinux、BlueCat RT等。4嵌入式系统一般由硬件层、中间层、软件层和功能层组成。其作用分别如下:1硬件层 :由嵌入式微处理器、外围电路和外设组成。外围电路有:电源电路、复位电路、调试接口和存储器电路,就构成一个嵌入式核心控制模块。操作系统和应用程序都可以固化在ROM或者Flash中。为方便使用,有的模块在此根底上增加了LCD、键盘、USB接口,以及其他一些功能的扩展电路。2中间层 :硬件层与软件层之间为中间层,也称为BSPBoard Support Package,板级支持包。作用:将系统软件与底层硬件局部隔离,使得系统的底层设备驱动程序与硬件无关;功能:一般应具有相关硬件的初始化、数据的输入/输出操作和硬件设备的配置等功能。BSP是主板硬件环境和操作系统的中间接口,是软件平台中具有硬件依赖性的那一局部,主要目的是为了支持操作系统,使之能够更好地运行于硬件主板上。3软件层 :主要是操作系统,有的还包括文件系统、图形用户接口和网络系统等。操作系统是一个标准的核,将中断、I/O、定时器等资源都封装起来,以方便用户使用。4功能层 :由基于操作系统开发的应用程序组成,用来完成对被控对象的控制功能。功能层是面向被控对象和用户的,为了方便用户操作,往往需要具有友好的人机界面。5非占先式调度法也称作合作型多任务cooperative multitasking,各个任务彼此合作共享一个CPU。中断效劳可以使一个高优先级的任务由挂起状态变为就绪状态。但中断效劳以后控制权还是回到原来被中断了的那个任务,直到该任务主动放弃CPU的使用权时,那个高优先级的任务才能获得CPU的使用权。当系统响应时间很重要时,要使用占先式preemptive核。最高优先级的任务一旦就绪,总能得到CPU的控制权。当一个运行着的任务使一个比它优先级高的任务进入了就绪态,当前任务的CPU使用权就被剥夺了,或者说被挂起了,那个高优先级的任务立刻得到了CPU的控制权。6在实时系统中,如果系统在指定的时间未能实现某个确定的任务,会导致系统的全面失败,这样的系统被称为强实时系统或硬实时系统。强实时系统响应时间一般在毫秒或微秒级。在弱实时系统中,虽然响应时间同样重要,但是超时却不会发生致命的错误。其系统响应时间在毫秒至秒的数量级上,其实时性的要求比强实时系统要差一些。7嵌入式系统的设计步骤及各局部的主要工作如下。1需求分析阶段,罗列出用户的需求;2体系构造设计阶段,描述系统的功能如何实现;3详细设计阶段,进展硬件系统与软件系统的分类划分,以决定哪些功能用硬件实现,哪些用软件实现;4系统集成,把系统的软件、硬件和执行装置集成在一起,进展调试,发现并改良在设计过程中的错误;5系统测试,对设计好的系统进展测试,看其是否满足给定的要求。8Linux作为嵌入式操作系统的优势主要有以下几点: 1可应用于多种硬件平台。Linux已经被移植到多种硬件平台,这对于经费,时间受限制的研究与开发工程是很有吸引力的。原型可以在标准平台上开发后移植到 具体的硬件上,加快了软件与硬件的开发过程。Linux采用一个统一的框架对硬件进展管理,从一个硬件平台到另一个硬件平台的改动与上层应用无关。2Linux的高度模块化使添加部件非常容易。本身置网络支持,而目前嵌入式系统对网络支持要求越来越高。3Linux是一个和Unix相似、以核为根底的、具有完全的存访问控制,支持大量硬件(包括X86,Alpha、ARM和Motorola等现有的大 局部芯片)等特性的一种通用操作系统。4Linux可以随意地配置,不需要任何的许可证或商家的合作关系。其程序源码全部公开,任何人可以修改并在GUN通用公共许可证(GNU General Public License)下发行。这样,开发人员可以对操作系统进展定制,适应其特殊需要。5Linux带有Unix用户熟悉的完善的开发工具,几乎所有的Unix系统的应用软件都已移植到了Linux上。其强大的语言编译器GCC,C+等也可以很容易得到,不但成熟完善,而且使用方便。9参考答案Linux执行进程调度一般是在以下情况发生的:1正在执行的进程运行完毕;2正在执行的进程调用阻塞原语将自己阻塞起来进入等待状态;3正在执行的进程调用了P原语操作,从而因资源缺乏而被阻塞;或调用了V原语操作激活了等待资源的进程队列;4执行中的进程提出I/O请求后被阻塞;5系统分配的时间片已经用完;以上都是CPU为不可剥夺方式下的引起进程调度的原因。在CPU方式是可剥夺时,还有下面的原因:6就绪队列中的某个进程的优先级变得高于当前运行进程的优先级,从而也将引起进程调度。嵌入式Linux实时操作系统及应用编程第2章 一.填空题:1、改变目录位置至用户的工作目录2、改变目录位置至相对路径user 的目录下3、查看当前目录下的文件 4、查看文件.bash_profile的容5、分页查看inittab文件容6、将目录/tmp 下的文件file1 复制到当前目录下,文件名为f i l e 27、将文件file1移到目录dir1 下,文件名仍为file18、建立一新目录d i r 19、删除目录dir1,但dir1 下必须没有文件存在,否那么无法删除10、删除文件名中有五个字符且前四个字符为file 的所有文件11、文件config的容依次显示到屏幕上12、以分页方式查看文件名file1 的容13、以分页方式查看文件名file1 的容14、显示目录dir1 的总容量15、对于目录d i r 1,设定成任何使用者皆有读取及执行的权利,但只有所有者可做修改16、对于文件f i l e 1,设定只有所有者可以读、写和执行的权利。17、将文件file4 至文件f i l e 3。18、寻找文件f i l e 1中包含字符串abc 所在行的文本容。19、自根目录下寻找文件file1 的路径。20、比拟目录dir1 与dir2 各文件的不同之处。二、单项选择题1.A 2.B 3.D 4.C 5.B 6.C 7.A 8.C 9.D 10.B11.A12.D 13.D 14.B 15.B 16.B 17.A 18.D 19.B 20.B嵌入式Linux实时操作系统及应用编程第3章一.填空题:1 next2命令模式3预处理、编译、汇编和连接4. step next5. 末行模式6.预处理 7.汇编8. w test.txt 二、综合题1参考答案:pr1: prog.o subr.ogcc o pr1 prog.o subr.oprog.o: prog.c prog.hgcc c o prog.o prog.cgcc c o subr.o subr.c2.(1)hello: main.o list.o symbol.o table.o gcc -o prog main.o list.o symbol.o table.omain.o: main.c table.h symbol.h list.h gcc -c -o main.o main.clist.o: list.c list.h gcc -c -o list.o list.csymbol.o: symbol.c symbol.h gcc -c -o symbol.o symbol.ctable.o: table.c table.h symbol.h list.hgcc -c -o table.o table.cclean:rm hello *.o(2) mount t nfs o noclock 192.168.0.10:/home/armtest/hello /mnt3.hello.c:# include <stdio.h># include <stdlib.h># include <string.h># include "hello.h"int main (int argc,char *argv) printf("Hello World!n"); message(); return 0; hello.h:void message(void);message.c:# include <stdio.h>void message (void) printf("This is a message!n");三选择题1 C2 A3 B 4 B 5B6C 7C8. D 9D10A11A嵌入式Linux实时操作系统及应用编程第4章一选择题1. A2 C 3 D 4. D 5. C 6 B7 C 8. A9. D 10. D 11D12D 13D 14B 15 D16B17A18C19C20C 二.简答题1、Linux 核的编译菜单有好几个版本,运行:1make config:进入命令行,可以一行一行的配置,但使用不十分方便。2make menuconfig:大多数开发人员使用的Linux 核编译菜单,使用方便。3make xconfig:在2.4.X 以及以前版本中xconfig 菜单是基于TCL/TK 的图形库的。2、在完成核的裁减之后,核的编译就只要执行以下几条命令:make clean编译核之前先把环境给清理干净。有时你也可以用makerealclean 或make mrproper来彻底去除相关依赖,保证没有不正确的.o 文件存在。make dep 编译相关依赖文件make zImage 创立核镜像文件make modules 创立核模块,假设不创立核模块,这步可以不要。make install 把相关文件拷贝到默认的目录。在给嵌入式设备编译时这步可以不要。因为具体的核安装还需要你手工进展。3、此命令是装载压缩映像文件zImage到flash存储器中,地址是kernel分区,并采用xmodem传输协议。4、此命令是设置网卡1的地址,掩码为,不写netmask参数那么默认为。5、此命令将nfs效劳的共享目录sharedir加载到/mnt/nfs。6、此命令是装载根文件系统root.cramfs到flash存储器中,地址是根文件系统分区,并采用xmodem传输协议。7、这个命令的操作同时进展了分区和格式化,0128K存放vivi,128K192K存放VIVI控制台指令,192K1216K存放kernel,1216K4288K存放root,其余局部存放应用程序。嵌入式Linux实时操作系统及应用编程第5章一选择题1B2 C 3 C 4C5 D 6C二.综合应用题1.Tom is my friendJack is my friendHarry is my friend2.(1程序注释#!/bin/sh 定义实用的shell# /etc/rc.d/rc. d 注释行,但凡以星号开场的行均为注释行。# Start/stop/restart the Apache web server.# To make Apache start automatically at boot, make this # file executable: chmod 755 /etc/rc.d/rc. d#case "$1" in #case构造开场,判断"位置参数决定执行的操作。本程序携带一个"位置参数,即$1'start') #假设位置参数为start/usr/sbin/apachectl start ; #启动 d进程'stop') #假设位置参数为stop/usr/sbin/apachectl stop ; #关闭 d进程'restart') #假设位置参数为stop/usr/sbin/apachectl restart ; #重新启动 d进程*) #假设位置参数不是start、stop或restart时echo "usage $0 start|stop|restart" ; #显示命令提示信息:程序的调用方法esac #case构造完毕2程序的功能是启动,停顿或重新启动 d进程3程序的调用方式有三种:启动,停顿和重新启动。3.#!/bin/shFILENAME=echo "Input file name:read FILENAMEif -c "$FILENAME" thencp $FILENAME /devfi4.#/bin/bashtypeset first secondread -p "Input the first number:" firstread -p "Input the second number:" secondresult=$first+$secondecho "result is : $result"exit 05、#!/bin/shi=1while i -le 50 doif -d /userdata ;thenmkdir -p /userdata/user$ichmod 754 /userdata/user$iecho "user$i"let "i = i + 1" 或i=$i1elsemkdir /userdatamkdir -p /userdata/user$ichmod 754 /userdata/user$iecho "user$i"let "i = i + 1" 或i=$i1fidone嵌入式Linux实时操作系统及应用编程第6章一简答题1使用虚拟地址寻址整个系统的主存和辅存的方式在现代操作系统中被称为虚拟存。MMU 便是实现虚拟存的必要条件。嵌入式处理器如果存在MMU ,由于在MMU具备存地址映射和寻址功能,操作系统会使用它完成从虚拟地址到物理地址的转换, 所有的应用程序只需要使用虚拟地址寻址数据。 虚拟存的管理方法使系统既可以运行体积比物理存还要大的应用程序,也可以实现"按需调页策略,既满足了程序的运行速度,又节约了物理存空间。2进程存区域涉及到5种数据段,即:代码段:代码段是用来存放可执行文件的操作指令,也就是说是它是可执行程序在存中的镜像。数据段:数据段用来存放可执行文件中已初始化全局变量,换句话说就是存放程序静态分配的变量和全局变量。BSS段:BSS段包含了程序中未初始化的全局变量,在存中 BSS段全部置零。堆heap:堆是用于存放进程运行中被动态分配的存段,它的大小并不固定,可动态扩或缩减。当进程调用malloc等函数分配存时,新分配的存就被动态添加到堆上堆被扩;当利用free等函数释放存时,被释放的存从堆中被剔除堆被缩减栈:栈是用户存放程序临时创立的局部变量,也就是说函数括弧"中定义的变量但不包括static声明的变量,static意味着在数据段中存放变量。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用完毕后,函数的返回值也会被存放回栈中。3在Linux系统中,核在最高级执行,也称为"系统态,在这一级任何操作都可以执行。而应用程序那么执行在最低级,即所谓的"用户态。在这一级处理器制止对硬件的直接访问和对存的未授权访问。模块是在所谓的"核空间中运行的,而应用程序那么是在"用户空间中运行的。它们分别引用不同的存映射,也就是程序代码使用不同的"地址空间。4共享存区域是被多个进程共享的一局部物理存。如果多个进程都把该存区域映射到自己的虚拟地址空间,那么这些进程就都可以直接访问该共享存区域,从而可以通过该区域进展通信。共享存是进程间共享数据的一种最快的方法,一个进程向共享存区域写入了数据,共享这个存区域的所有进程就可以立刻看到其中的容。这块共享虚拟存的页面,出现在每一个共享该页面的进程的页表中。但是它不需要在所有进程的虚拟存中都有一样的虚拟地址。5.存管理利用虚拟文件系统支持交换,交换进程(swapd)定期由调度程序调度,这也是存管理依赖于进程调度的唯一原因。当一个进程存取的存映射被换出时,存管理向文件系统发出请求,同时,挂起当前正在运行的进程。二.编程题1参考程序:#include<stdlib.h>struct testint a10;char b20;int main()struct test *ptr=calloc(sizeof(struct test),10);2参考程序:#include<sys/types.h>#include<sys/stat.h>#include<ftl.h>#include<unistd.h>#include<sys/mman.h>main()int fd;void *start;struct stat sb;fd=open("/etc/passwd,O_RDONLY); /*翻开/etc/passwd*/fstat(fd,&sb); /*取得文件大小*/start=mmap(NULL,sb.st_size,PROT_READ,MAP_PRIVATE,fd,0);if(start= = MAP_FAILED) /*判断是否映射成功*/return;printf("%s,start);munma(start,sb.st_size); /*解除映射*/closed(fd);嵌入式Linux实时操作系统及应用编程第7章一、单项选择题1A 2. B3. A 4. D 5 6. A 7. C 二.编程题1. (1)Void main(void)int fid;fid = open("./test.txt,O_RDWR|O_CREAT); if(fid=-1)Printf("open or create error n);exit(0);Close(fid);(2) objects = main.o exec = mainall:$(objects)gcc o $(exec) $(objects)main.o:main.c gcc c main.c clean:rm r $(exec) $(objects)2.参考程序:#include <sys/types.h>#include <sys/stat.h>#include <ftl.h>#include<stdlib.h>#include<unistd.h>#include<stdio.h>#define BUFFER_SIZE 1024 int main(int argc,char *argv) int from_fd,to_fd;int bytes_read,bytes_write; char bufferBUFFER_SIZE; char *ptr; if(argc!=3) fprintf(stderr,"Usage:%s fromfile tofilena",argv0);exit(1); /* 翻开源文件 */ if(from_fd=open(argv1,O_RDONLY)=-1) fprintf(stderr,"Open %s Error:%sn",argv1,strerror(errno); exit(1); /* 创立目的文件 */ if(to_fd=open(argv2,O_WRONLY|O_CREAT,S_IRUSR|S_IWUSR)=-1) fprintf(stderr,"Open %s Error:%sn",argv2,strerror(errno); exit(1); /* 以下代码是一个经典的拷贝文件的代码 */ while(bytes_read=read(from_fd,buffer,BUFFER_SIZE) if(bytes_read=-1)&&(errno!=EINTR) break; /*发生读错误,退出循环*/ else if(bytes_read>0) ptr=buffer; while(bytes_write=write(to_fd,ptr,bytes_read) if(bytes_write=-1)&&(errno!=EINTR)break; /*假设写错误,退出循环*/* 写完了所有读的字节 */ else if(bytes_write=bytes_read) break;/*读写字节不等退出循环*/ else if(bytes_write>0) /* 只写了一局部,继续写 */ ptr+=bytes_write; bytes_read-=bytes_write; if(bytes_write=-1)break; /* 写的时候发生的致命错误 */ close(from_fd); close(to_fd); exit(0); 3参考程序:#include <stdio.h>struct student char name10; int age; ;int main() FILE *fp; int i;struct student boya2, boyb2, *pp, *qq; if(fp = fopen("7-6.txt","w+")= NULL) /翻开文件 printf("Can not open file, exit .n"); return -1; pp = boya; qq = boyb; printf("please input data:n");/输入学生信息 for (i = 0; i < 2; i+, pp+) scanf("%s%d", pp->name, &pp->age); pp = boya; fwrite(pp, sizeof(struct student), 2, fp); /把学生信息写入文件 rewind(fp); /重定位文件 fread(qq, sizeof(struct student), 2, fp); /从文件中读取学生信息 printf("namettagen"); for(i = 0; i < 2; i+, qq+) printf("%stt%dn", qq->name, qq->age); fclose(fp); return 0;4参考程序如下:嵌入式Linux实时操作系统及应用编程第8章一、单项选择题1、B2、A 3、A4、D 5、C 6. D7、C 8、D 9、C10、B 二.阅读程序题1答案要点:(1) 将数据缓冲区清0 (2) 创立管道(3) 创立子进程(4) 关闭子进程写描述符 (5) 子进程读取管道容(6) 关闭子进程读描述符 (7) 父进程运行控制语句 (8) 关闭父进程的读描述符(9) 将数据写入缓冲区 (10) 关闭父进程写描述符 三程序设计1参考程序如下:void main() key_t unique_key; /* 定义一个IPC关键字*/int id;struct sembuf lock_it;union semun options;int i;unique_key = ftok(".", 'a'); /* 生成关键字,字符'a'是一个随机种子*/* 创立一个新的信号量集合*/id = semget(unique_key, 1, IPC_CREAT | IPC_EXCL | 0666);printf("semaphore id=%dn", id);options.val = 1; /*设置变量值*/semctl(id, 0, SETVAL, options); /*设置索引0的信号量*/*打印出信号量的值*/i = semctl(id, 0, GETVAL, 0);printf("value of semaphore at index 0 is %dn", i);/*下面重新设置信号量*/lock_it.sem_num = 0; /*设置哪个信号量*/lock_it.sem_op = -1; /*定义操作*/lock_it.sem_flg = IPC_NOWAIT; /*操作方式*/if (semop(id, &lock_it, 1) = -1) printf("can not lock semaphore.n");exit(1);i = semctl(id, 0, GETVAL, 0);printf("value of semaphore at index 0 is %dn", i);/*去除信号量*/semctl(id, 0, IPC_RMID, 0);2参考程序:1init.c清单 #include < unistd.h > #include < signal.h > #include < sys/param.h > #include < sys/types.h > #include < sys/stat.h > void init_daemon(void) int pid;int i; if(pid=fork() exit(0);/是父进程,完毕父进程 else if(pid< 0) exit(1);/fork失败,退出 /是第一子进程,后台继续执行 setsid();/第一子进程成为新的会话组长和进程组长并与控制终端别离if(pid=fork()exit(0);/是第一子进程,完毕第一子进程 else if(pid< 0)exit(1);/fork失败,退出 /是第二子进程,继续 /第二子进程不再是会话组长 for(i=0;i< NOFILE;+i)/关闭翻开的文件描述符 close(i); chdir("/tmp");/改变工作目录到/tmp umask(0);/重设文件创立掩模return; 2test.c清单 #include < stdio.h > #include < time.h > void init_daemon(void);/守护进程初始化函数 main() FILE *fp; time_t t; init_daemon();/初始化为Daemon while(1)/每隔一分钟向test.log报告运行状态 sleep(60);/睡眠一分钟 if(fp=fopen("test.log","a") >=0) t=time(0);re at %sn",asctime(localtime(&t) ); fclose(fp); 以上程序在RedHat Linux6.0下编译通过。步骤如下:编译:gcc -g -o test init.c test.c 查看进程:ps -ef 程的各种特性满足上面的要求。3参考程序:#define INPUT 0#define OUTPUT 1void main() int file_descriptors2;/*定义子进程号 */pid_t pid;char buf256;int returned_count;/*创立无名管道*/pipe(file_descriptors);/*创立子进程*/if(pid = fork() = -1) printf("Error in forkn");exit(1);/*执行子进程*/if(pid = 0) printf("in the spawned (child) process.n");/*子进程向父进程写数据,关闭管道的读端*/close(file_descriptorsINPUT);write(file_descriptorsOUTPUT, "test data", strlen("test data");exit(0); else /*执行父进程*/printf("in the spawning (parent) process.n");/*父进程从管道读取子进程写的数据,关闭管道的写端*/close(file_descriptorsOUTPUT);returned_count = read(file_descriptorsINPUT, buf, sizeof(buf);printf("%d bytes of data received from spawned process: %sn",returned_count, buf);在Linux系统下,有名管道可由两种方式创立:命令行方式mknod系统调用和函数mkfifo。下面的两种途径都在当前目录下生成了一个名为myfifo的有名管道:方式一:mkfifo("myfifo","rw");方式二:mknod myfifo p生成了有名管道后,就可以使用一般的文件I/O函数如open、close、read、write等来对它进展操作。下面即是一个简单的例子,假设我们已经创立了一个名为myfifo的有名管道。/* 进程一:读有名管道*/#include#includevoid main() FILE * in_file;int count = 1;char buf80;in_file = fopen("mypipe", "r");if (in_file = NULL) printf("Error in fdopen.n");exit(1);while (count = fread(buf, 1, 80, in_file) > 0)printf("received from pipe: %sn", buf);fclose(in_file);/* 进程二:写有名管道*/#include#includevoid main() FILE * out_file;int count = 1;char buf80;out_file = fopen("mypipe", "w");if (out_file = NULL) printf("Error opening pipe.");exit(1);sprintf(buf,"this is test data for the named pipe examplen");fwrite(buf, 1, 80, out_file);fclose(out_file);嵌入式Linux实时操作系统