课程设计(论文)Linux内核初起代码分析

上传人:1666****666 文档编号:37921727 上传时间:2021-11-05 格式:DOC 页数:20 大小:259.02KB
收藏 版权申诉 举报 下载
课程设计(论文)Linux内核初起代码分析_第1页
第1页 / 共20页
课程设计(论文)Linux内核初起代码分析_第2页
第2页 / 共20页
课程设计(论文)Linux内核初起代码分析_第3页
第3页 / 共20页
资源描述:

《课程设计(论文)Linux内核初起代码分析》由会员分享,可在线阅读,更多相关《课程设计(论文)Linux内核初起代码分析(20页珍藏版)》请在装配图网上搜索。

1、 计算机科学与工程学院课程设计报告题目全称:Linux 内核初起代码分析 学生学号: 姓名: 指导老师: 职称: 指导老师评语: 签字: 课程设计成绩:设计过程表现设计报告质量总分Linux 内核初起代码分析目录目录摘摘 要要.1第一章第一章 引引 言言.11.1 问题的提出问题的提出.11.2 任务与分析任务与分析.1第二章第二章 代码分析代码分析.22.1 系统初始化过程流程.22.2 数据结构 .22.3 常量和出错信息的意义.42.4 调用关系图.42.5 各模块/函数的功能及详细框图.52.5.1 static void time_init(void)分析.62.5.2 void m

2、ain(void)分析.62.5.3 pause()分析.82.5.4 static int printf(const char *fmt, .)分析.82.5.5 void init(void)分析.9第三章第三章 内核调试内核调试.123.1 运行环境 .123.2 编译内核过程 .12第四章第四章 总结与体会总结与体会 .15致致 谢谢.16参考文献参考文献 .17Linux 内核初起代码分析摘摘 要要随着计算机的普及,计算机发挥着越来越重要的作用,计算机的使用也越来越普遍,所以让更多的人能够更好的使用和掌握一些计算机方法显得十分重要。充分发挥计算机的作用也显得十分重要。操作系统应运而生

3、。操作系统是一种软件,用来帮助其他的程序控制计算机并和用户进行交互。因而,对操作系统的研究是很有必要的。操作系统包含了多个部分或者组件,最核心的部分是内核。其他的部分用来帮助内核完成计算机资源的管理和应用程序的控制。Linux 操作系统是使用很广泛的,高质量的一个操作系统。此次起始代码分析,我分析了 init/main.c 文件中的 main()、init()以及编译内核代码。main()中主要是关于起始的调用和设备和系统信息初始化,以及创建进程。此时中断仍被禁止着,做完必要的设置后就将其开启 init()是创建进程,并检测是否出错,出错则再次创建执行并打印出出错信息。init()函数运行在任

4、务 0 第 1 次创建的子进程(任务 1)中。它首先对第一个将要执行的程序(shell)的环境进行初始化,然后加载该程序并执行之。对Linux 初起代码的分析有助于了解操作系统的启动,可以更好地理解和认识操作系统是如何管理计算机资源的。关键词关键词:操作系统;Linux;初起代码Linux 内核初起代码分析-1-第一章第一章 引引 言言 1.1 问题的提出问题的提出 操作系统是一种软件,用来帮助其他的程序控制计算机并和用户进行交互。操作系统包含了众多程序用来控制计算机的核心功能,并且操作系统是链接用户和计算机硬件的桥梁,便于人们有效管理。尽管在过去操作系统取得了长足的进步,但是基本的目标并未改

5、变:通过使用操作系统来处理公共任务,程序员便可以更容易地编写应用程序。应用程序是一种软件,用来向计算机的用户提供某种服务,而不仅仅是控制计算机硬件。尽管在外观上和功能上有所不同,但是所有的操作系统都具有一些相同之处:初始化计算机硬件,以便操作系统和其他持续可以正常工作;为使用操作系统的程序分配系统资源,如内存和处理时间;跟踪调试运行的多个程序;为所有使用系统设备的程序提供规范的访问接口。操作系统包含了多个部分或者组件,最核心的部分是内核。其他的部分用来帮助内核完成计算机资源的管理和应用程序的控制。操作系统控制了计算机上运行的各种应用程序。没有操作系统各类函数的调用,应用程序就无法执行。因而,对

