LINUXinit.docx

上传人:b****6 文档编号:5987264 上传时间:2023-01-02 格式:DOCX 页数:11 大小:22.91KB
下载 相关 举报
LINUXinit.docx_第1页
第1页 / 共11页
LINUXinit.docx_第2页
第2页 / 共11页
LINUXinit.docx_第3页
第3页 / 共11页
LINUXinit.docx_第4页
第4页 / 共11页
LINUXinit.docx_第5页
第5页 / 共11页
点击查看更多>>
下载资源
资源描述

LINUXinit.docx

《LINUXinit.docx》由会员分享,可在线阅读,更多相关《LINUXinit.docx(11页珍藏版)》请在冰豆网上搜索。

LINUXinit.docx

LINUXinit

1/*

2*linux/init/main.c

3*

4*(C)1991LinusTorvalds

5*/

6

7#define__LIBRARY__//定义该变量是为了包括定义在unistd.h中的内嵌汇编代码等信息。

8#include//*.h头文件所在的默认目录是include/,则在代码中就不用明确指明位置。

//如果不是UNIX的标准头文件,则需要指明所在的目录,并用双引号括住。

//标准符号常数与类型文件。

定义了各种符号常数和类型,并申明了各种函数。

//如果定义了__LIBRARY__,则还包括系统调用号和内嵌汇编代码_syscall0()

等。

9#include//时间类型头文件。

其中最主要定义了tm结构和一些有关时间的函数原形。

10

11/*

12*weneedthisinline-forkingfromkernelspacewillresult

13*inNOCOPYONWRITE(!

!

!

),untilanexecveisexecuted.This

14*isnoproblem,butforthestack.Thisishandledbynotletting

15*main()usethestackatallafterfork().Thus,nofunction

16*calls-whichmeansinlinecodeforforktoo,asotherwisewe

17*wouldusethestackuponexitfrom'fork()'.

18*

19*Actuallyonlypauseandforkareneededinline,sothatthere

20*won'tbeanymessingwiththestackfrommain(),butwedefine

21*someotherstoo.

22*/

/*

*我们需要下面这些内嵌语句-从内核空间创建进程(forking)将导致没有写时复制(COPYON

WRITE)!

!

!

*直到一个执行execve调用。

这对堆栈可能带来问题。

处理的方法是在fork()调用之后不让main()使

*任何堆栈。

因此就不能有函数调用-这意味着fork也要使用内嵌的代码,否则我们在从fork()退出

*时就要使用堆栈了。

*实际上只有pause和fork需要使用内嵌方式,以保证从main()中不会弄乱堆栈,但是我们同时还

*定义了其它一些函数。

*/

23staticinline_syscall0(int,fork)//是unistd.h中的内嵌宏代码。

以嵌入汇编的形式调用

//Linux的系统调用中断0x80。

该中断是所有系统调用的

//入口。

该条语句实际上是intfork()创建进程系统调用。

//syscall0名称中最后的0表示无参数,1表示1个参数。

24staticinline_syscall0(int,pause)//intpause()系统调用:

暂停进程的执行,直到

//收到一个信号。

25staticinline_syscall1(int,setup,void*,BIOS)//intsetup(void*BIOS)系统调用,仅用于

//linux初始化(仅在这个程序中被调用)。

26staticinline_syscall0(int,sync)//intsync()系统调用:

更新文件系统。

27

28#include//tty头文件,定义了有关tty_io,串行通信方面的参数、常数。

29#include//调度程序头文件,定义了任务结构task_struct、第1个初始任务

//的数据。

还有一些以宏的形式定义的有关描述符参数设置和获取的

//嵌入式汇编函数程序。

30#include//head头文件,定义了段描述符的简单结构,和几个选择符常量。

31#include//系统头文件。

以宏的形式定义了许多有关设置或修改

//描述符/中断门等的嵌入式汇编子程序。

32#include//io头文件。

以宏的嵌入汇编程序形式定义对io端口操作的函数。

33

34#include//标准定义头文件。

定义了NULL,offsetof(TYPE,MEMBER)。

35#include//标准参数头文件。

以宏的形式定义变量参数列表。

