课程设计(论文)-Linux内核初起代码分析.doc_第1页
课程设计(论文)-Linux内核初起代码分析.doc_第2页
课程设计(论文)-Linux内核初起代码分析.doc_第3页
课程设计(论文)-Linux内核初起代码分析.doc_第4页
课程设计(论文)-Linux内核初起代码分析.doc_第5页
免费预览已结束,剩余15页可下载查看

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

计算机科学与工程学院 课程设计报告 题目全称:linux 内核初起代码分析 学生学号: 姓名: 指导老师: 职称: 指导老师评语: 签字: 课程设计成绩: 设计过程表现设计报告质量总分 linux 内核初起代码分析 目录目录 摘摘 要要1 第一章第一章 引引 言言.1 1.1 问题的提出问题的提出1 1.2 任务与分析任务与分析.1 第二章第二章 代码分析代码分析.2 2.1 系统初始化过程流程.2 2.2 数据结构 2 2.3 常量和出错信息的意义.4 2.4 调用关系图.4 2.5 各模块/函数的功能及详细框图5 2.5.1 static void time_init(void)分析.6 2.5.2 void main(void)分析.6 2.5.3 pause()分析8 2.5.4 static int printf(const char *fmt, .)分析8 2.5.5 void init(void)分析9 第三章第三章 内核调试内核调试.12 3.1 运行环境 12 3.2 编译内核过程 12 第四章第四章 总结与体会总结与体会 .15 致致 谢谢16 参考文献参考文献 17 linux 内核初起代码分析 摘摘 要要 随着计算机的普及,计算机发挥着越来越重要的作用,计算机的使用也越来越普遍,所以让更 多的人能够更好的使用和掌握一些计算机方法显得十分重要。充分发挥计算机的作用也显得十分重 要。操作系统应运而生。操作系统是一种软件,用来帮助其他的程序控制计算机并和用户进行交互。 因而,对操作系统的研究是很有必要的。操作系统包含了多个部分或者组件,最核心的部分是内核。 其他的部分用来帮助内核完成计算机资源的管理和应用程序的控制。linux 操作系统是使用很广泛 的,高质量的一个操作系统。此次起始代码分析,我分析了 init/main.c 文件中的 main()、init() 以及编译内核代码。main()中主要是关于起始的调用和设备和系统信息初始化,以及创建进程。此 时中断仍被禁止着,做完必要的设置后就将其开启 init()是创建进程,并检测是否出错,出错则 再次创建执行并打印出出错信息。init()函数运行在任务 0 第 1 次创建的子进程(任务 1)中。 它首先对第一个将要执行的程序(shell)的环境进行初始化,然后加载该程序并执行之。对 linux 初起代码的分析有助于了解操作系统的启动,可以更好地理解和认识操作系统是如何管理计 算机资源的。 关键词关键词:操作系统;linux;初起代码 linux 内核初起代码分析 -1- 第一章第一章 引引 言言 1.1 问题的提出问题的提出 操作系统是一种软件,用来帮助其他的程序控制计算机并和用户进行交互。操作系统包含了众 多程序用来控制计算机的核心功能,并且操作系统是链接用户和计算机硬件的桥梁,便于人们有效 管理。尽管在过去操作系统取得了长足的进步,但是基本的目标并未改变:通过使用操作系统来处 理公共任务,程序员便可以更容易地编写应用程序。 应用程序是一种软件,用来向计算机的用户提供某种服务,而不仅仅是控制计算机硬件。尽管 在外观上和功能上有所不同,但是所有的操作系统都具有一些相同之处:初始化计算机硬件,以便 操作系统和其他持续可以正常工作;为使用操作系统的程序分配系统资源,如内存和处理时间;跟 踪调试运行的多个程序;为所有使用系统设备的程序提供规范的访问接口。 操作系统包含了多个部分或者组件,最核心的部分是内核。其他的部分用来帮助内核完成计算 机资源的管理和应用程序的控制。操作系统控制了计算机上运行的各种应用程序。没有操作系统各 类函数的调用,应用程序就无法执行。因而,对操作系统的研究是很有必要的。 linux 操作系统 是使用很广泛的,高质量的一个操作系统,而且作为一个开源的系统,可以很方便的查看起代码并 进行分析,有利于更好的认识和了解操作系统。此次对 linux 初起代码的分析有助于了解操作系 统的启动,可以更好地理解和认识操作系统是如何管理计算机资源的。 1.2 任务与分析任务与分析 本课题主要的目的是了解一个操作系统的初起过程。根据操作系统的基础知识,分析 init/main.c 中关于系统初起的相关代码,了解一个操作系统的初起过程,得到相关的框图,写出 设计说明书。 1)代码分析结果, 包括但不限于: 2)数据结构 3)常量和出错信息的意义 4)调用关系图 5)各模块/函数详细框图 分析思路: 1)了解基础知识,找到相关的源码; 2)对代码充分阅读,先得到单个函数的数据结构和框图; 3)将多个函数的框图汇总,绘出整体的框图; 使用的源代码是 linux/init/main.c (c) 1991 linus torvalds linux 内核初起代码分析 -2- 第二章第二章 代码分析代码分析 2.1 系统初始化过程流程系统初始化过程流程 系统整个初始化过程见图 2.1 所示: 进程 n 进程 1 开始 系统初始化 对物理内存各部分进 行功能划分和分配 系统各个部分初始化, 包括对任务 0 初始化 移到任务 0 中执行 创建进程 1(init) 空闲时执行 pause() 加载根文件系统 设置终端标准 io 创建进程 2 循环等待进程 2 退 出 创建子进程 循环等待进程结束 任务进程 0 终端输入定向到 rc 执行 shell 退 出 设置终端标准 io 执行 shell 退 出 进程 2 图2.1 内核初始化程序流程示意图 2.2 数据结构数据结构 1) 时间结构: #define clocks_per_sec 100/* 系统时钟滴答频率,100hz */ typedef long clock_t;/* 从进程开始系统经过的时钟滴答数 */ struct tm int tm_sec;/* 秒数 0,59 */ int tm_min;/* 分钟数 0,59 */ int tm_hour;/* 小时数 0,59 */ int tm_mday;/* 1 个月的天数 0,31 */ int tm_mon;/* 1 年中月份 0,11 */ int tm_year;/* 从 1900 年开始的年数 */ int tm_wday;/* 1 星期中的某天 0,6(星期天=0) */ linux 内核初起代码分析 -3- int tm_yday;/* 1 年中的某天 0,365 */ int tm_isdst;/* 夏令时标志 */ ; 2) 存放硬盘参数表信息: struct drive_info char dummy32; drive_info;/* 用于存放硬盘参数表信息 */ 3) tty 等待队列数据结构和 tty 数据结构: struct tty_queue unsigned long data;/* 等待队列缓冲区中当前数据指针字符数 */ unsigned long head;/* 缓冲区中数据头指针 */ unsigned long tail;/* 缓冲区中数据尾指针 */ struct task_struct *proc_list; /* 等待进程列表 */ char buftty_buf_size;/* 队列的缓冲区 */ ; struct tty_struct /* tty 数据结构 */ struct termios termios;/* 终端 io 属性和控制字符数据结构 */ int pgrp;/* 所属进程组 */ int stopped;/* 停止标志 */ void (*write) (struct tty_struct * tty); /* tty 写函数指针 */ struct tty_queue read_q;/* tty 读队列 */ struct tty_queue write_q; /* tty 写队列 */ struct tty_queue secondary; /* tty 辅助队列(存放规范模式字符序列) */ ;/* 可称为规范(熟)模式队列 */ 4) 请求队列中项的结构和块设备结构: struct request /* 请求队列中项的结构。其中如果 dev=-1,则表示该项没有被使用 */ int dev;/* 使用的设备号 */ int cmd;/* 命令(read 或 write) */ int errors;/* 操作时产生的错误次数 */ unsigned long sector;/* 起始扇区(1 块=2 扇区) */ unsigned long nr_sectors; /* 读/写扇区数 */ char *buffer;/* 数据缓冲区 */ struct task_struct *waiting; /* 任务等待操作执行完成的地方 */ struct buffer_head *bh;/* 缓冲区头指针(include/linux/fs.h,68) */ struct request *next;/* 指向下一请求项 */ ; struct blk_dev_struct /* 块设备结构 */ void (*request_fn) (void); /* 请求操作的函数指针 */ struct request *current_request; /* 请求信息结构 */ ; linux 内核初起代码分析 -4- 2.3 常量和出错信息的意义常量和出错信息的意义 定义系统调用嵌入式汇编宏函数。不带参数的系统调用宏函数。type name(void)。%0 - eax(_res),%1 - eax(_nr_#name)。其中 name 是系统调用的名称,与 _nr_ 组合形成上面的 系统调用符号常数,从而用来对系统调用表中函数指针寻址。返回:如果返回值大于等于 0,则返 回该值,否则置出错号 errno,并返回-1。 #define _syscall0(type,name) type name(void) long _res; _asm_ volatile ( “int $0x80“ /* 调用系统中断 0x80 */ :“=a“ (_res) /* 返回值 eax(_res) */ :“ (_nr_ #name); /* 输入为系统中断调用号_nr_name */ if (_res = 0) /* 如果返回值=0,则直接返回该值 */ return (type) _res; errno = -_res; /* 否则置出错号,并返回-1 */ return -1; /* 有 1 个参数的系统调用宏函数。type name(atype a) */ /* %0 - eax(_res),%1 - eax(_nr_name),%2 - ebx(a) */ #define _syscall1(type,name,atype,a) type name(atype a) long _res; _asm_ volatile ( “int $0x80“ : “=a“ (_res) : “ (_nr_#name), “b“ (long)(a); if (_res = 0) return (type) _res; errno = -_res; return -1; extern int errno;/* 出错号,全局变量 */ static inline _syscall0(int,fork) /*这是unistd.h 中的内嵌宏代码。以嵌入汇编的形式调用linux 的系统调用中断0x80。该中断 是所有系统调用的入口。该条语句实际上是int fork()创建进程系统调用。syscall0 名称中最后 的0 表示无参数,1 表示1 个参数 */ static inline _syscall0(int,pause) /* int pause()系统调用:暂停进程的执行,直到收到一个信号 */ static inline _syscall1(int,setup,void *,bios) /* int setup(void * bios)系统调用,仅用于linux 初始化(仅在这个程序中被调用)*/ static inline _syscall0(int,sync) /* int sync()系统调用更新文件系统 */ 2.4 调用关系图调用关系图 在内核源代码的init/目录中只有一个main.c 文件。系统在执行完boot/目录中的head.s 程序后就 linux 内核初起代码分析 -5- 会将执行权交给main.c。该程序虽然不长,但却包括了内核初始化的所有工作。 main.c 程序首先利用前面setup.s 程序取得的系统参数设置系统的根文件设备号以及一些内存 全局变量。这些内存变量指明了主内存的开始地址、系统所拥有的内存容量和作为高速缓冲区内存 的末端地址。如果还定义了虚拟盘(ramdisk),则主内存将适当减少。整个内存的映像示意图 见图3.1 所示。 内核程序高速缓存主内存区虚拟盘 图 2.1 系统中内存功能划分示意图 图中,高速缓冲部分还要扣除被显存和rom bios 占用的部分。高速缓冲区是用于磁盘等块 设备临时存放数据的地方,以1k(1024)字节为一个数据块单位。主内存区域的内存是由内存管 理模块mm通过分页机制进行管理分配,以4k 字节为一个内存页单位。内核程序可以自由访问高 速缓冲中的数据,但需要通过mm 才能使用分配到的内存页面。 然后,内核进行所有方面的硬件初始化工作。包括陷阱门、块设备、字符设备和 tty,包括人 工设置第一个任务(task 0)。待所有初始化工作完成后就设置中断允许标志以开启中断,main()也 切换到了任务 0 中运行。 在整个内核完成初始化后,内核将执行权切换到了用户模式(任务0),也即cpu 从0 特权级 切换到了第3 特权级。此时main.c 的主程序就工作在任务0 中。然后系统第一次调用进程创建函数 fork(),创建出一个用于运行init()的子进程。 2.5 各模块各模块/函数的功能及详细框图函数的功能及详细框图 该程序首先确定如何分配使用系统物理内存,然后调用内核各部分的初始化函数分别对内存管 理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘硬件进行初始化处理。在完成了这些 操作之后,系统各部分已处于可运行状态。此后程序把自己“手工”移动到任务0(进程0)中运行, 并使用fork()调用首次创建出进程1(init 进程)。在init进程中程序将继续进行应用环境的初始化并 执行shell 登录程序。而原进程0则会在系统空闲时被调度执行,此时任务0仅执行pause()系统调用, 并又会调用调度函数。 在init 进程中,如果终端环境建立成功,则会再生成一个子进程(进程2),用于运行shell 程 序/bin/sh。若该子进程退出,则父进程进入一个死循环内,继续生成子进程,并在此子进程中再次 执行shell 程序/bin/sh,而父进程则继续等待。 由于创建新进程的过程是通过完全复制父进程代码段和数据段的方式实现的,因此在首次使用 fork()创建新进程init 时,为了确保新进程用户态堆栈没有进程0 的多余信息,要求进程0 在创建首 个新进程之前不要使用用户态堆栈,也即要求任务0 不要调用函数。因此在main.c 主程序移动到任 务0 执行后,任务0 中的代码fork()不能以函数形式进行调用。程序中实现的方法是采用gcc 函数 内嵌的形式来执行这个系统调用。 通过申明一个内嵌(inline)函数,可以让gcc 把函数的代码集成到调用它的代码中。这会提高 代码执行的速度,因为省去了函数调用的开销。另外,如果任何一个实际参数是一个常量,那么在 编译时这些已知值就可能使得无需把内嵌函数的所有代码都包括进来而让代码也得到简化。 另外,任务0 中的pause()也需要使用函数内嵌形式来定义。如果调度程序首先执行新创建的子 进程init,那么pause()采用函数调用形式不会有什么问题。但是内核调度程序执行父进程(进程0) 和子进程init 的次序是随机的,在创建了init 后有可能首先会调度进程0 执行。因此pause()也必须 采用宏定义来实现。 对于linux 来说,所有任务都是在用户模式运行的,包括很多系统应用程序,如shell 程序、 网络子系统程序等。内核源代码lib/目录下的库文件就是专门为这里新创建的进程提供支持函数的。 linux 内核初起代码分析 -6- 2.5.1 static void time_init(void)分析分析 该子程用于读取取 cmos 时钟,并设置开机时间 startup_time(秒)。 struct tm time; /* 时间结构 tm 定义在 include/time.h 中 */ do time.tm_sec = cmos_read(0); /* 当前时间秒值(均是 bcd 码值)*/ time.tm_min = cmos_read(2); /* 当前分钟值 */ time.tm_hour = cmos_read(4); /* 当前小时值 */ time.tm_mday = cmos_read(7); /* 一月中的当天日期 */ time.tm_mon = cmos_read(8); /* 当前月份(112)*/ time.tm_year = cmos_read(9); /* 当前年份 */ while (time.tm_sec != cmos_read(0); cmos 的访问速度很慢。为了减小时间误差,在读取了下面循环中所有数值后,若此时 cmos 中秒值发生了变化,那么就重新读取所有值。 bcd_to_bin(time.tm_sec); /* 转换成二进制数值 */ bcd_to_bin(time.tm_min); bcd_to_bin(time.tm_hour); bcd_to_bin(time.tm_mday); bcd_to_bin(time.tm_mon); bcd_to_bin(time.tm_year); time.tm_mon-; /* tm_mon 中月份范围是 011 */ startup_time = kernel_mktime( /* 调用 kernel/mktime.c 中函数,计算从 1970 年 1 月 1 日 0 时起到开机当日经过的秒数,作为 开机时间 */ 2.5.2 void main(void)分析分析 main()函数中完成启动时对设备内核初始化,以及创建进程。此时中断仍被禁止着,做完必要 的设置后就将其开启。下面这段代码用于保存:根设备号:root_dev; 高速缓存末端地址: buffer_memory_end;机器内存:memory_end;主内存开始地址 :main_memory_start; root_dev = orig_root_dev; /* root_dev 定义在 fs/super.c */ drive_info = drive_info; /* 复制 0x90080 处的硬盘参数表 */ memory_end = (1 16*1024*1024) /* 如果内存超过 16mb,则按 16mb 计 */ memory_end = 16*1024*1024; if (memory_end 12*1024*1024) /* 如果内存12mb,则设置缓冲区末端=4mb */ buffer_memory_end = 4*1024*1024; else if (memory_end 6*1024*1024) /* 否则如果内存6mb,则设置缓冲区末端=2mb */ buffer_memory_end = 2*1024*1024; else buffer_memory_end = 1*1024*1024; /* 否则则设置缓冲区末端=1mb */ linux 内核初起代码分析 -7- main_memory_start = buffer_memory_end; /* 主内存起始位置=缓冲区末端 */ /* 如果定义了内存虚拟盘,则初始化虚拟盘。此时主内存将减少。参见 kernel/blk_drv/ramdisk.c。*/ #ifdef ramdisk main_memory_start += rd_init(main_memory_start, ramdisk*1024); #endif mem_init(main_memory_start,memory_end); /* 内核进行所有方面的初始化工作 */ trap_init(); /* 陷阱门(硬件中断向量)初始化。(kernel/traps.c) */ blk_dev_init(); /* 块设备初始化。 (kernel/blk_drv/ll_rw_blk.c)*/ chr_dev_init(); /* 字符设备初始化。 (kernel/chr_drv/tty_io.c)*/ tty_init(); /* tty 初始化。 (kernel/chr_drv/tty_io.c)*/ time_init(); /* 设置开机启动时间:startup_time */ sched_init(); /* 调度程序初始化(加载了任务 0 的 tr,ldtr)(kernel/sched.c) */ buffer_init(buffer_memory_end); /* 缓冲管理初始化,建内存链表等。(fs/buffer.c) */ hd_init(); /* 硬盘初始化。 (kernel/blk_drv/hd.c)*/ floppy_init(); /* 软驱初始化。 (kernel/blk_drv/floppy.c)*/ sti(); /* 所有初始化工作都做完了,开启中断 */ /* 下面过程通过在堆栈中设置的参数,利用中断返回指令启动任务 0 执行 */ move_to_user_mode(); /* 移到用户模式下执行。(include/asm/system.h)*/ if (!fork() init(); /* 在新建的子进程(任务 1)中执行 */ main()流程图如图 2.2: linux 内核初起代码分析 -8- yn 开始 内存起始分配 启动设备和程序初始化 开启中断 切换到用户模式 fork()!=0 调用 init()初始化 调用 pause ()运行任务 0 结 束 图 2.2 main()流程图 2.5.3 pause()分析分析 代码开始以任务 0 的身份运行。对于任何其它的任务,pause()将意味着我们必须等待收到一个 信号才会返回就绪运行态,但任务 0(task0)是唯一的例外情况,因为任务 0 在任何空闲时间里都 会被激活(当没有其它任务在运行时),因此对于任务 0 pause()仅意味着我们返回来查看是否有其 它任务可以运行,如果没有的话我们就回到这里,一直循环执行 pause()。 pause()系统调用(kernel/sched.c,144)会把任务 0 转换成可中断等待状态,再执行调度函数。 但是调度函数只要发现系统中没有其它任务可以运行时就会切换到任务 0,而不依赖于任务 0 的状 态。 2.5.4 static int printf(const char *fmt, .)分析分析 产生格式化信息并输出到标准输出设备 stdout(1),这里是指屏幕上显示。参数*fmt指定输出将 采用的格式。该子程序正好是 vsprintf 如何使用的一个例子。该程序使用 vsprintf()将格式化的字符 串放入 printbuf 缓冲区,然后用 write()将缓冲区的内容输出到标准设备(1-stdout)。 static int printf(const char *fmt, .) va_list args; int i; va_start(args, fmt); write(1,printbuf,i=vsprintf(printbuf, fmt, args); va_end(args); return i; linux 内核初起代码分析 -9- 2.5.5 void init(void)分析分析 argv0中的字符“-”是传递给 shell 程序 sh 的一个标志。通过识别该标志,sh 程序会作为登录 shell 执行。其执行过程与在 shell 提示符下执行 sh 不太一样。 static char * argv_rc = “/bin/sh“, null ; /* 调用执行程序时参数的字符串数组 */ static char * envp_rc = “home=/“, null ; /* 调用执行程序时的环境字符串数组 */ static char * argv = “-/bin/sh“,null ; /* 同上 */ static char * envp = “home=/usr/root“, null ; 在 main()中已经进行了系统初始化,包括内存管理、各种硬件设备和驱动程序。init()函数运行 在任务 0 第 1 次创建的子进程(任务 1)中。它首先对第一个将要执行的程序(shell)的环境进行 初始化,然后加载该程序并执行之。 setup(void *) /* 这是一个系统调用。用于读取硬盘参数包括分区表信息并加载虚拟盘(若存在的话)和安 装根文件系统设备。该函数对应函数是 sys_setup() */ 然后以读写访问方式打开设备“/dev/tty0”,它对应终端控制台。由于这是第一次打开文件操作, 因此产生的文件句柄号(文件描述符)肯定是 0。该句柄是 unix 类操作系统默认的控制台标准输 入句柄 stdin。这里把它以读和写的方式打开是为了复制产生标准 输出(写)句柄 stdout 和标准出 错输出句柄 stderr。 (void) open(“/dev/tty0“,o_rdwr,0); (void) dup(0); /* 复制句柄,产生句柄 1 号 - stdout 标准输出设备 */ (void) dup(0); /* 复制句柄,产生句柄 2 号 - stderr 标准出错输出设备 */ 打印缓冲区块数和总字节数,每块 1024 字节,以及主内存区空闲内存字节数。 printf(“%d buffers = %d bytes buffer spacenr“,nr_buffers, nr_buffers*block_size); printf(“free mem: %d bytesnr“,memory_end-main_memory_start); fork()用于创建一个子进程(任务 2)。对于被创建的子进程,fork()将返回 0 值,对于原进程(父 进程)则返回子进程的进程号 pid。该子进程关闭了句柄 0(stdin) 、以只读方式打开/etc/rc 文件,并 使用 execve()函数将进程自身替换成/bin/sh 程序(即 shell 程序),然后执行/bin/sh 程序。所带参数 和环境变量分别由 argv_rc 和 envp_rc 数组给出。函数_exit()退出时的出错码 1 操作未许可;2 - 文件或目录不存在。 if (!(pid=fork() close(0); if (open(“/etc/rc“,o_rdonly,0) _exit(1); /* 如果打开文件失败,则退出(lib/_exit.c,10) */ execve(“/bin/sh“,argv_rc,envp_rc); /* 替换成/bin/sh 程序并执行 */ _exit(2); /* 若 execve()执行失败则退出 */ 下面还是父进程(1)执行的语句。wait()等待子进程停止或终止,返回值应是子进程的进程号 (pid)。这三句的作用是父进程等待子进程的结束。&i 是存放返回状态信息的位置。如果 wait()返回 值不等于子进程号,则继续等待。 if (pid0) while (pid != wait(&i) /* 空循环 */ 如果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建一个子 进程,如果出错,则显示“初始化程序创建子进程失败”信息并继续执行。对于所创建的子进程将关 linux 内核初起代码分析 -10- 闭所有以前还遗留的句柄(stdin, stdout, stderr),新创建一个会话并设置进程组号,然后重新打开 /dev/tty0 作为 stdin,并复制成 stdout 和 stderr。再次执行系统解释程序/bin/sh。但这次执行所选用 的参数和环境数组另选了一套。然后父进程再次运行 wait()等待。如果子进程又停止了执行,则在 标准输出上显示出错信息“子进程 pid 停止了运行,返回码是 i”,然后继续重试下去,形成一个死 循环。 while (1) if (pid=fork()0 是否等待? 再次创建? 打印创建出错 打开执行 pid= wait(&i) 打印出错信息_exit(0) 结 束 图 2.3 init()流程图 linux 内核初起代码分析 -12- 第三章第三章 内核调试内核调试 3.1 运行环境 内核编译运行于模拟 linux 环境的 bochs-2.1.1 中。 3.2 编译内核过程 1)用 bochs 运行 linux0.11,开始如图 3.1: 图 3.1 bochs 运行 linux0.11 2)进入 /usr/src/linux/init, 使用 ls 命令显示当前目录文件,可以看到我们需要的 main.c 文件,如图 3.2: 图 3.2 ls 命令显示当前目录文件 3)vi main.c 可以编译启动代码,insert 插入,esc+:wq 保存并退出,如图 3.3: 图 3.3 vi编译启动代码 4)返回 linux 目录,使用 make clean 清除源代码生成的执行文件和中间的目标文件,如图 3.4: linux 内核初起代码分析 -13- 图 3.4 make clean 5)然后使用 make 命令编译生成新的内核,如图 3.5: 图 3.5 make 命令编译生成新的内核 6)然后修复 grub 引导(这里不再赘述),重启后可看到多选菜单,默认首选就是我修改 main.c 编译 的内核,如图 3.6: 图 3.6 新建 grub 引导 7)进入后启动显示信息。首先,显示的是硬盘信息以及执行起始程序(kernel() ) ,由此可见, 启动时先要对硬件初始化和起始程序位置。然后显示硬盘是否有错误信息以及磁盘使用情况。 因为 linux 编程有严格的限制,我试着将 printf()定义移到 main()之前,修改 main()的内容并 使之显示,没有成功,我就只修改了 init()里的内容。可以看到显示打印出磁盘信息,然后用 fork()创建一个进程,然后打开/etc/rc/、执行 bin/sh,由于没有出错,就没有跳转到出错的死 循环中,没有错误信息显示,最后打印出创建进程成功。系统初始化完成,返回值 ok。由此 可以验证我们对初起代码 main.c 分析的正确性。如图 3.7: linux 内核初起代码分析 -14- 图 3.7 linux 内核初起代码分析 -15- 第四章第四章 总结与体会总结与体会 该程序首先确定如何分配使用系统物理内存,然后调用内核各部分的初始化函数分别对内

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论