6、操作系统的研究是很有必要的。 Linux 操作系统是使用很广泛的,高质量的一个操作系统,而且作为一个开源的系统,可以很方便的查看起代码并进行分析,有利于更好的认识和了解操作系统。此次对 Linux 初起代码的分析有助于了解操作系统的启动,可以更好地理解和认识操作系统是如何管理计算机资源的。1.2 任务与分析任务与分析 本课题主要的目的是了解一个操作系统的初起过程。根据操作系统的基础知识,分析init/main.c 中关于系统初起的相关代码,了解一个操作系统的初起过程,得到相关的框图,写出设计说明书。1)代码分析结果, 包括但不限于:2)数据结构3)常量和出错信息的意义4)调用关系图5)各模块/

7、函数详细框图分析思路:1)了解基础知识,找到相关的源码;2)对代码充分阅读,先得到单个函数的数据结构和框图;3)将多个函数的框图汇总,绘出整体的框图;使用的源代码是Linux/init/main.c (C) 1991 Linus TorvaldsLinux 内核初起代码分析-2-第二章第二章 代码分析代码分析 2.1 系统初始化过程流程系统初始化过程流程系统整个初始化过程见图 2.1 所示:进程 n进程 1开始系统初始化对物理内存各部分进行功能划分和分配系统各个部分初始化,包括对任务 0 初始化移到任务 0 中执行创建进程 1(init)空闲时执行 pause()加载根文件系统设置终端标准 I

8、O创建进程 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 t

9、m_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 数据结构:str

10、uct 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;/* 所属进

11、程组 */ 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,则表示该项

12、没有被使用 */ 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)

13、*/ 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 是系统调用的名称,与 _

14、NR_ 组合形成上面的系统调用符号常数,从而用来对系统调用表中函数指针寻址。返回:如果返回值大于等于 0,则返回该值,否则置出错号 errno,并返回-1。#define _syscall0(type,name) type name(void) long _res; _asm_ volatile ( int $0 x80 /* 调用系统中断 0 x80 */:=a (_res) /* 返回值 eax(_res) */: (_NR_#name); /* 输入为系统中断调用号_NR_name */ if (_res = 0) /* 如果返回值=0,则直接返回该值 */ return (type)

15、_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 $0 x80 : =a (_res) : (_NR_#name), b (long)(a); if (_res = 0) return (typ

16、e) _res; errno = -_res; return -1; extern int errno;/* 出错号,全局变量 */static inline _syscall0(int,fork)/*这是unistd.h 中的内嵌宏代码。以嵌入汇编的形式调用Linux 的系统调用中断0 x80。该中断是所有系统调用的入口。该条语句实际上是int fork()创建进程系统调用。syscall0 名称中最后的0 表示无参数,1 表示1 个参数 */static inline _syscall0(int,pause) /* int pause()系统调用:暂停进程的执行,直到收到一个信号 */st

17、atic 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 程序首先利用前面s

18、etup.s 程序取得的系统参数设置系统的根文件设备号以及一些内存全局变量。这些内存变量指明了主内存的开始地址、系统所拥有的内存容量和作为高速缓冲区内存的末端地址。如果还定义了虚拟盘(RAMDISK),则主内存将适当减少。整个内存的映像示意图见图3.1 所示。内核程序高速缓存主内存区虚拟盘图 2.1 系统中内存功能划分示意图图中,高速缓冲部分还要扣除被显存和ROM BIOS 占用的部分。高速缓冲区是用于磁盘等块设备临时存放数据的地方,以1K(1024)字节为一个数据块单位。主内存区域的内存是由内存管理模块mm通过分页机制进行管理分配,以4K 字节为一个内存页单位。内核程序可以自由访问高速缓冲中