主要说明了-个

//类型(va_list)和三个宏(va_start,va_arg和va_end),vsprintf、

//vprintf、vfprintf。

36#include

37#include//文件控制头文件。

用于文件及其描述符的操作控制常数符号的定义。

38#include//类型头文件。

定义了基本的系统数据类型。

39

40#include//文件系统头文件。

定义文件表结构(file,buffer_head,m_inode等)。

41

42staticcharprintbuf[1024];//静态字符串数组。

43

44externintvsprintf();//送格式化输出到一字符串中(在kernel/vsprintf.c,92行)。

45externvoidinit(void);//函数原形,初始化(在168行)。

46externvoidblk_dev_init(void);//块设备初始化子程序(kernel/blk_drv/ll_rw_blk.c,157行)

47externvoidchr_dev_init(void);//字符设备初始化(kernel/chr_drv/tty_io.c,347行)

48externvoidhd_init(void);//硬盘初始化程序(kernel/blk_drv/hd.c,343行)

49externvoidfloppy_init(void);//软驱初始化程序(kernel/blk_drv/floppy.c,457行)

50externvoidmem_init(longstart,longend);//内存管理初始化(mm/memory.c,399行)

51externlongrd_init(longmem_start,intlength);//虚拟盘初始化(kernel/blk_drv/ramdisk.c,52)

52externlongkernel_mktime(structtm*tm);//建立内核时间(秒)。

53externlongstartup_time;//内核启动时间(开机时间)(秒)。

54

55/*

56*Thisissetupbythesetup-routineatboot-time

57*/

/*

*以下这些数据是由setup.s程序在引导时间设置的(参见第2章2.3.1节中的表2.1)。

*/

58#defineEXT_MEM_K(*(unsignedshort*)0x90002)//1M以后的扩展内存大小(KB)。

59#defineDRIVE_INFO(*(structdrive_info*)0x90080)//硬盘参数表基址。

60#defineORIG_ROOT_DEV(*(unsignedshort*)0x901FC)//根文件系统所在设备号。

61

62/*

63*Yeah,yeah,it'sugly,butIcannotfindhowtodothiscorrectly

64*andthisseemstowork.Ianybodyhasmoreinfoonthereal-time

65*clockI'dbeinterested.Mostofthiswastrialanderror,andsome

66*bios-listingreading.Urghh.

67*/

/*

*是啊,是啊,下面这段程序很差劲,但我不知道如何正确地实现,而且好象它还能运行。

如果有

*关于实时时钟更多的资料,那我很感兴趣。

这些都是试探出来的,以及看了一些bios程序,呵!

*/

68

69#defineCMOS_READ(addr)({\//这段宏读取CMOS实时时钟信息。

70outb_p(0x80|addr,0x70);\//0x70是写端口号,0x80|addr是要读取的CMOS内存地址。

71inb_p(0x71);\//0x71是读端口号。

72})

73

74#defineBCD_TO_BIN(val)((val)=((val)&15)+((val)>>4)*10)//将BCD码转换成数字。

75

76staticvoidtime_init(void)//该子程序取CMOS时钟,并设置开机时间􀃆startup_time(秒)。

77{

78structtmtime;

79

80do{

81time.tm_sec=CMOS_READ(0);//参见后面CMOS内存列表。

82time.tm_min=CMOS_READ

(2);

83time.tm_hour=CMOS_READ(4);

84time.tm_mday=CMOS_READ(7);

85time.tm_mon=CMOS_READ(8);

86time.tm_year=CMOS_READ(9);

87}while(time.tm_sec!

=CMOS_READ(0));

88BCD_TO_BIN(time.tm_sec);

89BCD_TO_BIN(time.tm_min);

90BCD_TO_BIN(time.tm_hour);

91BCD_TO_BIN(time.tm_mday);

92BCD_TO_BIN(time.tm_mon);

93BCD_TO_BIN(time.tm_year);

94time.tm_mon--;

95startup_time=kernel_mktime(&time);

96}

97

98staticlongmemory_end=0;//机器具有的内存(字节数)。

99staticlongbuffer_memory_end=0;//高速缓冲区末端地址。

