标题:
基于Linux的嵌入式操作系统研究(6)
[打印本页]
作者:
blackghost
时间:
2003-4-4 22:12
标题:
基于Linux的嵌入式操作系统研究(6)
第四章 实时Linux 利用Linux实现一个实时系统有这样那样的好处,在前面已经详细描述过了。但是,如何 通过Linux来实现这个实时系统呢?一般说来,有两种途径来实现,第一种方法是通过P OSIX方法;另一种方法是通过底层编程的方法实现。 POSIX(Portable Operating System Interface)是标准化类UNIX操作系统必须具有的 特征和接口的运动。使用POSIX的思想就是为了促进为UNIX编写软件的可移植性,从而使 得UNIX程序员的工作更为容易。并且在POSIX里面加进了对实时性的扩展,如POSIX.1b或 者IEEE 1003.1b已经加入到POSIX标准中。在这些实时性扩展里面定义了一些工具,如信 号、内存锁定、时钟和计数器、消息队列以及优先级抢先调度等等。不过通过POSIX基础 来标准化实时操作系统并不被开发者所看中。因为POSIX系统调用反映了UNIX系统调用的 复杂和笨重。 现在已经有很多程序员在Linux下做实现POSIX .1b的实现工作,并且已经实现了POSIX的 内存锁定工具和决定调度算法。另外,计数器函数和POSIX.1b信号还没有完成,而且信 号和消息队列也还没有实现。或许,如果一直遵循POSIX.1c中,开发出POSIX线程。在L inux中实现一个进程内可以有多个线程,共享相同的地址空间,从而作为嵌入式系统中 的任务这个概念。但是我们可以这样想象,即使把POSIX.1b函数完全移植到Linux上来, 而Linux内核却不能被实时任务所抢占,那么这样的实现恐怕还是没有意义,无法实现真 正意义上的“硬”实时。 另外一条实现实时的途径就是使用底层编程的方法。在Linux内核中作出最少的改动,在 Linux内核中影响实时性能的地方增加进控制,将控制权交给RTLinux内核。因此,RTLi nux只需要提供一些必要的功能,如底层任务创建、中断服务程序,并且为底层任务、I SR和Linux进程之间进行通信排队。在第三节我将详细描述RTLinux的实现机理。 4.1 实时Linux(RTLinux)介绍 RTLinux是通过底层路线实现对Linux实时改造的产物。这是美国新墨西哥州大学计算机 科学系Victor Yodaiken和Michael Brananov开发的。他在Linux内核的下层实现了一个 简单的实时内核,而Linux本身作为这个实时内核的优先级最低的任务,所有的实时任务 的优先级都要高于Linux本身以及Linux下的一般任务。 目前,RT Linux已经得到了一些应用。NASA用RTLinux开发出来的数据采集系统用来采集 George飓风中心的风速;好莱坞的电影制作人用RTLinux来作为视频编辑工具;RTLinux 还用来作为机器人控制器;甚至在医学方面有用RTLinux来控制心脏的跳动的。 RTLinux的性能是“硬”实时系统的性能。在一台386机器上,RT Linux从处理器检测到 中断到中断处理程序开始工作不会超过15微秒;对一个周期性的任务,在35微秒内一定 会执行。如果是普通的Linux,一般是在600微秒内开始一个中断服务程序,对周期性的 任务很可能会超过20毫秒(20,000微秒)。 4.2 RTLinux特征 4.2.1 小而精巧的实时内核 RTLinux实现了一个在Linux本身内核之下的一个小而精巧的内核,这个内核只需要完成 底层任务创建、中断服务程序,并为底层任务、ISR和Linux进程之间进行通信排队的工 作。将原来的Linux作为实时内核下的一个随时可被实时任务抢占的优先级最低的任务。 因为RTLinux工作是和Linux相结合进行工作,因此它可以设计得很简单,大部分的应用 都不需要考虑,可以由Linux来完成。因为简单,使得它不容易出现错误;即使出现错误 也容易得到更正。同时因为简单,并且完全抢占Linux内核任务,使得它的响应速度特别 快。 4.2.2 模块化的设计方案 RTLinux是对Linux内核做了修改,做成一个小而简单的实时内核在现有的Linux内核下的 。对RTLinux的调度方法、用户实时任务的工作是通过Linux的可导入模块的方式进行的 。可以自己实现一个实时调度算法,作为Linux的模块插入到内核运行空间,作为实时任 务的调度策略。用户实时任务的编程也是通过模块编程来实现的。 4.2.3 和Linux内核的结合 正如我在3.2里面提到的那样,采用双内核系统有很多好处。这样,我们对RTLinux应用 来看,存在有两个域,一个是实时域,一个是非实时域。放在实时域的函数能满足其实 是的要求,不过这些实时任务必须要简单,因为可用的资源收到了限制;非实时域的函 数可以利用整个Linux的资源,不过不能提供任何的实时性能。在两个域之间可以通过多 种途径进行通信,如FIFO,共享内存等方法。 4.2.3.1 利用Linux内核的好处 通过RTLinux内核和Linux内核的结合,RTLinux可以利用存在Linux的非实时域函数的长 处: 1)Linux提供了现代操作系统和环境的方便和强大功能,如网络服务,GUI 系统,C/C+ +/Java开发工具和标准的编程接口。 2)利用Linux和Linux下开发环境的快速发展的优点。因为RTLinux对Linux所做的改动比 较少,RTLinux可以很方便的移植到Linux较新的版本中,升级速度较快。 4.2.3.2 和Linux进行通信的方法 在RTLinux的实时任务并不能直接调用系统调用,它必须通过特定的方法和Linux进程进 行通信。RTLinux提供了三种通信的方法: 1)共享内存 在RTLinux启动的时候,通过指定给内核一个mem参数决定内核可以使用的内存大小,空 出来的内存空间用于实时任务和Linux进程进行通信的共享内存。在RTLinux任务中通过 /dev/mem设备在这段内存中寻址,Linux进程也通过读取这段内存的数据获得实时任务提 供的信息,这样完成实时任务和Linux进程之间的通信。 2)FIFO设备 利用一种特殊属性的管道进行通信。通过在/dev/下面创建FIFO的字符设备。实时任务可 以往这个字符设备写数据,非实时任务可以从这个设备中读取数据。在对FIFO设备的读 写是不同步的,非实时任务对设备的读取并不会影响实时任务对它的写。 这种机制是RTLinux应用中使用最广泛的通信方法,比如在一个数据采集系统上,通过实 时任务将数据采集并处理,然后写到FIFO设备中,通过一个在X Windows环境中的图形界 面程序读取FIFO的数据,然后显示在X Windows窗口中。这样,既保证了对数据的实时处 理,又提供了友好的监控界面。 3)mbuff驱动程序 由Tomasz Motylewski提供的一个使用共享内存的驱动程序。用来实现核心内存空间和用 户内存空间之间的共享。通过使用mbuff提供的mbuff_alloc()函数对申请的内存取一 个名字,mbuff驱动程序使用一个链表通过这个名字来管理这些申请的内存。通过这个驱 动程序可以在包括RTLinux任务的Linux内核内存空间和用户内存空间之间共享内存。 4.3 RTLinux的实现机理 RTLinux对Linux内核进行改造,将Linux内核工作环境做了一些变化,如下图所示: 从这张图可以看出,在Linux进程和硬件中断之间,本来由Linux内核完全控制,现在在 Linux内核和硬件中断的地方,加上了一个RTLinux内核的控制。Linux的控制信号都要先 交给RTLinux内核先进行处理。在RTLinux内核中实现了一个虚拟中断机制,Linux本身永 远不能屏蔽中断,它发出的中断屏蔽信号和打开中断信号都修改成向RTLinux发送一个信 号。如在Linux里面使用“sti”和“cli”宏指令来屏蔽和使能中断,是通过向x86处理 器发送一个指令,而RTLinux修改了这些宏指令,使得只是让RTLinux里面的某些标记做 了修改而已。也就是说对所有的中断,分成Linux中断和实时中断两类,如果RTLinux内 核收到的中断信号是普通Linux中断,那就设置一个标志位;如果是实时中断,就继续向 硬件发出中断。在RT Linux中执行sti将中断打开之后,那些设置了标志位表示的Linux 中断就继续执行。因此,cli并不能禁止RT Linux内核的运行,却可以用来中断Linux。 Linux不能中断自己,而RT Linux可以。 这里体现了RTLinux设计过程中的原则:在实时内核模块中的工作尽量少,如果能在Lin ux中完成而不影响实时性能的话,就尽量在Linux中完成。因此,RTLinux内核尽量做的 简单,在RTLinux内核中,不应该等待资源,也不需要使用共享旋转锁(SpinLock),实 时任务和Linux进程间的通信也是非阻塞的,从来不用等待进队列和出队列的数据。 RT Linux将系统和设备的初始化交给了Linux完成,对动态资源的申请和分配也交给了L inux。RTLinux使用静态分配的内存来完成硬实时任务,因为在没有内存资源的时候,被 阻塞的线程不可能具有硬实时能力。 4.4 RTLinux的编程接口(API) 在2.0版本的RTLinux里面包括了两个部分的内容。 一部分是对Linux内核的修改,使得Linux内核中形成一个RTLinux实时小内核。在这个内 核中提供了小延时并且不能被Linux延迟和抢占的的中断处理过程,并且提供了一些底层 的同步和中断的控制过程。并且现在可以支持SMP(对称多处理器系统),同时为了简化 RTLinux结构,把一些没有必要的功能从其中移走了。 另外一部分是作为Linux标准模块出现的,提供了RTLinux的编程和控制接口(API)。通 过使用这些API可以提供对实时任务的创建和删除、任务的调度和控制等功能。这些模块 如下: 1)rtl_sched 提供了基于优先级的调度方法,支持POSIX接口和1.0版本的RTLinux API 函数。 2)rtl_time提供了控制处理器时钟,并且提供了一个时钟处理过程的抽象接口。 3)rtl_posixio提供了使用POSIX方式的驱动程序的读/写/打开调用。 4)rtl_fifo提供了实时任务和Linux进程的通信接口,通过一个Linux进程可以读写的F IFO设备进行通信。 5)semaphore是由Jerry Epplin提供的给实时任务提供信号量的模块。 6)mbuff是由Tomasz Motylewski提供的内核进程和Linux用户进程进行通信的共享内存 驱动程序。通过mbuff驱动程序可以让实时任务和用户线程共享内存。 在这些模块里面,提供的API函数主要有如下几类: 1)中断控制API函数 2)时钟控制和获取 3)线程的创建和删除(在2.0版本的RTLinux里面,不再通过rt_task_init和rt_task_d elete创建和删除实时任务,而是通过pthread线程库进行操作。)以及线程优先级和调 度策略的控制API函数 4)POSIX方式的驱动接口 5)FIFO设备驱动程序 6)串口驱动程序的API函数 7)mbuff驱动API函数 8)浮点运算API函数 具体的API介绍和使用方法可以参考《RTLinux Version Two White Pages》 4.5 RTLinux的编程方法示例 在这里介绍一个RT Linux编程示例,说明在RT Linux下面编程的方法。因为篇幅关系, 我选择了一个验证RT Linux的任务调度性能测试的例子。在介绍例子之前,先介绍需要 用到的RT Linux标准API函数: 4.5.1 需要用到的API函数: 4.5.1.1 任务生成和调度函数 int pthread_create(pthread_t *p, pthread_attr_t *a, void *(*f)(void*), void *x); 创建一个线程,这个线程运行函数指针f指向的过程,x是这个函数指针的入口参数。a是 这个线程的属性值,可以为这个属性设置CPU号、堆栈大小等等属性。返回0值表示成功 创建一个线程,线程号存放在p所指向的空间;返回非0表示创建线程失败。 int pthread_delete_np(pthread_t thread); 删除一个线程,并且释放该线程的所有资源。返回0表示成功删除,非0表示删除失败。 int pthread_setschedparam(pthread_t thread, int policy, const struct sched_p aram*); 设置一个线程的调度参数,用policy和sched_param两个参数设置thread的调度参数属性 。policy = SCHED_RR的时候使用Round-Robin调度方法进行调度;policy = SCHED_FIF O的时候使用先进先出的方法进行调度。返回值为0表示调度成功,否则就是失败。 int pthread_getschedparam(pthread_t thread, int*policy, const struct sched_p aram*); 获得一个线程的调度参数。将获得的policy和sched_param 结构放在入口参数所指向的 地址里面。 int pthread_attr_init(pthread_attr_t* attr); 初始化线程运行的属性值。 pthread_t pthead_self(void); 获得当前正在运行的线程号。 int pthread_attr_getcpu_np(pthread_att_t *attr, int* cpu); 设置当前的线程属性的CPU号。 clockid_t rtl_getschedclock(void); 获得当前调度方法的时钟。 int rtl_setclockmode (clockid_t clock, int mode, hrtime_t mode_param); 设置当前时钟模式,mode = RTL_CLOCK_MODE_ONESHOT时是非周期状态,mode_param参数 无用;mode = RTL_CLOCK_MODE_PERIODIC时是周期状态,mode_param参数是周期的长度 。 int pthread_wait_np(void); 当前周期的线程运行结束。总是返回0值。 4.5.1.2 时间控制函数 void clock_getres(clockid_t clock, struct timespec* resolution); 获得clock时钟的解析度,并且将解析度存放在resolution所指向的地址里面。 4.5.1.3 FIFO控制函数 int rtf_create(unsigned int fifo, int size); 创建一个FIFO设备,该FIFO设备为第fifo个,缓冲区大小为size字节。 int rtf_destroy(unsigned int fifo); 销毁第fifo个FIFO设备。 4.5.2 程序原理 该程序的原理是测出在RT Linux中进行实时任务调度过程中调度需要花费时间的多少。 算法如下: /*实时任务端*/ 对于每500个周期 等待上一个周期的任务完成 获得当前时间和上次周期任务完成时间的差,就是调度的时间 循环 向FIFO输出500个周期中完成的最大值和最小值。 /*应用程序端*/ 读取FIFO设备,获取最大值和最小值 在屏幕上打印出来 这种编程方法是进行RT Linux编程的通用方法,将一个任务分为实时部分和非实时部分 ,在实时部分完成的是实时任务;在非实时部分主要是完成显示等不需要实时的功能。 程序的体系结构如下所示: 4.5.3 程序实现 正如上面所说的,程序分为两个部分,实时部分和非实时部分。实时部分通过使用一个 模块,在将该实时模块插入之后,运行实时任务。对于非实时部分,实现对FIFO设备的 读取,完成和实时任务的通信就可以了。 4.5.3.1 实时部分代码 1)init_module() _________________________________________________________init_module() int init_module(void) { pthread_attr_t attr; struct sched_param sched_param; int thread_status; int fifo_status; rtf_destroy(0); fifo_status = rtf_create(0, 4000);/*创建/dev/rtf0用来通信*/ if (fifo_status) { rtl_printf("FIFO Create failed. fifo_status=%d\n",fifo_statu s); return -1; } rtl_printf("RTL measurement module on CPU %d\n",rtl_getcpuid()); pthread_attr_init (&attr); pthread_attr_setcpu_np(&attr, 0);/*第一个CPU*/ sched_param.sched_priority = 1;/*调度参数*/ pthread_attr_setschedparam (&attr, &sched_param); rtl_printf("Begin to create thread\n"); thread_status = pthread_create (&task1, &attr, thread_code, (void * )1); /*创建一个实时任务的线程,线程代码过程为thread_code*/ if (thread_status != 0) { rtl_printf("failed to create RT-thread: %d\n", thread_status ); return -1; } else {/*成功创建*/ rtl_printf("created RT-thread\n"); } return 0; } 2)cleanup_module() _____________________________________________________cleanup_module() void cleanup_module(void) { pthread_delete_np(task1);/*删除该线程*/ close(fifo);/*关闭/dev/rtf0的文件描述符*/ rtf_destroy(0);/*销毁该FIFO设备*/ } _____________________________________________________________________ 3)实时任务过程thread_code(): _________________________________________________________thread_code() void *thread_code(void* param) { hrtime_t expected, diff, now, min, max,;/*纳秒为单位*/ struct data samp;/*写到FIFO设备的形式*/ int i; int count = 0; DECLARE_CPUID(cpu_id);/*获得CPU 号,前面指定为第一个CPU*/ rtl_printf ("Measurement task starts on CPU %d\n", cpu_id); if (mode) {/*周期模式*/ int ret = rtl_setclockmode (rtl_getschedclock(), RTL_CLOCK_MODE_PERIODIC, period); if (ret != 0) { rtl_printf("Setting periodic mode failed\n"); mode = 0;/*仍然是oneshot模式*/ } } if (mode) {//周期模式 struct timespec resolution; clock_getres (rtl_getschedclock(), &resolution); period = timespec_to_ns (&resolution);/*从时钟里获得解析度*/ } else {//oneshot模式 rtl_setclockmode (rtl_getschedclock(), RTL_CLOCK_MODE_ONESHOT, 0); } fifo = open("/dev/rtf0", O_NONBLOCK);/*非阻塞模式打开rtf0*/ /*使用rtf0来和Linux进程进行通信*/ if (fifo < 0) {/*打开失败*/ rtl_printf("Error in opening /dev/rtf0: returned %d\n", fifo); return (void *) -1; } expected = clock_gethrtime(rtl_getschedclock()) + 5*period;/*开始时间*/ pthread_make_periodic_np(pthread_self(), expected, period); /*设置调度的开始时间和周期*/ /*死循环*/ do { min = 2000000000;//2 seconds. max = -2000000000;//-2 seconds. for (i = 0; i < ntests; i++) {/**/ ++count; pthread_wait_np(); now = clock_gethrtime(CLOCK_REALTIME); if (!mode) { if (now < expected) { rtl_delay (expected - now); } now = clock_gethrtime(CLOCK_REALTIME); }/*if*/ diff = now - expected; if (diff < min) { min = diff; } if (diff > max) { max = diff; } expected += period;/*下一个周期的开始时间*/ }/*for*//*一次循环结束,得到最大值最小值*/ samp.min = min; samp.max = max; write (fifo, &samp, sizeof(samp));/*向FIFO设备写*/ } while (1);/*无穷的循环*/ return 0; } _____________________________________________________________________ 4)程序头 ______________________________________________________mymeasurement.c #include
#include
#include
#include
#include
#include
#include
#include
#include
#include “myheader.h” pthread_t task1; int ntests = 100; int period = 100000; int mode = 0; /*可以向该模块传入下面三个整型参数*/ MODULE_PARM(ntests, "i");/*每次循环的遍数*/ MODULE_PARM(period, "i");/*周期的长度*/ MODULE_PARM(mode, "i");/*模式,0表示oneshot, 1表示周期性*/ int fifo;/*FIFO设备打开后的文件描述符*/ _____________________________________________________________________ 4.5.3.2 非实时部分 非实时部分实际上就是一个应用程序,可以在控制台下编写,也可以在X-Windows下编写 。不过需要获得对/dev/rtf0的读的权限的用户才能运行。一般将rtf设备设置为所有用 户可读,只对超级用户可写。 1)主函数 _______________________________________________________________main() int main(int argc, char* argv[]) { int fd0;/*用于打开rtf0设备*/ int n; struct data samp; if ((fd0 = open("/dev/rtf0", O_RDONLY)) < 0) { fprintf(stderr, "Error opening /dev/rtf0\n"); exit(1); } printf("In every hundreds of loops, the schedule accuracy test.\n"); printf("MIN\t\tMAX\n"); while (1) { n = read(fd0, &samp, sizeof(samp)); printf("%8d, %8d\n", (int) samp.min, (int) samp.max); /*打印出来*/ fflush(stdout);/*标准输出清空*/ } return 0; } _____________________________________________________________________ 2)程序头 _____________________________________________________________moniter.c #include
#include
#include
#include
#include
#include
#include
#include
#include "myheader.h" _____________________________________________________________________ 4.5.3.3 公用头文件 该头文件定义了向FIFO写数据的格式。文件名为myheader.h ___________________________________________________________myheader.h struct sample {/*调度的最长时间和最短时间*/ hrtime_t min; hrtime_t max; };/*myheader.h*/ _____________________________________________________________________ 4.5.4 程序执行结果 在一台Pentium120,40M内存,RTLinux2.2的机器上,如下进行编译: #gcc -I/usr/src/rtlinux-2.2/linux/include -I/usr/src/rtlinux-2.2/include -I/ usr/src/rtlinux-2.2 -I/usr/src/rtlinux-2.2/include/posix -Wall -Wstrict-prot otypes -O2 -fomit-frame-pointer -D__RTL__ -D__KERNEL__ -DMODULE -pipe -fno-s trength-reduce -m386 -DCPU=386 -c -o mymeasument.o mymeasument.c #gcc -I/usr/src/rtlinux-2.2/linux/include -I/usr/src/rtlinux-2.2/include -I/ usr/src/rtlinux-2.2 -Wall -O2 -o monitor monitor.c 生成两个文件mymeasument.o和monitor,前者是模块方式的实时任务,后者是非实时部 分的应用程序。如下执行程序: #insmod mymeasument.o ntests=500 period=1000000 mode=0 说明是采用每次进行500个循环寻找最大值和最小值,每次进入循环的周期为1百万个纳 秒,也即1个毫秒,使用的是oneshot的方式。 #./monitor In every hundreds of loops, the schedule accuracy test. MIN MAX 12288, 12224 12224, 12320 12256, 12320 12192, 12320 ……(单位为纳秒) 如果如下执行程序: #insmod mymeasument.o ntests=500 period=1000000 mode=1 说明是采用每次进行500个循环寻找最小值和最小值,每次进入循环的周期为1百万个微 妙,也即1个毫秒,使用的是periodic模式。 #./monitor In every hundreds of loops, the schedule accuracy test. MIN MAX 5978, 5974 5976, 5978 5970, 5972 5968, 5972 5978, 5972 5970, 5966 ……(单位为纳秒) 4.6 嵌入式RTLinux的设计 Linux因为本身的特性,使得我们可以利用Linux内核做出很多有意义的事情。比如,把 Linux内核放在一张软盘上,并且利用ramdisk技术在内核导入到内存之后在内存中申请 出一片空间,存放root文件系统。我们也可以使用Linux做一个网络启动的无盘系统。我 们可以设想两个通过现有的RTLinux实现嵌入式RTLinux的方案: 4.6.1 将RTLinux嵌入EPROM中 我们可以成功的把Linux内核嵌入在EPROM中,从而可以在嵌入式设备中利用Linux内核启 动系统。因为毕业设计过程中不能提供使用EPROM的条件,我先用一张1.44M软盘作为在 EPROM中的固体状态盘(Solid State Disk. SSD)的模拟,让这张软盘可以启动一个系 统。如果这样可以成功的话,就可以把软盘的数据写到EPROM的SSD中去,并且把对1.44 M 3.5寸软盘的驱动改成对EPROM的驱动程序就可以完成设计任务。 我们可以发现,在软盘里面提供必要的工具,我们就可以把RTLinux提供的那几个实时模 块加进去,从而形成一个实时任务运行环境,这样的工作很有意义,因为这样做出来的 系统经过少许改动就可以利用于嵌入式环境的系统。 实现这样一个系统的技术支持是使用Linux的ramdisk技术,在系统启动之后,通过在内 存里面申请出一块空间,作为虚拟的硬盘空间,然后把软盘里面的数据装载上来。 这张软盘准备使用Linux Router Project的工作成果。Linux Router Project是一个专 门开发将Linux嵌入在一张软盘上并且能启动起一个包含有路由功能、瘦服务器、瘦客户 端功能、网络应用等功能的系统。在这个工作的基础之上,自己定制一个Linux内核,并 且将实时功能嵌入,从而让这张软盘启动的一个系统不仅仅是一个提供了Web服务器和t elnet服务器的系统,而且还是一个实时任务运行的环境。 实现这样的环境有个最大的难点,就在于如何将内核的体积变小。因为在嵌入式系统中 ,对Linux内核这种现代操作系统得的强大功能需求并不是全部都需要,而应该根据需要 实现的功能,自行定制合适的内核。在功能和体积上都能满足需求的内核是我们的目的 ,在嵌入到EPROM中或者Flash RAM中的时候,也能节省很大的一笔费用。 实现内核定制,从一定程度上说,就是我们在嵌入式系统开发过程中的“个性化定制内 核”的说法,需要将内核的所占体积尽量缩小,而不应该在内核中留有不需要的功能。 进行这样的工作需要有内核在线调试环境,作为毕业设计中的一部分内容,在第六章我 将详细介绍Linux内核调试环境的搭建,提供自行定制Linux内核的方法。 下面介绍如何自己做一张用来支持RT Linux内核的软盘系统。 1)需要自己做一个在软盘系统启动之后作为根文件系统的压缩文件。在Linux内核中对 ramdisk的支持可以直接支持使用gzip压缩方法的压缩文件作为一个根文件系统装载,具 体做法使先将这个文件解压缩,然后装载到/dev/ram0中。注意,/dev/ram0并不是一个 硬盘,而是从内存重划分出去的一块作为根文件系统。在系统启动的时候可以给内核传 递参数指定ramdisk的大小,也可以利用rdev命令直接进行操作。在根文件系统需要提供 一些简单的应用程序和自己需要的一些函数库、RT Linux需要的模块等等,可以自行定 制。 ramdisk的做法如下步骤: #dd if=/dev/zero of=/dev/ram bs=1k count=4096 先将/dev/ram设备前4096字节清零,并且该ramdisk大小为4M; #mkfs.minix –vm0 /dev/ram 4096 创建文件系统,因为minix文件系统支持比ext2文件系统支持需要的内核体积小,因此采 用minix文件系统而不使用ext2文件系统; 然后将/dev/ram装载上来,把自己需要的东西都拷贝到这个文件系统下面,然后卸载该 文件系统。然后: #dd if=/dev/ram bs=1k count=4096 | gzip –v9 > /tmp/ram_image.gz ram_image.gz就是这张软盘的操作系统在启动的时候需要装载的根文件系统。 定制自己的内核,假设这个内核大小为420K,那么将内核写到软盘上。使用dd命令直接 写: #dd if=zImage of=/dev/fd0 bs=1k 然后将ramdisk写到内核之后的位置,现在这种情况可以写在420K之后,1440K-ramdisk 大小的任意一处,这里写到430K的位置: #dd if=/tmp/ram_image.gz of=/dev/fd0 bs=1k seek=430 然后修改/dev/fd0的启动参数,利用rdev命令完成,使用rdev可以将内核启动参数直 接写到/dev/fd0上面。关于内核的ramdisk参数,是用两个字节表示的,从0到10位表示 的是ramdisk的位置(2^11表示最大的偏移量,为2096Bytes),第15位表示是否需要提示 插入ramdisk的软盘,第14位表示需要导入ramdisk,其它位目前还没有意义。那么,我 们现在的软盘参数应该是2^15+2^14+430=49582。如下操作: #rdev /dev/fd0 /dev/fd0 #rdev –r /dev/fd0 48952 然后使用这张软盘,就可以启动一台无盘,8M内存的机器。在根文件系统种存放有RT L inux的启动模块和程序,就可以正常运行RT Linux了。 4.6.2 通过网络启动的方法设计嵌入式RTLinux。 Linux提供了一种很方便的方式来创建无盘工作站,这样的无盘工作站可以从一台网络服 务器启动。网络启动的时候,通过网络获得Linux内核、驱动程序模块和应用程序。这样 的启动过程如下所示; ①从bootp或者dhcp服务器获得这台机器的IP地址; ②从一台tftp服务器获得Linux的内核; ③从一台NFS服务器上装载文件系统; ④必要的话,可以装载进X-Server软件并且运行X Windows; ⑤运行应用程序。 4.7 小结 本章详细介绍了Linux的实时变种——RT Linux的实现原理、编程原理和示例。RT Linu x是一种通过Linux实现的“硬实时系统”,系统的性能很好。 在4.6介绍了两种利用Linux创建“嵌入式系统”的方法:使用一张软盘模拟EPROM环境和 使用网络自举的系统设想。这些在Linux下都已经得到实现。
欢迎光临 黑色海岸线论坛 (http://bbs.thysea.com/)
Powered by Discuz! 7.2