19、的数据,但需要通过mm 才能使用分配到的内存页面。然后,内核进行所有方面的硬件初始化工作。包括陷阱门、块设备、字符设备和 tty,包括人工设置第一个任务(task 0)。待所有初始化工作完成后就设置中断允许标志以开启中断,main()也切换到了任务 0 中运行。在整个内核完成初始化后,内核将执行权切换到了用户模式(任务0),也即CPU 从0 特权级切换到了第3 特权级。此时main.c 的主程序就工作在任务0 中。然后系统第一次调用进程创建函数fork(),创建出一个用于运行init()的子进程。2.5 各模块各模块/函数的功能及详细框图函数的功能及详细框图该程序首先确定如何分配使用系统物理内

20、存,然后调用内核各部分的初始化函数分别对内存管理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘硬件进行初始化处理。在完成了这些操作之后,系统各部分已处于可运行状态。此后程序把自己“手工”移动到任务0(进程0)中运行,并使用fork()调用首次创建出进程1(init 进程)。在init进程中程序将继续进行应用环境的初始化并执行shell 登录程序。而原进程0则会在系统空闲时被调度执行,此时任务0仅执行pause()系统调用,并又会调用调度函数。在init 进程中,如果终端环境建立成功,则会再生成一个子进程(进程2),用于运行shell 程序/bin/sh。若该子进程退出,则父进程进入一个死

21、循环内,继续生成子进程,并在此子进程中再次执行shell 程序/bin/sh,而父进程则继续等待。由于创建新进程的过程是通过完全复制父进程代码段和数据段的方式实现的,因此在首次使用fork()创建新进程init 时,为了确保新进程用户态堆栈没有进程0 的多余信息,要求进程0 在创建首个新进程之前不要使用用户态堆栈,也即要求任务0 不要调用函数。因此在main.c 主程序移动到任务0 执行后,任务0 中的代码fork()不能以函数形式进行调用。程序中实现的方法是采用gcc 函数内嵌的形式来执行这个系统调用。通过申明一个内嵌(inline)函数,可以让gcc 把函数的代码集成到调用它的代码中。这会

22、提高代码执行的速度,因为省去了函数调用的开销。另外,如果任何一个实际参数是一个常量,那么在编译时这些已知值就可能使得无需把内嵌函数的所有代码都包括进来而让代码也得到简化。另外,任务0 中的pause()也需要使用函数内嵌形式来定义。如果调度程序首先执行新创建的子进程init,那么pause()采用函数调用形式不会有什么问题。但是内核调度程序执行父进程(进程0)和子进程init 的次序是随机的,在创建了init 后有可能首先会调度进程0 执行。因此pause()也必须采用宏定义来实现。对于Linux 来说,所有任务都是在用户模式运行的,包括很多系统应用程序,如shell 程序、网络子系统程序等。

23、内核源代码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_REA

24、D(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(tim

25、e.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(&time); /* 调用 kernel/mktime.c 中函数,计算从 1970 年 1 月 1 日 0 时起到开机当日经过的秒数,作为开机时间 */2.5.2 void main(void)分析分析main()函数中完成启动时对设备内

26、核初始化,以及创建进程。此时中断仍被禁止着,做完必要的设置后就将其开启。下面这段代码用于保存:根设备号:ROOT_DEV; 高速缓存末端地址:buffer_memory_end;机器内存:memory_end;主内存开始地址 :main_memory_start; ROOT_DEV = ORIG_ROOT_DEV; /* ROOT_DEV 定义在 fs/super.c */ drive_info = DRIVE_INFO; /* 复制 0 x90080 处的硬盘参数表 */ memory_end = (120) + (EXT_MEM_K 16*1024*1024) /* 如果内存超过 16Mb

27、,则按 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

28、- main_memory_start = buffer_memory_end; /* 主内存起始位置=缓冲区末端 */* 如果定义了内存虚拟盘,则初始化虚拟盘。此时主内存将减少。参见kernel/blk_drv/ramdisk.c。*/#ifdef RAMDISKmain_memory_start += rd_init(main_memory_start, RAMDISK*1024);#endifmem_init(main_memory_start,memory_end); /* 内核进行所有方面的初始化工作 */trap_init(); /* 陷阱门(硬件中断向量)初始化。(kernel/

29、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

30、_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)中执行

31、 */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()仅意味着我们返回来查看是否有其它任务可以运行,如果没有的话

32、我们就回到这里,一直循环执行 pause()。pause()系统调用(kernel/sched.c,144)会把任务 0 转换成可中断等待状态,再执行调度函数。但是调度函数只要发现系统中没有其它任务可以运行时就会切换到任务 0,而不依赖于任务 0 的状态。2.5.4 static int printf(const char *fmt, .)分析分析产生格式化信息并输出到标准输出设备 stdout(1),这里是指屏幕上显示。参数*fmt指定输出将采用的格式。该子程序正好是 vsprintf 如何使用的一个例子。该程序使用 vsprintf()将格式化的字符串放入 printbuf 缓冲区,然后用

33、 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 提

34、示符下执行 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)中。它首