100staticlongmain_memory_start=0;//主内存(将用于分页)开始的位置。

101

102structdrive_info{chardummy[32];}drive_info;//用于存放硬盘参数表信息。

103

104voidmain(void)/*ThisreallyISvoid,noerrorhere.*/

105{/*Thestartuproutineassumes(well,...)this*/

/*这里确实是void,并没错。

在startup程序(head.s)中就是这样假设的。

//参见head.s程序第136行开始的几行代码。

106/*

107*Interruptsarestilldisabled.Donecessarysetups,then

108*enablethem

109*/

/*

*此时中断仍被禁止着,做完必要的设置后就将其开启。

*/

//下面这段代码用于保存:

//根设备号􀃎ROOT_DEV;高速缓存末端地址􀃎buffer_memory_end;

//机器内存数􀃎memory_end;主内存开始地址􀃎main_memory_start;

110ROOT_DEV=ORIG_ROOT_DEV;

111drive_info=DRIVE_INFO;

112memory_end=(1<<20)+(EXT_MEM_K<<10);//内存大小=1Mb字节+扩展内存(k)*1024字节。

113memory_end&=0xfffff000;//忽略不到4Kb(1页)的内存数。

114if(memory_end>16*1024*1024)//如果内存超过16Mb,则按16Mb计。

115memory_end=16*1024*1024;

116if(memory_end>12*1024*1024)//如果内存>12Mb,则设置缓冲区末端=4Mb

117buffer_memory_end=4*1024*1024;

118elseif(memory_end>6*1024*1024)//否则如果内存>6Mb,则设置缓冲区末端=2Mb

119buffer_memory_end=2*1024*1024;

120else

121buffer_memory_end=1*1024*1024;//否则则设置缓冲区末端=1Mb

122main_memory_start=buffer_memory_end;//主内存起始位置=缓冲区末端;

123#ifdefRAMDISK//如果定义了虚拟盘,则主内存将减少。

124main_memory_start+=rd_init(main_memory_start,RAMDISK*1024);

125#endif

//以下是内核进行所有方面的初始化工作。

阅读时最好跟着调用的程序深入进去看,实在看

//不下去了,就先放一放,看下一个初始化调用--这是经验之谈☺。

126mem_init(main_memory_start,memory_end);

127trap_init();//陷阱门(硬件中断向量)初始化。

(kernel/traps.c,181行)

128blk_dev_init();//块设备初始化。

(kernel/blk_dev/ll_rw_blk.c,157行)

129chr_dev_init();//字符设备初始化。

(kernel/chr_dev/tty_io.c,347行)

130tty_init();//tty初始化。

(kernel/chr_dev/tty_io.c,105行)

131time_init();//设置开机启动时间􀃎startup_time(见76行)。

132sched_init();//调度程序初始化(加载了任务0的tr,ldtr)(kernel/sched.c,385)

133buffer_init(buffer_memory_end);//内存管理初始化,建内存链表等。

(fs/buffer.c,348)

134hd_init();//硬盘初始化。

(kernel/blk_dev/hd.c,343行)

135floppy_init();//软驱初始化。

(kernel/blk_dev/floppy.c,457行)

136sti();//所有初始化工作都做完了,开启中断。

137move_to_user_mode();//移到用户模式。

(include/asm/system.h,第1行)

138if(!

fork()){/*wecountonthisgoingok*/

139init();

140}

141/*

142*NOTE!

!

Foranyothertask'pause()'wouldmeanwehavetogeta

143*signaltoawaken,buttask0isthesoleexception(see'schedule()')

144*astask0getsactivatedateveryidlemoment(whennoothertasks

145*canrun).Fortask0'pause()'justmeanswegocheckifsomeother

146*taskcanrun,andifnotwereturnhere.

147*/

/*注意!

!

对于任何其它的任务,'pause()'将意味着我们必须等待收到一个信号才会返

*回就绪运行态,但任务0(task0)是唯一的意外情况(参见'schedule()'),因为任务0在

*任何空闲时间里都会被激活(当没有其它任务在运行时),因此对于任务0'pause()'仅意味着

*我们返回来查看是否有其它任务可以运行,如果没有的话我们就回到这里,一直循环执行'pause()'。

*/

148for(;;)pause();

149}

150

151staticintprintf(constchar*fmt,...)

//产生格式化信息并输出到标准输出设备stdout

(1),这里是指屏幕上显示。

参数'*fmt'指定输出将

//采用的格式,参见各种标准C语言书籍。

该子程序正好是vsprintf如何使用的一个例子。

//该程序使用vsprintf()将格式化的字符串放入printbuf缓冲区,然后用write()将缓冲区的内容

//输出到标准设备(1--stdout)。

152{

153va_listargs;

154inti;

155

156va_start(args,fmt);

157write(1,printbuf,i=vsprintf(printbuf,fmt,args));

158va_end(args);

159returni;

160}

161

162staticchar*argv_rc[]={"/bin/sh",NULL};//调用执行程序时参数的字符串数组。

163staticchar*envp_rc[]={"HOME=/",NULL};//调用执行程序时的环境字符串数组。

164

165staticchar*argv[]={"-/bin/sh",NULL};//同上。

166staticchar*envp[]={"HOME=/usr/root",NULL};

167

168voidinit(void)

169{

170intpid,i;

171

172setup((void*)&drive_info);//读取硬盘参数包括分区表信息并建立虚拟盘和

//安装根文件系统设备。

(kernel/blk_drv/hd.c,71)

173(void)open("/dev/tty0",O_RDWR,0);//用读写访问方式打开设备“/dev/tty0”,

//这里对应终端控制台。

//返回的句柄号0--stdin标准输入设备。

174(void)dup(0);//复制句柄,产生句柄1号--stdout标准输出设备。

175(void)dup(0);//复制句柄,产生句柄2号--stderr标准出错输出设备。

176printf("%dbuffers=%dbytesbufferspace\n\r",NR_BUFFERS,

177NR_BUFFERS*BLOCK_SIZE);//打印缓冲区块数和总字节数,每块1024字节。

178printf("Freemem:

%dbytes\n\r",memory_end-main_memory_start);//空闲内存字节数。

//下面fork()用于创建一个子进程(子任务)。

对于被创建的子进程,fork()将返回0值,

//对于原(父进程)将返回子进程的进程号。

所以180-184句是子进程执行的内容。

该子进程

//关闭了句柄0(stdin),以只读方式打开/etc/rc文件,并执行/bin/sh程序,所带参数和

//环境变量分别由argv_rc和envp_rc数组给出。

参见后面的描述。

179if(!

(pid=fork())){

180close(0);

181if(open("/etc/rc",O_RDONLY,0))

182_exit

(1);//如果打开文件失败,则退出(/lib/_exit.c,10)。

183execve("/bin/sh",argv_rc,envp_rc);//装入/bin/sh程序并执行。

184_exit

(2);

185}

//下面是父进程执行的语句。

wait()是等待子进程停止或终止,其返回值应是子进程的进程号(pid)。

//这三句的作用是父进程等待子进程的结束。

&i是存放返回状态信息的位置。

如果wait()返回值不

//等于子进程号,则继续等待。

186if(pid>0)

187while(pid!

=wait(&i))

188/*nothing*/;

//如果执行到这里,说明刚创建的子进程的执行已停止或终止了。

下面循环中首先再创建一个子进程,

//如果出错,则显示“初始化程序创建子进程失败”的信息并继续执行。

对于所创建的子进程关闭所

//以前还遗留的句柄(stdin,stdout,stderr),新创建一个会话并设置进程组号,然后重新打开

///dev/tty0作为stdin,并复制成stdout和stderr。

再次执行系统解释程序/bin/sh。

但这次执行所

//选用的参数和环境数组另选了一套(见上面165-167行)。

然后父进程再次运行wait()等待。

如果

//子进程又停止了执行,则在标准输出上显示出错信息“子进程pid停止了运行,返回码是i”,然后

//继续重试下去…,形成“大”死循环。

189while

(1){

190if((pid=fork())<0){

191printf("Forkfailedininit\r\n");

192continue;

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 自然科学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1