35、先对第一个将要执行的程序(shell)的环境进行初始化,然后加载该程序并执行之。setup(void *) &drive_info);/* 这是一个系统调用。用于读取硬盘参数包括分区表信息并加载虚拟盘(若存在的话)和安装根文件系统设备。该函数对应函数是 sys_setup() */然后以读写访问方式打开设备“/dev/tty0”,它对应终端控制台。由于这是第一次打开文件操作,因此产生的文件句柄号(文件描述符)肯定是 0。该句柄是 UNIX 类操作系统默认的控制台标准输入句柄 stdin。这里把它以读和写的方式打开是为了复制产生标准 输出(写)句柄 stdout 和标准出错输出句柄 stderr

36、。(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_memo

37、ry_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

38、_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) /* 空循环 */如

39、果执行到这里,说明刚创建的子进程的执行已停止或终止了。下面循环中首先再创建一个子进程,如果出错,则显示“初始化程序创建子进程失败”信息并继续执行。对于所创建的子进程将关Linux 内核初起代码分析-10-闭所有以前还遗留的句柄(stdin, stdout, stderr),新创建一个会话并设置进程组号,然后重新打开/dev/tty0 作为 stdin,并复制成 stdout 和 stderr。再次执行系统解释程序/bin/sh。但这次执行所选用的参数和环境数组另选了一套。然后父进程再次运行 wait()等待。如果子进程又停止了执行,则在标准输出上显示出错信息“子进程 pid 停止了运行,返回码

40、是 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.112)进入 /usr/src/Linux/init, 使用 ls 命令显示当前目录文件,可以看

41、到我们需要的 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 clean5)然后使用 make 命令编译生成新的内核,如图 3.5:图 3.5 make 命令编译生成新的内核6)然后修复 grub 引导(这里不再赘述),重启后可看到多选菜单,默认首选就是我修改 main.c

42、编译的内核,如图 3.6:图 3.6 新建 grub 引导7)进入后启动显示信息。首先,显示的是硬盘信息以及执行起始程序(kernel() ) ,由此可见,启动时先要对硬件初始化和起始程序位置。然后显示硬盘是否有错误信息以及磁盘使用情况。因为 LINUX 编程有严格的限制,我试着将 printf()定义移到 main()之前,修改 main()的内容并使之显示,没有成功,我就只修改了 init()里的内容。可以看到显示打印出磁盘信息,然后用fork()创建一个进程,然后打开/etc/rc/、执行 bin/sh,由于没有出错,就没有跳转到出错的死循环中,没有错误信息显示,最后打印出创建进程成功。

43、系统初始化完成,返回值 OK。由此可以验证我们对初起代码 main.c 分析的正确性。如图 3.7:Linux 内核初起代码分析-14-图 3.7Linux 内核初起代码分析-15-第四章第四章 总结与体会总结与体会该程序首先确定如何分配使用系统物理内存,然后调用内核各部分的初始化函数分别对内存管理、中断处理、块设备和字符设备、进程管理以及硬盘和软盘硬件进行初始化处理。在内核源代码的init/目录中只有一个main.c 文件。系统在执行完boot/目录中的head.s 程序后就会将执行权交给main.c。该程序虽然不长,但却包括了内核初始化的所有工作。因此在阅读该程序的代码时需要参照很多其它程

44、序中的初始化部分。而关于main.c,其中的头文件定义,需要引用到头文件等,因此要分析其数据结构,就需要查看Linux目录下的相关文件。main.c中最主要是分析main()函数调用关系,而面对大篇幅的代码,光看是很容易迷糊的,所以我选用了代码查看的软件SourceInsight。此次起始代码分析,分析了 main()、time()、init()以及各个头文件定义等。通过申明一个内嵌(inline)函数,可以让gcc 把函数的代码集成到调用它的代码中。这会提高代码执行的速度,因为省去了函数调用的开销。另外,如果任何一个实际参数是一个常量,那么在编译时这些已知值就可能使得无需把内嵌函数的所有代码

45、都包括进来而让代码也得到简化。另外,任务 0 中的 pause()也需要使用函数内嵌形式来定义。如果调度程序首先执行新创建的子进程 init,那么 pause()采用函数调用形式不会有什么问题。但是内核调度程序执行父进程(进程0)和子进程 init 的次序是随机的,在创建了 init 后有可能首先会调度进程 0 执行。因此 pause()也必须采用宏定义来实现。由于自己第一次接触内核代码,一时有些茫然,面对大量的代码无从下手。Linux 是基于 C 语言编写的,所以自然需要 C 语言更为深层的知识,借助一些内核查看工具和相关书籍,可以方便的查看代码并且分析出各个模块的调用关系,编译出自己的 L

46、inux 内核。出于对 Linux 的爱好以及平时对 Linux 的知识也比较丰富,所以我查找和获取 Linux 初起原代码也比较容易。大家通力合作,最终完成了对 Linux 初起代码的分析任务。过程是艰辛的,但收获也很大,让我认识到面对一个Linux 内核,不能恐惧,只要静下心来,一步一步分析,最终就能得出框架,并且加深了我对Linux 相关知识的认识,开阔了眼界。Linux 内核初起代码分析-16-致致 谢谢 能够完成这次课程设计我要感谢老师对我的悉心指导,没有你们的工作,我不可能完成这次对于我来说算得上是陌生的任务,没有你们的帮助,我们会走更多的弯路;没有你平时的教育,我知识不会得到提高

47、,不会有丰富的眼界。另外感谢同学们的帮助,在关键的时候,总是能够给我耐心的帮助,我由于对操作系统的知识不是很熟悉,在我遇到困难的时候你们总是能给我耐心的讲解,让我在完成任务的同时也能够掌握一些知识,我觉得这是我本次设计最大的收获。也让我感觉到了友谊的力量,再次谢谢你们。 Linux 内核初起代码分析-17-参考文献参考文献1 李善平. Linux 内核 2.4 版源代码分析大全. 北京:机械工业出版社,2002,1.2 陈莉君. 深入分析 Linux 内核源代码. 北京:人民邮电出版社,2002,8.3 陈向群.操作系统教程.北京:北京大学出版社,2007,01.4 罗宇.操作系统课程设计.北京:机械工业出版社,2005,9.5 Gary Nutt.Linux 操作系统内核实习. 北京:机械工业出版社,2002,1.

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

copyright@ 2023-2025  zhuangpeitu.com 装配图网版权所有   联系电话:18123376007

备案号:ICP2024067431-1 川公网安备51140202000466号


本站为文档C2C交易模式,即用户上传的文档直接被用户下载,本站只是中间服务平台,本站所有文档下载所得的收益归上传人(含作者)所有。装配图网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。若文档所含内容侵犯了您的版权或隐私,请立即通知装配图网,我们立即给予删除!